mirror of
https://github.com/hauler-dev/hauler.git
synced 2026-05-06 09:18:30 +00:00
Merge pull request #102 from joshrwolf/content-location-tagging
standardize content naming for unnamed content
This commit is contained in:
@@ -5,7 +5,6 @@ import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/authn"
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
"github.com/google/go-containerregistry/pkg/v1/remote"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -15,6 +14,7 @@ import (
|
||||
"github.com/rancherfederal/hauler/internal/mapper"
|
||||
"github.com/rancherfederal/hauler/pkg/consts"
|
||||
"github.com/rancherfederal/hauler/pkg/log"
|
||||
"github.com/rancherfederal/hauler/pkg/reference"
|
||||
)
|
||||
|
||||
type Opts struct {
|
||||
@@ -36,7 +36,7 @@ func (o *Opts) AddArgs(cmd *cobra.Command) {
|
||||
f.BoolVar(&o.PlainHTTP, "plain-http", false, "Toggle allowing plain http connections when copying to a remote registry")
|
||||
}
|
||||
|
||||
func Cmd(ctx context.Context, o *Opts, reference string) error {
|
||||
func Cmd(ctx context.Context, o *Opts, ref string) error {
|
||||
l := log.FromContext(ctx)
|
||||
|
||||
rs, err := content.NewRegistry(content.RegistryOptions{
|
||||
@@ -49,12 +49,12 @@ func Cmd(ctx context.Context, o *Opts, reference string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
ref, err := name.ParseReference(reference)
|
||||
r, err := reference.Parse(ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
desc, err := remote.Get(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain), remote.WithContext(ctx))
|
||||
desc, err := remote.Get(r, remote.WithAuthFromKeychain(authn.DefaultKeychain), remote.WithContext(ctx))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -74,7 +74,7 @@ func Cmd(ctx context.Context, o *Opts, reference string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
pushedDesc, err := oras.Copy(ctx, rs, ref.Name(), mapperStore, "",
|
||||
pushedDesc, err := oras.Copy(ctx, rs, r.Name(), mapperStore, "",
|
||||
oras.WithAdditionalCachedMediaTypes(consts.DockerManifestSchema2))
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -155,7 +155,7 @@ func addStoreInfo() *cobra.Command {
|
||||
Use: "info",
|
||||
Short: "Print out information about the store",
|
||||
Args: cobra.ExactArgs(0),
|
||||
Aliases: []string{"i"},
|
||||
Aliases: []string{"i", "list", "ls"},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Context()
|
||||
|
||||
|
||||
@@ -2,11 +2,9 @@ package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/rancherfederal/hauler/pkg/apis/hauler.cattle.io/v1alpha1"
|
||||
@@ -14,6 +12,7 @@ import (
|
||||
"github.com/rancherfederal/hauler/pkg/content/file"
|
||||
"github.com/rancherfederal/hauler/pkg/content/image"
|
||||
"github.com/rancherfederal/hauler/pkg/log"
|
||||
"github.com/rancherfederal/hauler/pkg/reference"
|
||||
"github.com/rancherfederal/hauler/pkg/store"
|
||||
)
|
||||
|
||||
@@ -26,68 +25,9 @@ func (o *AddFileOpts) AddFlags(cmd *cobra.Command) {
|
||||
f.StringVarP(&o.Name, "name", "n", "", "(Optional) Name to assign to file in store")
|
||||
}
|
||||
|
||||
// func addContent(ctx context.Context, o *AddFileOpts, s *store.Store, meta metav1.TypeMeta) error {
|
||||
// l := log.FromContext(ctx)
|
||||
//
|
||||
// var (
|
||||
// oci artifact.OCI
|
||||
// loc string
|
||||
// )
|
||||
//
|
||||
// switch cfg := meta.(type) {
|
||||
// case cfg == v1alpha1.FilesContentKind:
|
||||
// f := file.NewFile(cfg)
|
||||
// oci = f
|
||||
//
|
||||
// loc = f.Name(reference)
|
||||
//
|
||||
// case "chart":
|
||||
// oci, err := chart.NewThickChart(ch.Name, ch.RepoURL, ch.Version)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// tag := ch.Version
|
||||
// if tag == "" {
|
||||
// tag = name.DefaultTag
|
||||
// }
|
||||
//
|
||||
// ref, err := name.ParseReference(ch.Name, name.WithDefaultRegistry(""), name.WithDefaultTag(tag))
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
//
|
||||
// case "image":
|
||||
// i, err := image.NewImage(reference)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// oci = i
|
||||
//
|
||||
// loc = reference
|
||||
//
|
||||
// default:
|
||||
// return nil
|
||||
//
|
||||
// }
|
||||
// ref, err := name.ParseReference(loc, name.WithDefaultRegistry(""))
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// desc, err := s.AddArtifact(ctx, oci, ref)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// l.Infof("added [%s] of type [%s] to store", ref.Name(), s.Identify(ctx, desc))
|
||||
// return nil
|
||||
// }
|
||||
|
||||
func AddFileCmd(ctx context.Context, o *AddFileOpts, s *store.Store, reference string) error {
|
||||
cfg := v1alpha1.File{
|
||||
Ref: reference,
|
||||
Path: reference,
|
||||
}
|
||||
|
||||
return storeFile(ctx, s, cfg)
|
||||
@@ -96,14 +36,18 @@ func AddFileCmd(ctx context.Context, o *AddFileOpts, s *store.Store, reference s
|
||||
func storeFile(ctx context.Context, s *store.Store, fi v1alpha1.File) error {
|
||||
l := log.FromContext(ctx)
|
||||
|
||||
f := file.NewFile(fi.Ref)
|
||||
|
||||
desc, err := s.AddArtifact(ctx, f, f.Name(fi.Ref))
|
||||
f := file.NewFile(fi.Path)
|
||||
ref, err := reference.NewTagged(f.Name(fi.Path), reference.DefaultTag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l.With(log.Fields{"type": s.Identify(ctx, desc)}).Infof("added [%s] to store", desc.Annotations[ocispec.AnnotationRefName])
|
||||
desc, err := s.AddArtifact(ctx, f, ref.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l.Infof("added 'file' to store at [%s], with digest [%s]", ref.Name(), desc.Digest.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -118,7 +62,7 @@ func (o *AddImageOpts) AddFlags(cmd *cobra.Command) {
|
||||
|
||||
func AddImageCmd(ctx context.Context, o *AddImageOpts, s *store.Store, reference string) error {
|
||||
cfg := v1alpha1.Image{
|
||||
Ref: reference,
|
||||
Name: reference,
|
||||
}
|
||||
|
||||
return storeImage(ctx, s, cfg)
|
||||
@@ -127,17 +71,22 @@ func AddImageCmd(ctx context.Context, o *AddImageOpts, s *store.Store, reference
|
||||
func storeImage(ctx context.Context, s *store.Store, i v1alpha1.Image) error {
|
||||
l := log.FromContext(ctx)
|
||||
|
||||
oci, err := image.NewImage(i.Ref)
|
||||
oci, err := image.NewImage(i.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
desc, err := s.AddArtifact(ctx, oci, i.Ref)
|
||||
r, err := name.ParseReference(i.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l.With(log.Fields{"type": s.Identify(ctx, desc)}).Infof("added [%s] to store", i.Ref)
|
||||
desc, err := s.AddArtifact(ctx, oci, r.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l.Infof("added 'image' to store at [%s], with digest [%s]", r.Name(), desc.Digest.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -178,17 +127,15 @@ func storeChart(ctx context.Context, s *store.Store, cfg v1alpha1.Chart) error {
|
||||
return err
|
||||
}
|
||||
|
||||
tag := cfg.Version
|
||||
if tag == "" {
|
||||
tag = name.DefaultTag
|
||||
ref, err := reference.NewTagged(cfg.Name, cfg.Version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ref := fmt.Sprintf("%s:%s", cfg.Name, tag)
|
||||
desc, err := s.AddArtifact(ctx, oci, ref)
|
||||
desc, err := s.AddArtifact(ctx, oci, ref.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l.With(log.Fields{"type": s.Identify(ctx, desc)}).Infof("added [%s] to store", ref)
|
||||
l.Infof("added 'chart' to store at [%s], with digest [%s]", ref.Name(), desc.Digest.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,14 +3,14 @@ package store
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
"github.com/google/go-containerregistry/pkg/v1/layout"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/rancherfederal/hauler/internal/mapper"
|
||||
"github.com/rancherfederal/hauler/pkg/log"
|
||||
"github.com/rancherfederal/hauler/pkg/reference"
|
||||
"github.com/rancherfederal/hauler/pkg/store"
|
||||
)
|
||||
|
||||
@@ -21,56 +21,55 @@ type ExtractOpts struct {
|
||||
func (o *ExtractOpts) AddArgs(cmd *cobra.Command) {
|
||||
f := cmd.Flags()
|
||||
|
||||
f.StringVar(&o.DestinationDir, "dir", "", "Directory to save contents to (defaults to current directory)")
|
||||
f.StringVarP(&o.DestinationDir, "output", "o", "", "Directory to save contents to (defaults to current directory)")
|
||||
}
|
||||
|
||||
func ExtractCmd(ctx context.Context, o *ExtractOpts, s *store.Store, reference string) error {
|
||||
func ExtractCmd(ctx context.Context, o *ExtractOpts, s *store.Store, ref string) error {
|
||||
l := log.FromContext(ctx)
|
||||
|
||||
ref, err := name.ParseReference(reference, name.WithDefaultRegistry(""), name.WithDefaultTag("latest"))
|
||||
r, err := reference.Parse(ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p, err := layout.FromPath("store")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ii, _ := p.ImageIndex()
|
||||
im, _ := ii.IndexManifest()
|
||||
var manifest ocispec.Manifest
|
||||
for _, m := range im.Manifests {
|
||||
if r, ok := m.Annotations[ocispec.AnnotationRefName]; !ok || r != ref.Name() {
|
||||
continue
|
||||
found := false
|
||||
if err := s.Content.Walk(func(reference string, desc ocispec.Descriptor) error {
|
||||
if reference != r.Name() {
|
||||
return nil
|
||||
}
|
||||
found = true
|
||||
|
||||
desc, err := p.Image(m.Digest)
|
||||
rc, err := s.Content.Fetch(ctx, desc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Infof(m.Annotations[ocispec.AnnotationRefName])
|
||||
defer rc.Close()
|
||||
|
||||
manifestData, err := desc.RawManifest()
|
||||
var m ocispec.Manifest
|
||||
if err := json.NewDecoder(rc).Decode(&m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mapperStore, err := mapper.FromManifest(m, o.DestinationDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(manifestData, &manifest); err != nil {
|
||||
pushedDesc, err := s.Copy(ctx, r.Name(), mapperStore, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
mapperStore, err := mapper.FromManifest(manifest, o.DestinationDir)
|
||||
if err != nil {
|
||||
l.Infof("extracted [%s] from store with digest [%s]", pushedDesc.MediaType, pushedDesc.Digest.String())
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
desc, err := s.Copy(ctx, ref.Name(), mapperStore, "")
|
||||
if err != nil {
|
||||
return err
|
||||
if !found {
|
||||
return fmt.Errorf("reference [%s] not found in store (hint: use `hauler store info` to list store contents)", ref)
|
||||
}
|
||||
|
||||
l.Infof("downloaded [%s] with digest [%s]", desc.MediaType, desc.Digest.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/rancherfederal/hauler/pkg/consts"
|
||||
"github.com/rancherfederal/hauler/pkg/reference"
|
||||
"github.com/rancherfederal/hauler/pkg/store"
|
||||
)
|
||||
|
||||
@@ -115,8 +116,13 @@ func newItem(s *store.Store, desc ocispec.Descriptor, m ocispec.Manifest) item {
|
||||
ctype = "unknown"
|
||||
}
|
||||
|
||||
ref, err := reference.Parse(desc.Annotations[ocispec.AnnotationRefName])
|
||||
if err != nil {
|
||||
return item{}
|
||||
}
|
||||
|
||||
return item{
|
||||
Reference: desc.Annotations[ocispec.AnnotationRefName],
|
||||
Reference: ref.Name(),
|
||||
Type: ctype,
|
||||
Layers: len(m.Layers),
|
||||
Size: byteCountSI(size),
|
||||
|
||||
4
go.mod
4
go.mod
@@ -95,8 +95,8 @@ require (
|
||||
github.com/lib/pq v1.10.4 // indirect
|
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
|
||||
github.com/mailru/easyjson v0.7.6 // indirect
|
||||
github.com/mattn/go-colorable v0.0.9 // indirect
|
||||
github.com/mattn/go-isatty v0.0.4 // indirect
|
||||
github.com/mattn/go-colorable v0.1.2 // indirect
|
||||
github.com/mattn/go-isatty v0.0.8 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
|
||||
7
go.sum
7
go.sum
@@ -684,11 +684,13 @@ github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2
|
||||
github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI=
|
||||
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
|
||||
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
|
||||
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
@@ -1193,6 +1195,7 @@ golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
||||
@@ -18,5 +18,11 @@ type FileSpec struct {
|
||||
}
|
||||
|
||||
type File struct {
|
||||
Ref string `json:"ref"`
|
||||
// Path is the path to the file contents, can be a local or remote path
|
||||
Path string `json:"path"`
|
||||
|
||||
// Reference is an optionally defined reference to the contents within the store
|
||||
// If not specified, this will be generated as follows:
|
||||
// hauler/<path base>:latest
|
||||
Reference string `json:"reference,omitempty"`
|
||||
}
|
||||
|
||||
@@ -18,5 +18,6 @@ type ImageSpec struct {
|
||||
}
|
||||
|
||||
type Image struct {
|
||||
Ref string `json:"ref"`
|
||||
// Name is the full location for the image, can be referenced by tags or digests
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
@@ -74,11 +74,11 @@ func (c *tchart) dependentImages() error {
|
||||
}
|
||||
|
||||
for _, img := range imgs.Spec.Images {
|
||||
i, err := image.NewImage(img.Ref)
|
||||
i, err := image.NewImage(img.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.contents[img.Ref] = i
|
||||
c.contents[img.Name] = i
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ func ImagesInChart(c *helmchart.Chart) (v1alpha1.Images, error) {
|
||||
|
||||
found := find(raw, defaultKnownImagePaths...)
|
||||
for _, f := range found {
|
||||
images = append(images, v1alpha1.Image{Ref: f})
|
||||
images = append(images, v1alpha1.Image{Name: f})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,9 +16,9 @@ import (
|
||||
var _ artifact.OCI = (*File)(nil)
|
||||
|
||||
// File implements the OCI interface for File API objects. API spec information is
|
||||
// stored into the Ref field.
|
||||
// stored into the Path field.
|
||||
type File struct {
|
||||
Ref string
|
||||
Path string
|
||||
|
||||
client *getter.Client
|
||||
|
||||
@@ -29,12 +29,12 @@ type File struct {
|
||||
annotations map[string]string
|
||||
}
|
||||
|
||||
func NewFile(ref string, opts ...Option) *File {
|
||||
func NewFile(path string, opts ...Option) *File {
|
||||
client := getter.NewClient(getter.ClientOptions{})
|
||||
|
||||
f := &File{
|
||||
client: client,
|
||||
Ref: ref,
|
||||
Path: path,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
@@ -43,8 +43,8 @@ func NewFile(ref string, opts ...Option) *File {
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *File) Name(ref string) string {
|
||||
return f.client.Name(ref)
|
||||
func (f *File) Name(path string) string {
|
||||
return f.client.Name(path)
|
||||
}
|
||||
|
||||
func (f *File) MediaType() string {
|
||||
@@ -80,7 +80,7 @@ func (f *File) compute() error {
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
blob, err := f.client.LayerFrom(ctx, f.Ref)
|
||||
blob, err := f.client.LayerFrom(ctx, f.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -90,9 +90,9 @@ func (f *File) compute() error {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg := f.client.Config(f.Ref)
|
||||
cfg := f.client.Config(f.Path)
|
||||
if cfg == nil {
|
||||
cfg = f.client.Config(f.Ref)
|
||||
cfg = f.client.Config(f.Path)
|
||||
}
|
||||
|
||||
cfgDesc, err := partial.Descriptor(cfg)
|
||||
|
||||
@@ -2,7 +2,7 @@ package image
|
||||
|
||||
import (
|
||||
"github.com/google/go-containerregistry/pkg/authn"
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
gname "github.com/google/go-containerregistry/pkg/name"
|
||||
gv1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/google/go-containerregistry/pkg/v1/remote"
|
||||
|
||||
@@ -24,14 +24,14 @@ func (i *Image) RawConfig() ([]byte, error) {
|
||||
}
|
||||
|
||||
// Image implements the OCI interface for Image API objects. API spec information
|
||||
// is stored into the Ref field.
|
||||
// is stored into the Name field.
|
||||
type Image struct {
|
||||
Ref string
|
||||
Name string
|
||||
gv1.Image
|
||||
}
|
||||
|
||||
func NewImage(ref string, opts ...remote.Option) (*Image, error) {
|
||||
r, err := name.ParseReference(ref)
|
||||
func NewImage(name string, opts ...remote.Option) (*Image, error) {
|
||||
r, err := gname.ParseReference(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -47,7 +47,7 @@ func NewImage(ref string, opts ...remote.Option) (*Image, error) {
|
||||
}
|
||||
|
||||
return &Image{
|
||||
Ref: ref,
|
||||
Name: name,
|
||||
Image: img,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ type Logger interface {
|
||||
SetLevel(string)
|
||||
With(Fields) *logger
|
||||
WithContext(context.Context) context.Context
|
||||
|
||||
Errorf(string, ...interface{})
|
||||
Infof(string, ...interface{})
|
||||
Warnf(string, ...interface{})
|
||||
|
||||
49
pkg/reference/reference.go
Normal file
49
pkg/reference/reference.go
Normal file
@@ -0,0 +1,49 @@
|
||||
// Package reference provides general types to represent oci content within a registry or local oci layout
|
||||
// Grammar (stolen mostly from containerd's grammar)
|
||||
//
|
||||
// reference :=
|
||||
package reference
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
gname "github.com/google/go-containerregistry/pkg/name"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultNamespace = "hauler"
|
||||
DefaultTag = "latest"
|
||||
)
|
||||
|
||||
type Reference interface {
|
||||
// FullName is the full name of the reference
|
||||
FullName() string
|
||||
|
||||
// Name is the registryless name
|
||||
Name() string
|
||||
}
|
||||
|
||||
// NewTagged will create a new docker.NamedTagged given a path-component
|
||||
func NewTagged(n string, tag string) (gname.Reference, error) {
|
||||
repo, err := Parse(n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return repo.Context().Tag(tag), nil
|
||||
}
|
||||
|
||||
// Parse will parse a reference and return a name.Reference namespaced with DefaultNamespace if necessary
|
||||
func Parse(ref string) (gname.Reference, error) {
|
||||
r, err := gname.ParseReference(ref, gname.WithDefaultRegistry(""), gname.WithDefaultTag(DefaultTag))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !strings.ContainsRune(r.String(), '/') {
|
||||
ref = DefaultNamespace + "/" + r.String()
|
||||
return gname.ParseReference(ref, gname.WithDefaultRegistry(""), gname.WithDefaultTag(DefaultTag))
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
57
pkg/reference/reference_test.go
Normal file
57
pkg/reference/reference_test.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package reference_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/rancherfederal/hauler/pkg/reference"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
type args struct {
|
||||
ref string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Should add hauler namespace when doesn't exist",
|
||||
args: args{
|
||||
ref: "myfile",
|
||||
},
|
||||
want: "hauler/myfile:latest",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "shouldn't modify namespaced reference",
|
||||
args: args{
|
||||
ref: "rancher/rancher:latest",
|
||||
},
|
||||
want: "rancher/rancher:latest",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Shouldn't modify canonical reference",
|
||||
args: args{
|
||||
ref: "index.docker.io/library/registry@sha256:42043edfae481178f07aa077fa872fcc242e276d302f4ac2026d9d2eb65b955f",
|
||||
},
|
||||
want: "index.docker.io/library/registry@sha256:42043edfae481178f07aa077fa872fcc242e276d302f4ac2026d9d2eb65b955f",
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := reference.Parse(tt.args.ref)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got.Name(), tt.want) {
|
||||
t.Errorf("Parse() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/rancherfederal/hauler/internal/cache"
|
||||
"github.com/rancherfederal/hauler/pkg/artifact"
|
||||
"github.com/rancherfederal/hauler/pkg/consts"
|
||||
"github.com/rancherfederal/hauler/pkg/reference"
|
||||
)
|
||||
|
||||
type Store struct {
|
||||
@@ -46,7 +47,7 @@ func NewStore(rootdir string, opts ...Options) (*Store, error) {
|
||||
// saved, the entirety of the layout is copied to the store (which is just a registry). This allows us to not only use
|
||||
// strict types to define generic content, but provides a processing pipeline suitable for extensibility. In the
|
||||
// future we'll allow users to define their own content that must adhere either by artifact.OCI or simply an OCI layout.
|
||||
func (s *Store) AddArtifact(ctx context.Context, oci artifact.OCI, reference string) (ocispec.Descriptor, error) {
|
||||
func (s *Store) AddArtifact(ctx context.Context, oci artifact.OCI, r string) (ocispec.Descriptor, error) {
|
||||
stage, err := newLayout()
|
||||
if err != nil {
|
||||
return ocispec.Descriptor{}, err
|
||||
@@ -57,10 +58,10 @@ func (s *Store) AddArtifact(ctx context.Context, oci artifact.OCI, reference str
|
||||
oci = cached
|
||||
}
|
||||
|
||||
// Ensure that index.docker.io isn't prepended
|
||||
ref, err := name.ParseReference(reference, name.WithDefaultRegistry(""), name.WithDefaultTag("latest"))
|
||||
// Validate we have a locatable reference
|
||||
ref, err := reference.Parse(r)
|
||||
if err != nil {
|
||||
return ocispec.Descriptor{}, fmt.Errorf("%w", ErrInvalidReference)
|
||||
return ocispec.Descriptor{}, err
|
||||
}
|
||||
|
||||
if err := stage.add(ctx, oci, ref); err != nil {
|
||||
|
||||
@@ -51,7 +51,7 @@ func TestStore_AddArtifact(t *testing.T) {
|
||||
name: "should add artifact with a valid tagged reference",
|
||||
args: args{
|
||||
ctx: ctx,
|
||||
reference: "random:v1",
|
||||
reference: "hauler/random:v1",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
|
||||
16
testdata/contents.yaml
vendored
16
testdata/contents.yaml
vendored
@@ -5,17 +5,17 @@ metadata:
|
||||
spec:
|
||||
files:
|
||||
# hauler can save/redistribute files on disk (be careful! paths are relative)
|
||||
- ref: testdata/contents.yaml
|
||||
- path: testdata/contents.yaml
|
||||
|
||||
# TODO: when directories are specified, they will be archived and stored as a file
|
||||
# - ref: testdata/
|
||||
# - path: testdata/
|
||||
|
||||
# hauler can also fetch remote content, and will "smartly" identify filenames _when possible_
|
||||
# filename below = "k3s-images.txt"
|
||||
- ref: "https://github.com/k3s-io/k3s/releases/download/v1.22.2%2Bk3s2/k3s-images.txt"
|
||||
- path: "https://github.com/k3s-io/k3s/releases/download/v1.22.2%2Bk3s2/k3s-images.txt"
|
||||
|
||||
# when filenames are not appropriate, a name should be specified
|
||||
- ref: https://get.k3s.io?filename=get-k3s.sh
|
||||
- path: https://get.k3s.io?filename=get-k3s.sh
|
||||
|
||||
---
|
||||
apiVersion: content.hauler.cattle.io/v1alpha1
|
||||
@@ -25,16 +25,16 @@ metadata:
|
||||
spec:
|
||||
images:
|
||||
# images can be referenced shorthanded without a tag
|
||||
- ref: hello-world
|
||||
- name: hello-world
|
||||
|
||||
# or namespaced with a tag
|
||||
- ref: rancher/cowsay:latest
|
||||
- name: rancher/cowsay:latest
|
||||
|
||||
# or by their digest:
|
||||
# - ref: registry@sha256:42043edfae481178f07aa077fa872fcc242e276d302f4ac2026d9d2eb65b955f
|
||||
- name: registry@sha256:42043edfae481178f07aa077fa872fcc242e276d302f4ac2026d9d2eb65b955f
|
||||
|
||||
# or fully qualified from any OCI compliant registry registry
|
||||
- ref: ghcr.io/fluxcd/flux-cli:v0.22.0
|
||||
- name: ghcr.io/fluxcd/flux-cli:v0.22.0
|
||||
|
||||
---
|
||||
apiVersion: content.hauler.cattle.io/v1alpha1
|
||||
|
||||
Reference in New Issue
Block a user