mirror of
https://github.com/hauler-dev/hauler.git
synced 2026-02-14 18:09:51 +00:00
142 lines
2.9 KiB
Go
142 lines
2.9 KiB
Go
package chart
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
gv1 "github.com/google/go-containerregistry/pkg/v1"
|
|
"github.com/google/go-containerregistry/pkg/v1/partial"
|
|
gtypes "github.com/google/go-containerregistry/pkg/v1/types"
|
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
"helm.sh/helm/v3/pkg/action"
|
|
"helm.sh/helm/v3/pkg/chart"
|
|
"helm.sh/helm/v3/pkg/chart/loader"
|
|
"helm.sh/helm/v3/pkg/cli"
|
|
|
|
"github.com/rancherfederal/hauler/internal/layer"
|
|
"github.com/rancherfederal/hauler/pkg/artifact"
|
|
"github.com/rancherfederal/hauler/pkg/consts"
|
|
)
|
|
|
|
var _ artifact.OCI = (*Chart)(nil)
|
|
|
|
type Chart struct {
|
|
path string
|
|
|
|
annotations map[string]string
|
|
}
|
|
|
|
func NewChart(name, repo, version string) (*Chart, error) {
|
|
cpo := action.ChartPathOptions{
|
|
RepoURL: repo,
|
|
Version: version,
|
|
}
|
|
|
|
cp, err := cpo.LocateChart(name, cli.New())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &Chart{
|
|
path: cp,
|
|
}, nil
|
|
}
|
|
|
|
func (h *Chart) MediaType() string {
|
|
return consts.OCIManifestSchema1
|
|
}
|
|
|
|
func (h *Chart) Manifest() (*gv1.Manifest, error) {
|
|
cfgDesc, err := h.configDescriptor()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var layerDescs []gv1.Descriptor
|
|
ls, err := h.Layers()
|
|
for _, l := range ls {
|
|
desc, err := partial.Descriptor(l)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
layerDescs = append(layerDescs, *desc)
|
|
}
|
|
|
|
return &gv1.Manifest{
|
|
SchemaVersion: 2,
|
|
MediaType: gtypes.MediaType(h.MediaType()),
|
|
Config: cfgDesc,
|
|
Layers: layerDescs,
|
|
Annotations: h.annotations,
|
|
}, nil
|
|
}
|
|
|
|
func (h *Chart) RawConfig() ([]byte, error) {
|
|
ch, err := loader.Load(h.path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(ch.Metadata)
|
|
}
|
|
|
|
func (h *Chart) configDescriptor() (gv1.Descriptor, error) {
|
|
data, err := h.RawConfig()
|
|
if err != nil {
|
|
return gv1.Descriptor{}, err
|
|
}
|
|
|
|
hash, size, err := gv1.SHA256(bytes.NewBuffer(data))
|
|
if err != nil {
|
|
return gv1.Descriptor{}, err
|
|
}
|
|
|
|
return gv1.Descriptor{
|
|
MediaType: consts.ChartConfigMediaType,
|
|
Size: size,
|
|
Digest: hash,
|
|
}, nil
|
|
}
|
|
|
|
func (h *Chart) Load() (*chart.Chart, error) {
|
|
rc, err := chartOpener(h.path)()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rc.Close()
|
|
return loader.LoadArchive(rc)
|
|
}
|
|
|
|
func (h *Chart) Layers() ([]gv1.Layer, error) {
|
|
chartDataLayer, err := h.chartDataLayer()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return []gv1.Layer{
|
|
chartDataLayer,
|
|
// TODO: Add provenance
|
|
}, nil
|
|
}
|
|
|
|
func (h *Chart) RawChartData() ([]byte, error) {
|
|
return os.ReadFile(h.path)
|
|
}
|
|
|
|
func (h *Chart) chartDataLayer() (gv1.Layer, error) {
|
|
annotations := make(map[string]string)
|
|
annotations[ocispec.AnnotationTitle] = filepath.Base(h.path)
|
|
|
|
return layer.FromOpener(chartOpener(h.path),
|
|
layer.WithMediaType(consts.ChartLayerMediaType),
|
|
layer.WithAnnotations(annotations))
|
|
}
|
|
|
|
func chartOpener(path string) layer.Opener {
|
|
return func() (io.ReadCloser, error) {
|
|
return os.Open(path)
|
|
}
|
|
}
|