diff --git a/.gitignore b/.gitignore index e4da768..8ab6942 100644 --- a/.gitignore +++ b/.gitignore @@ -23,8 +23,7 @@ airgap-scp.sh # generated dist/ -./bundle/ tmp/ bin/ -pkg.yaml -haul/ \ No newline at end of file +/store/ +/registry/ \ No newline at end of file diff --git a/README.md b/README.md index 4890082..61d8f25 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Hauler: Airgap Assistant +# Hauler: Airgap Swiss Army Knife > ⚠️ This project is still in active development and _not_ GA. While a lot of the core features are ready, we're still adding a _ton_, and we may make breaking api and feature changes version to version. diff --git a/cmd/hauler/cli/cli.go b/cmd/hauler/cli/cli.go index b2b0271..62aaf11 100644 --- a/cmd/hauler/cli/cli.go +++ b/cmd/hauler/cli/cli.go @@ -8,7 +8,7 @@ import ( "github.com/spf13/cobra" - "github.com/rancherfederal/hauler/pkg/cache" + cache2 "github.com/rancherfederal/hauler/internal/cache" "github.com/rancherfederal/hauler/pkg/log" "github.com/rancherfederal/hauler/pkg/store" ) @@ -19,10 +19,13 @@ type rootOpts struct { storeDir string } -const defaultStoreLocation = "haul" - var ro = &rootOpts{} +const ( + DefaultStoreName = "store" + DefaultCacheDir = "hauler" +) + func New() *cobra.Command { cmd := &cobra.Command{ Use: "hauler", @@ -41,11 +44,12 @@ func New() *cobra.Command { pf := cmd.PersistentFlags() pf.StringVarP(&ro.logLevel, "log-level", "l", "info", "") pf.StringVar(&ro.cacheDir, "cache", "", "Location of where to store cache data (defaults to $XDG_CACHE_DIR/hauler)") - pf.StringVarP(&ro.storeDir, "store", "s", "", "Location to create store at (defaults to $PWD/store)") + pf.StringVarP(&ro.storeDir, "store", "s", DefaultStoreName, "Location to create store at") // Add subcommands addDownload(cmd) addStore(cmd) + addServe(cmd) addVersion(cmd) return cmd @@ -55,16 +59,6 @@ func (o *rootOpts) getStore(ctx context.Context) (*store.Store, error) { l := log.FromContext(ctx) dir := o.storeDir - if dir == "" { - l.Debugf("no store path specified, defaulting to $PWD/store") - pwd, err := os.Getwd() - if err != nil { - return nil, err - } - - dir = filepath.Join(pwd, defaultStoreLocation) - } - abs, err := filepath.Abs(dir) if err != nil { return nil, err @@ -86,11 +80,14 @@ func (o *rootOpts) getStore(ctx context.Context) (*store.Store, error) { return nil, err } - s := store.NewStore(ctx, abs, store.WithCache(c)) + s, err := store.NewStore(abs, store.WithCache(c)) + if err != nil { + return nil, err + } return s, nil } -func (o *rootOpts) getCache(ctx context.Context) (cache.Cache, error) { +func (o *rootOpts) getCache(ctx context.Context) (cache2.Cache, error) { dir := o.cacheDir if dir == "" { @@ -100,7 +97,7 @@ func (o *rootOpts) getCache(ctx context.Context) (cache.Cache, error) { return nil, err } - abs, _ := filepath.Abs(filepath.Join(cachedir, "hauler")) + abs, _ := filepath.Abs(filepath.Join(cachedir, DefaultCacheDir)) if err := os.MkdirAll(abs, os.ModePerm); err != nil { return nil, err } @@ -108,6 +105,6 @@ func (o *rootOpts) getCache(ctx context.Context) (cache.Cache, error) { dir = abs } - c := cache.NewFilesystem(dir) + c := cache2.NewFilesystem(dir) return c, nil } diff --git a/cmd/hauler/cli/download/download.go b/cmd/hauler/cli/download/download.go index 503a24d..35de066 100644 --- a/cmd/hauler/cli/download/download.go +++ b/cmd/hauler/cli/download/download.go @@ -6,17 +6,19 @@ import ( "fmt" "path" - "github.com/containerd/containerd/remotes/docker" - "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote" - "github.com/google/go-containerregistry/pkg/v1/tarball" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/spf13/cobra" "oras.land/oras-go/pkg/content" "oras.land/oras-go/pkg/oras" + "oras.land/oras-go/pkg/target" - "github.com/rancherfederal/hauler/pkg/artifact/types" + "github.com/rancherfederal/hauler/internal/mapper" + "github.com/rancherfederal/hauler/pkg/consts" + "github.com/rancherfederal/hauler/pkg/content/chart" + "github.com/rancherfederal/hauler/pkg/content/file" + "github.com/rancherfederal/hauler/pkg/content/image" "github.com/rancherfederal/hauler/pkg/log" ) @@ -35,8 +37,13 @@ func (o *Opts) AddArgs(cmd *cobra.Command) { func Cmd(ctx context.Context, o *Opts, reference string) error { l := log.FromContext(ctx) - cs := content.NewFileStore(o.DestinationDir) - defer cs.Close() + fs := content.NewFile(o.DestinationDir) + defer fs.Close() + + rs, err := content.NewRegistry(content.RegistryOptions{}) + if err != nil { + return err + } ref, err := name.ParseReference(reference) if err != nil { @@ -58,66 +65,76 @@ func Cmd(ctx context.Context, o *Opts, reference string) error { return err } + var ms target.Target // TODO: These need to be factored out into each of the contents own logic switch manifest.Config.MediaType { - case types.DockerConfigJSON, types.OCIManifestSchema1: + case consts.DockerConfigJSON, consts.OCIManifestSchema1: l.Debugf("identified [image] (%s) content", manifest.Config.MediaType) - img, err := remote.Image(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain)) - if err != nil { - return err - } outputFile := o.OutputFile if outputFile == "" { outputFile = fmt.Sprintf("%s:%s.tar", path.Base(ref.Context().RepositoryStr()), ref.Identifier()) } - if err := tarball.WriteToFile(outputFile, ref, img); err != nil { - return err - } + is := mapper.NewStore(o.DestinationDir, image.Mapper()) + defer is.Close() - l.Infof("downloaded image [%s] to [%s]", ref.Name(), outputFile) + ms = is - case types.FileConfigMediaType: + // l.Infof("downloaded image [%s] to [%s]", ref.Name(), outputFile) + + case consts.FileConfigMediaType: l.Debugf("identified [file] (%s) content", manifest.Config.MediaType) - fs := content.NewFileStore(o.DestinationDir) + fs := mapper.NewStore(o.DestinationDir, file.Mapper()) + defer fs.Close() - resolver := docker.NewResolver(docker.ResolverOptions{}) - _, descs, err := oras.Pull(ctx, resolver, ref.Name(), fs) - if err != nil { - return err - } + ms = fs - ldescs := len(descs) - for i, desc := range descs { - // NOTE: This is safe without a map key check b/c we're not allowing unnamed content from oras.Pull - l.Infof("downloaded (%d/%d) files to [%s]", i+1, ldescs, desc.Annotations[ocispec.AnnotationTitle]) - } + // _, err := oras.Copy(ctx, rs, ref.Name(), fs, "", + // oras.WithLayerDescriptors(func(descriptors []ocispec.Descriptor) { + // for _, desc := range descriptors { + // if _, ok := desc.Annotations[ocispec.AnnotationTitle]; !ok { + // continue + // } + // descs = append(descs, desc) + // } + // })) + // if err != nil { + // return err + // } + // + // ldescs := len(descs) + // for i, desc := range descs { + // // NOTE: This is safe without a map key check b/c we're not allowing unnamed content from oras.Pull + // l.Infof("downloaded (%d/%d) files to [%s]", i+1, ldescs, desc.Annotations[ocispec.AnnotationTitle]) + // } - case types.ChartLayerMediaType, types.ChartConfigMediaType: + case consts.ChartLayerMediaType, consts.ChartConfigMediaType: l.Debugf("identified [chart] (%s) content", manifest.Config.MediaType) - fs := content.NewFileStore(o.DestinationDir) + cs := mapper.NewStore(o.DestinationDir, chart.Mapper()) + defer cs.Close() - resolver := docker.NewResolver(docker.ResolverOptions{}) - _, descs, err := oras.Pull(ctx, resolver, ref.Name(), fs) - if err != nil { - return err - } - - cn := path.Base(ref.Name()) - for _, d := range descs { - if n, ok := d.Annotations[ocispec.AnnotationTitle]; ok { - cn = n - } - } - - l.Infof("downloaded chart [%s] to [%s]", ref.String(), cn) + ms = cs + // desc, err := oras.Copy(ctx, rs, ref.Name(), fs, "") + // if err != nil { + // return err + // } + // + // l.Infof("downloaded chart [%s] to [%s]", ref.String(), desc.Annotations[ocispec.AnnotationTitle]) default: return fmt.Errorf("unrecognized content type: %s", manifest.Config.MediaType) } + pushedDesc, err := oras.Copy(ctx, rs, ref.Name(), ms, "", + oras.WithAdditionalCachedMediaTypes(consts.DockerManifestSchema2)) + if err != nil { + return err + } + + l.Infof("downloaded [%s] with digest [%s]", pushedDesc.MediaType, pushedDesc.Digest.String()) + return nil } diff --git a/cmd/hauler/cli/serve.go b/cmd/hauler/cli/serve.go new file mode 100644 index 0000000..8a386a3 --- /dev/null +++ b/cmd/hauler/cli/serve.go @@ -0,0 +1,55 @@ +package cli + +import ( + "github.com/spf13/cobra" + + "github.com/rancherfederal/hauler/cmd/hauler/cli/serve" +) + +func addServe(parent *cobra.Command) { + cmd := &cobra.Command{ + Use: "serve", + Short: "Run one or more of hauler's embedded servers types", + RunE: func(cmd *cobra.Command, args []string) error { + return cmd.Help() + }, + } + + cmd.AddCommand( + addServeFiles(), + addServeRegistry(), + ) + + parent.AddCommand(cmd) +} + +func addServeFiles() *cobra.Command { + o := &serve.FilesOpts{} + cmd := &cobra.Command{ + Use: "files", + Short: "Start a fileserver", + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + return serve.FilesCmd(ctx, o) + }, + } + o.AddFlags(cmd) + + return cmd +} + +func addServeRegistry() *cobra.Command { + o := &serve.RegistryOpts{} + + cmd := &cobra.Command{ + Use: "registry", + Short: "Start a registry", + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + return serve.RegistryCmd(ctx, o) + }, + } + o.AddFlags(cmd) + + return cmd +} diff --git a/cmd/hauler/cli/serve/files.go b/cmd/hauler/cli/serve/files.go new file mode 100644 index 0000000..790545f --- /dev/null +++ b/cmd/hauler/cli/serve/files.go @@ -0,0 +1,33 @@ +package serve + +import ( + "context" + + "github.com/spf13/cobra" + + "github.com/rancherfederal/hauler/pkg/server" +) + +type FilesOpts struct { + Root string + Port int +} + +func (o *FilesOpts) AddFlags(cmd *cobra.Command) { + f := cmd.Flags() + f.StringVarP(&o.Root, "root", "r", ".", "Path to root of the directory to serve") + f.IntVarP(&o.Port, "port", "p", 8080, "Port to listen on") +} + +func FilesCmd(ctx context.Context, o *FilesOpts) error { + s, err := server.NewFile(ctx, o.Root) + if err != nil { + return err + } + + if err := s.ListenAndServe(); err != nil { + return err + } + + return nil +} diff --git a/cmd/hauler/cli/serve/registry.go b/cmd/hauler/cli/serve/registry.go new file mode 100644 index 0000000..d551885 --- /dev/null +++ b/cmd/hauler/cli/serve/registry.go @@ -0,0 +1,83 @@ +package serve + +import ( + "context" + "fmt" + "net/http" + "os" + + "github.com/distribution/distribution/v3/configuration" + dcontext "github.com/distribution/distribution/v3/context" + "github.com/distribution/distribution/v3/version" + "github.com/spf13/cobra" + + "github.com/rancherfederal/hauler/pkg/server" +) + +type RegistryOpts struct { + Root string + Port int + ConfigFile string +} + +func (o *RegistryOpts) AddFlags(cmd *cobra.Command) { + f := cmd.Flags() + f.StringVarP(&o.Root, "root", "r", ".", "Path to root of the directory to serve") + f.IntVarP(&o.Port, "port", "p", 5000, "Port to listen on") + f.StringVarP(&o.ConfigFile, "config", "c", "", "Path to a config file, will override all other configs") +} + +func RegistryCmd(ctx context.Context, o *RegistryOpts) error { + ctx = dcontext.WithVersion(ctx, version.Version) + + cfg := o.defaultConfig() + if o.ConfigFile != "" { + ucfg, err := loadConfig(o.ConfigFile) + if err != nil { + return err + } + cfg = ucfg + } + + s, err := server.NewRegistry(ctx, cfg) + if err != nil { + return err + } + + if err := s.ListenAndServe(); err != nil { + return err + } + + // TODO: Graceful cancelling + + return nil +} + +func loadConfig(filename string) (*configuration.Configuration, error) { + f, err := os.Open(filename) + if err != nil { + return nil, err + } + + return configuration.Parse(f) +} + +func (o *RegistryOpts) defaultConfig() *configuration.Configuration { + cfg := &configuration.Configuration{ + Version: "0.1", + Storage: configuration.Storage{ + "cache": configuration.Parameters{"blobdescriptor": "inmemory"}, + "filesystem": configuration.Parameters{"rootdirectory": o.Root}, + + // TODO: Ensure this is toggleable via cli arg if necessary + // "maintenance": configuration.Parameters{"readonly.enabled": false}, + }, + } + cfg.Log.Level = "info" + cfg.HTTP.Addr = fmt.Sprintf(":%d", o.Port) + cfg.HTTP.Headers = http.Header{ + "X-Content-Type-Options": []string{"nosniff"}, + } + + return cfg +} diff --git a/cmd/hauler/cli/store.go b/cmd/hauler/cli/store.go index 5098361..6682de7 100644 --- a/cmd/hauler/cli/store.go +++ b/cmd/hauler/cli/store.go @@ -22,7 +22,7 @@ func addStore(parent *cobra.Command) { addStoreLoad(), addStoreSave(), addStoreServe(), - addStoreList(), + addStoreInfo(), addStoreCopy(), // TODO: Remove this in favor of sync? @@ -92,8 +92,9 @@ func addStoreLoad() *cobra.Command { if err != nil { return err } + _ = s - return store.LoadCmd(ctx, o, s.DataDir, args...) + return store.LoadCmd(ctx, o, "", args...) }, } o.AddFlags(cmd) @@ -137,8 +138,9 @@ func addStoreSave() *cobra.Command { if err != nil { return err } + _ = s - return store.SaveCmd(ctx, o, o.FileName, s.DataDir) + return store.SaveCmd(ctx, o, o.FileName, "") }, } o.AddArgs(cmd) @@ -146,14 +148,14 @@ func addStoreSave() *cobra.Command { return cmd } -func addStoreList() *cobra.Command { - o := &store.ListOpts{} +func addStoreInfo() *cobra.Command { + o := &store.InfoOpts{} cmd := &cobra.Command{ - Use: "list", - Short: "List all content references in a store", + Use: "info", + Short: "Print out information about the store", Args: cobra.ExactArgs(0), - Aliases: []string{"ls"}, + Aliases: []string{"i"}, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() @@ -162,7 +164,7 @@ func addStoreList() *cobra.Command { return err } - return store.ListCmd(ctx, o, s) + return store.InfoCmd(ctx, o, s) }, } o.AddFlags(cmd) diff --git a/cmd/hauler/cli/store/add.go b/cmd/hauler/cli/store/add.go index 665a0fe..0b5b9ac 100644 --- a/cmd/hauler/cli/store/add.go +++ b/cmd/hauler/cli/store/add.go @@ -2,10 +2,8 @@ package store import ( "context" - "path/filepath" "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" @@ -25,13 +23,68 @@ func (o *AddFileOpts) AddFlags(cmd *cobra.Command) { f.StringVarP(&o.Name, "name", "n", "", "(Optional) Name to assign to file in store") } -func AddFileCmd(ctx context.Context, o *AddFileOpts, s *store.Store, reference string) error { - s.Open() - defer s.Close() +// 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.NewChart(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, - Name: o.Name, + Ref: reference, } return storeFile(ctx, s, cfg) @@ -40,28 +93,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) - if fi.Name == "" { - base := filepath.Base(fi.Ref) - fi.Name = filepath.Base(fi.Ref) - l.Warnf("no name specified for file reference [%s], using base filepath: [%s]", fi.Ref, base) - } - - oci, err := file.NewFile(fi.Ref, fi.Name) + f := file.NewFile(fi.Ref) + ref, err := name.ParseReference(f.Name(fi.Ref), name.WithDefaultRegistry("")) if err != nil { return err } - ref, err := name.ParseReference(fi.Name, name.WithDefaultRegistry("")) + desc, err := s.AddArtifact(ctx, f, ref) if err != nil { return err } - desc, err := s.AddArtifact(ctx, oci, ref) - if err != nil { - return err - } - - l.Infof("file [%s] added at: [%s]", ref.Name(), desc.Annotations[ocispec.AnnotationTitle]) + l.With(log.Fields{"type": s.Identify(ctx, desc)}).Infof("added [%s] to store", ref.Name()) return nil } @@ -75,9 +118,6 @@ func (o *AddImageOpts) AddFlags(cmd *cobra.Command) { } func AddImageCmd(ctx context.Context, o *AddImageOpts, s *store.Store, reference string) error { - s.Open() - defer s.Close() - cfg := v1alpha1.Image{ Ref: reference, } @@ -103,7 +143,7 @@ func storeImage(ctx context.Context, s *store.Store, i v1alpha1.Image) error { return err } - l.Infof("image [%s] added at: [%s]", ref.Name(), desc.Annotations[ocispec.AnnotationTitle]) + l.With(log.Fields{"type": s.Identify(ctx, desc)}).Infof("added [%s] to store", ref.Name()) return nil } @@ -112,15 +152,6 @@ type AddChartOpts struct { RepoURL string // TODO: Support helm auth - Username string - Password string - PassCredentialsAll bool - CertFile string - KeyFile string - CaFile string - InsecureSkipTLSverify bool - RepositoryConfig string - RepositoryCache string } func (o *AddChartOpts) AddFlags(cmd *cobra.Command) { @@ -131,9 +162,6 @@ func (o *AddChartOpts) AddFlags(cmd *cobra.Command) { } func AddChartCmd(ctx context.Context, o *AddChartOpts, s *store.Store, chartName string) error { - s.Open() - defer s.Close() - cfg := v1alpha1.Chart{ Name: chartName, RepoURL: o.RepoURL, @@ -166,6 +194,6 @@ func storeChart(ctx context.Context, s *store.Store, ch v1alpha1.Chart) error { return err } - l.Infof("chart [%s] added at: [%s]", ref.Name(), desc.Annotations[ocispec.AnnotationTitle]) + l.With(log.Fields{"type": s.Identify(ctx, desc)}).Infof("added [%s] to store", ref.Name()) return nil } diff --git a/cmd/hauler/cli/store/copy.go b/cmd/hauler/cli/store/copy.go index c630ce1..a921f32 100644 --- a/cmd/hauler/cli/store/copy.go +++ b/cmd/hauler/cli/store/copy.go @@ -2,56 +2,62 @@ package store import ( "context" + "fmt" + "strings" - "github.com/google/go-containerregistry/pkg/name" - "github.com/google/go-containerregistry/pkg/v1/remote" + "github.com/pkg/errors" "github.com/spf13/cobra" + "oras.land/oras-go/pkg/content" "github.com/rancherfederal/hauler/pkg/log" "github.com/rancherfederal/hauler/pkg/store" ) -type CopyOpts struct{} +type CopyOpts struct { + Target string +} func (o *CopyOpts) AddFlags(cmd *cobra.Command) { - f := cmd.Flags() - _ = f - - // TODO: Regex matching + _ = cmd.Flags() } -func CopyCmd(ctx context.Context, o *CopyOpts, s *store.Store, registry string) error { +func CopyCmd(ctx context.Context, o *CopyOpts, s *store.Store, targetRef string) error { l := log.FromContext(ctx) + _ = l - s.Open() - defer s.Close() + components := strings.SplitN(targetRef, "://", 2) + switch components[0] { + case "dir": + fs := content.NewFile(components[1]) + defer fs.Close() + + if err := s.Copy(ctx, fs, nil); err != nil { + return err + } + + case "registry": + r, err := content.NewRegistry(content.RegistryOptions{}) + if err != nil { + return err + } + + fmt.Println(components[1]) + + mapperFn := func(reference string) (string, error) { + ref, err := store.RelocateReference(reference, components[1]) + if err != nil { + return "", err + } + return ref.Name(), nil + } + + if err := s.Copy(ctx, r, mapperFn); err != nil { + return err + } + + default: + return errors.Errorf("determining target protocol from: [%s]", targetRef) - refs, err := s.List(ctx) - if err != nil { - return err } - - for _, r := range refs { - ref, err := name.ParseReference(r, name.WithDefaultRegistry(s.Registry())) - if err != nil { - return err - } - - o, err := remote.Image(ref) - if err != nil { - return err - } - - rref, err := name.ParseReference(r, name.WithDefaultRegistry(registry)) - if err != nil { - return err - } - - l.Infof("copying [%s] -> [%s]", ref.Name(), rref.Name()) - if err := remote.Write(rref, o); err != nil { - return err - } - } - return nil } diff --git a/cmd/hauler/cli/store/extract.go b/cmd/hauler/cli/store/extract.go index 1d7a945..41d22dd 100644 --- a/cmd/hauler/cli/store/extract.go +++ b/cmd/hauler/cli/store/extract.go @@ -6,7 +6,6 @@ import ( "github.com/spf13/cobra" "github.com/rancherfederal/hauler/cmd/hauler/cli/download" - "github.com/rancherfederal/hauler/pkg/layout" "github.com/rancherfederal/hauler/pkg/store" ) @@ -21,10 +20,7 @@ func (o *ExtractOpts) AddArgs(cmd *cobra.Command) { } func ExtractCmd(ctx context.Context, o *ExtractOpts, s *store.Store, reference string) error { - s.Open() - defer s.Close() - - eref, err := layout.RelocateReference(reference, s.Registry()) + eref, err := store.RelocateReference(reference, "") if err != nil { return err } diff --git a/cmd/hauler/cli/store/info.go b/cmd/hauler/cli/store/info.go new file mode 100644 index 0000000..4dfce8a --- /dev/null +++ b/cmd/hauler/cli/store/info.go @@ -0,0 +1,43 @@ +package store + +import ( + "context" + "fmt" + "os" + "text/tabwriter" + + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/spf13/cobra" + + "github.com/rancherfederal/hauler/pkg/store" +) + +type InfoOpts struct{} + +func (o *InfoOpts) AddFlags(cmd *cobra.Command) { + f := cmd.Flags() + _ = f + + // TODO: Regex matching +} + +func InfoCmd(ctx context.Context, o *InfoOpts, s *store.Store) error { + refs, err := s.List(ctx) + if err != nil { + return err + } + + tw := tabwriter.NewWriter(os.Stdout, 0, 8, 0, '\t', 0) + defer tw.Flush() + + fmt.Fprintf(tw, "Reference\tTag/Digest\tType\n") + fmt.Fprintf(tw, "---------\t----------\t----\n") + for _, r := range refs { + if _, ok := r.Annotations[ocispec.AnnotationRefName]; !ok { + continue + } + fmt.Fprintf(tw, "%s\t%s\n", r.Annotations[ocispec.AnnotationRefName], "") + } + + return nil +} diff --git a/cmd/hauler/cli/store/list.go b/cmd/hauler/cli/store/list.go deleted file mode 100644 index 449d4b8..0000000 --- a/cmd/hauler/cli/store/list.go +++ /dev/null @@ -1,47 +0,0 @@ -package store - -import ( - "context" - "fmt" - "os" - "text/tabwriter" - - "github.com/google/go-containerregistry/pkg/name" - "github.com/spf13/cobra" - - "github.com/rancherfederal/hauler/pkg/store" -) - -type ListOpts struct{} - -func (o *ListOpts) AddFlags(cmd *cobra.Command) { - f := cmd.Flags() - _ = f - - // TODO: Regex matching -} - -func ListCmd(ctx context.Context, o *ListOpts, s *store.Store) error { - s.Open() - defer s.Close() - - refs, err := s.List(ctx) - if err != nil { - return err - } - - tw := tabwriter.NewWriter(os.Stdout, 0, 8, 0, '\t', 0) - defer tw.Flush() - - fmt.Fprintf(tw, "Reference\tTag/Digest\n") - for _, r := range refs { - ref, err := name.ParseReference(r, name.WithDefaultRegistry("")) - if err != nil { - return err - } - - fmt.Fprintf(tw, "%s\t%s\n", ref.Context().String(), ref.Identifier()) - } - - return nil -} diff --git a/cmd/hauler/cli/store/serve.go b/cmd/hauler/cli/store/serve.go index f74dd89..8795345 100644 --- a/cmd/hauler/cli/store/serve.go +++ b/cmd/hauler/cli/store/serve.go @@ -7,14 +7,20 @@ import ( "os" "github.com/distribution/distribution/v3/configuration" - "github.com/distribution/distribution/v3/registry" + dcontext "github.com/distribution/distribution/v3/context" + _ "github.com/distribution/distribution/v3/registry/storage/driver/base" + _ "github.com/distribution/distribution/v3/registry/storage/driver/filesystem" + _ "github.com/distribution/distribution/v3/registry/storage/driver/inmemory" + "github.com/distribution/distribution/v3/version" "github.com/spf13/cobra" + "github.com/rancherfederal/hauler/pkg/server" "github.com/rancherfederal/hauler/pkg/store" ) type ServeOpts struct { Port int + RootDir string ConfigFile string Daemon bool @@ -25,13 +31,28 @@ func (o *ServeOpts) AddFlags(cmd *cobra.Command) { f := cmd.Flags() f.IntVarP(&o.Port, "port", "p", 5000, "Port to listen on") + f.StringVar(&o.RootDir, "directory", "registry", "Directory to use for registry backend (defaults to '$PWD/registry')") f.StringVarP(&o.ConfigFile, "config", "c", "", "Path to a config file, will override all other configs") f.BoolVarP(&o.Daemon, "daemon", "d", false, "Toggle serving as a daemon") } -// ServeCmd does +// ServeCmd serves the embedded registry almost identically to how distribution/v3 does it func ServeCmd(ctx context.Context, o *ServeOpts, s *store.Store) error { - cfg := o.defaultConfig(s) + ctx = dcontext.WithVersion(ctx, version.Version) + + tr := server.NewTempRegistry(ctx, o.RootDir) + if err := tr.Start(); err != nil { + return err + } + + opts := &CopyOpts{} + if err := CopyCmd(ctx, opts, s, "registry://"+tr.Registry()); err != nil { + return err + } + + tr.Close() + + cfg := o.defaultConfig() if o.ConfigFile != "" { ucfg, err := loadConfig(o.ConfigFile) if err != nil { @@ -40,7 +61,7 @@ func ServeCmd(ctx context.Context, o *ServeOpts, s *store.Store) error { cfg = ucfg } - r, err := registry.NewRegistry(ctx, cfg) + r, err := server.NewRegistry(ctx, cfg) if err != nil { return err } @@ -48,7 +69,6 @@ func ServeCmd(ctx context.Context, o *ServeOpts, s *store.Store) error { if err = r.ListenAndServe(); err != nil { return err } - return nil } @@ -61,15 +81,15 @@ func loadConfig(filename string) (*configuration.Configuration, error) { return configuration.Parse(f) } -func (o *ServeOpts) defaultConfig(s *store.Store) *configuration.Configuration { +func (o *ServeOpts) defaultConfig() *configuration.Configuration { cfg := &configuration.Configuration{ Version: "0.1", Storage: configuration.Storage{ "cache": configuration.Parameters{"blobdescriptor": "inmemory"}, - "filesystem": configuration.Parameters{"rootdirectory": s.DataDir}, + "filesystem": configuration.Parameters{"rootdirectory": o.RootDir}, // TODO: Ensure this is toggleable via cli arg if necessary - "maintenance": configuration.Parameters{"readonly.enabled": true}, + // "maintenance": configuration.Parameters{"readonly.enabled": false}, }, } cfg.Log.Level = "info" diff --git a/cmd/hauler/cli/store/sync.go b/cmd/hauler/cli/store/sync.go index 3f63ad2..b7adc3b 100644 --- a/cmd/hauler/cli/store/sync.go +++ b/cmd/hauler/cli/store/sync.go @@ -32,14 +32,11 @@ func SyncCmd(ctx context.Context, o *SyncOpts, s *store.Store) error { l := log.FromContext(ctx) // Start from an empty store (contents are cached elsewhere) - l.Debugf("flushing any existing content in store: %s", s.DataDir) + l.Debugf("flushing content store") if err := s.Flush(ctx); err != nil { return err } - s.Open() - defer s.Close() - for _, filename := range o.ContentFiles { l.Debugf("processing content file: '%s'", filename) fi, err := os.Open(filename) @@ -68,7 +65,7 @@ func SyncCmd(ctx context.Context, o *SyncOpts, s *store.Store) error { return err } - l.Infof("syncing [%s] to [%s]", obj.GroupVersionKind().String(), s.DataDir) + l.Infof("syncing [%s] to store", obj.GroupVersionKind().String()) // TODO: Should type switch instead... switch obj.GroupVersionKind().Kind { @@ -133,7 +130,7 @@ func SyncCmd(ctx context.Context, o *SyncOpts, s *store.Store) error { } for _, cfg := range cfg.Spec.Charts { - tc, err := tchart.NewChart(cfg.Name, cfg.RepoURL, cfg.Version) + tc, err := tchart.NewChart(cfg) if err != nil { return err } diff --git a/cmd/hauler/cli/version.go b/cmd/hauler/cli/version.go index 7e02795..bae31a1 100644 --- a/cmd/hauler/cli/version.go +++ b/cmd/hauler/cli/version.go @@ -5,7 +5,7 @@ import ( "github.com/spf13/cobra" - "github.com/rancherfederal/hauler/pkg/version" + "github.com/rancherfederal/hauler/internal/version" ) func addVersion(parent *cobra.Command) { @@ -13,8 +13,7 @@ func addVersion(parent *cobra.Command) { cmd := &cobra.Command{ Use: "version", - Short: "Print current hauler version", - Long: "Print current hauler version", + Short: "Print the current version", Aliases: []string{"v"}, RunE: func(cmd *cobra.Command, args []string) error { v := version.GetVersionInfo() diff --git a/go.mod b/go.mod index d007ad7..cd00209 100644 --- a/go.mod +++ b/go.mod @@ -3,32 +3,35 @@ module github.com/rancherfederal/hauler go 1.17 require ( - github.com/containerd/containerd v1.5.7 - github.com/distribution/distribution/v3 v3.0.0-20210926092439-1563384b69df - github.com/google/go-containerregistry v0.6.0 + github.com/containerd/containerd v1.5.8 + github.com/distribution/distribution/v3 v3.0.0-20211118083504-a29a3c99a684 + github.com/docker/go-metrics v0.0.1 + github.com/google/go-containerregistry v0.7.0 + github.com/gorilla/handlers v1.5.1 + github.com/gorilla/mux v1.8.0 github.com/mholt/archiver/v3 v3.5.0 github.com/opencontainers/go-digest v1.0.0 - github.com/opencontainers/image-spec v1.0.1 + github.com/opencontainers/image-spec v1.0.2 + github.com/pkg/errors v0.9.1 github.com/rancher/wrangler v0.8.4 github.com/rs/zerolog v1.26.0 github.com/sirupsen/logrus v1.8.1 github.com/spf13/cobra v1.2.1 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - helm.sh/helm/v3 v3.7.1 - k8s.io/apimachinery v0.22.2 - k8s.io/client-go v0.22.2 - oras.land/oras-go v0.4.0 - sigs.k8s.io/controller-runtime v0.10.3 + helm.sh/helm/v3 v3.6.1-0.20211119214113-6a1daecd0c29 + k8s.io/apimachinery v0.22.3 + k8s.io/client-go v0.22.3 + oras.land/oras-go v1.0.0 ) require ( github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect - github.com/BurntSushi/toml v0.3.1 // indirect + github.com/BurntSushi/toml v0.4.1 // indirect github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.1.1 // indirect github.com/Masterminds/sprig/v3 v3.2.2 // indirect - github.com/Masterminds/squirrel v1.5.0 // indirect + github.com/Masterminds/squirrel v1.5.1 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d // indirect @@ -40,16 +43,14 @@ require ( github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b // indirect github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 // indirect github.com/cespare/xxhash/v2 v2.1.1 // indirect - github.com/containerd/stargz-snapshotter/estargz v0.7.0 // indirect - github.com/cyphar/filepath-securejoin v0.2.2 // indirect + github.com/cyphar/filepath-securejoin v0.2.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/docker/cli v20.10.9+incompatible // indirect + github.com/docker/cli v20.10.11+incompatible // indirect github.com/docker/distribution v2.7.1+incompatible // indirect - github.com/docker/docker v20.10.9+incompatible // indirect + github.com/docker/docker v20.10.11+incompatible // indirect github.com/docker/docker-credential-helpers v0.6.4 // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect - github.com/docker/go-metrics v0.0.1 // indirect github.com/docker/go-units v0.4.0 // indirect github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 // indirect github.com/dsnet/compress v0.0.1 // indirect @@ -59,7 +60,7 @@ require ( github.com/felixge/httpsnoop v1.0.1 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-errors/errors v1.0.1 // indirect - github.com/go-logr/logr v0.4.0 // indirect + github.com/go-logr/logr v1.2.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.19.5 // indirect github.com/go-openapi/swag v0.19.14 // indirect @@ -75,21 +76,19 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.3.0 // indirect github.com/googleapis/gnostic v0.5.5 // indirect - github.com/gorilla/handlers v1.5.1 // indirect - github.com/gorilla/mux v1.8.0 // indirect github.com/gosuri/uitable v0.0.4 // indirect github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect github.com/huandu/xstrings v1.3.2 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect - github.com/jmoiron/sqlx v1.3.1 // indirect + github.com/jmoiron/sqlx v1.3.4 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.11 // indirect github.com/klauspost/compress v1.13.6 // indirect github.com/klauspost/pgzip v1.2.4 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect - github.com/lib/pq v1.10.0 // indirect + 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.1.8 // indirect @@ -107,9 +106,10 @@ require ( github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/nwaples/rardecode v1.1.0 // indirect + github.com/onsi/ginkgo v1.16.4 // indirect + github.com/onsi/gomega v1.15.0 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pierrec/lz4/v4 v4.0.3 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.11.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect @@ -124,7 +124,7 @@ require ( github.com/spf13/cast v1.4.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/testify v1.7.0 // indirect - github.com/ulikunitz/xz v0.5.7 // indirect + github.com/ulikunitz/xz v0.5.8 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect @@ -134,32 +134,32 @@ require ( github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 // indirect github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f // indirect go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect - golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect - golang.org/x/net v0.0.0-20210913180222-943fd674d43e // indirect - golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c // indirect - golang.org/x/sys v0.0.0-20211013075003-97ac67df715c // indirect + golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 // indirect + golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect + golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect + golang.org/x/sys v0.0.0-20211110154304-99a53858aa08 // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20210719143636-1d5a45f8e492 // indirect - google.golang.org/grpc v1.39.0 // indirect + google.golang.org/genproto v0.0.0-20211111162719-482062a4217b // indirect + google.golang.org/grpc v1.42.0 // indirect google.golang.org/protobuf v1.27.1 // indirect gopkg.in/gorp.v1 v1.7.2 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect - k8s.io/api v0.22.2 // indirect - k8s.io/apiextensions-apiserver v0.22.2 // indirect - k8s.io/apiserver v0.22.2 // indirect - k8s.io/cli-runtime v0.22.1 // indirect - k8s.io/component-base v0.22.2 // indirect - k8s.io/klog/v2 v2.9.0 // indirect + k8s.io/api v0.22.3 // indirect + k8s.io/apiextensions-apiserver v0.22.3 // indirect + k8s.io/apiserver v0.22.3 // indirect + k8s.io/cli-runtime v0.22.3 // indirect + k8s.io/component-base v0.22.3 // indirect + k8s.io/klog/v2 v2.30.0 // indirect k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e // indirect - k8s.io/kubectl v0.22.1 // indirect + k8s.io/kubectl v0.22.3 // indirect k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a // indirect sigs.k8s.io/kustomize/api v0.8.11 // indirect sigs.k8s.io/kustomize/kyaml v0.11.0 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect - sigs.k8s.io/yaml v1.2.0 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index 0d79372..0525c78 100644 --- a/go.sum +++ b/go.sum @@ -20,6 +20,12 @@ cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECH cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -69,8 +75,9 @@ github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw= +github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= @@ -87,8 +94,8 @@ github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZC github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= -github.com/Masterminds/squirrel v1.5.0 h1:JukIZisrUXadA9pl3rMkjhiamxiB0cXiu+HGp/Y8cY8= -github.com/Masterminds/squirrel v1.5.0/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= +github.com/Masterminds/squirrel v1.5.1 h1:kWAKlLLJFxZG7N2E0mBMNWVp5AuUX+JUrnhFN74Eg+w= +github.com/Masterminds/squirrel v1.5.1/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/Masterminds/vcs v1.13.1/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= @@ -98,8 +105,8 @@ github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugX github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU= -github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= +github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= @@ -107,8 +114,9 @@ github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg3 github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= -github.com/Microsoft/hcsshim v0.8.21 h1:btRfUDThBE5IKcvI8O8jOiIkujUsAMBSRsYDYmEi6oM= github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= +github.com/Microsoft/hcsshim v0.8.23 h1:47MSwtKGXet80aIn+7h4YI6fwPmwIghAnsx2aOUrG2M= +github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg= github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= @@ -147,7 +155,6 @@ github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:o github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.34.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -173,6 +180,7 @@ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembj github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= +github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= @@ -195,7 +203,11 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= @@ -228,14 +240,15 @@ github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMX github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= -github.com/containerd/containerd v1.5.2/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= -github.com/containerd/containerd v1.5.7 h1:rQyoYtj4KddB3bxG6SAqd4+08gePNyJjRqvOIfV3rkM= github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= +github.com/containerd/containerd v1.5.8 h1:NmkCC1/QxyZFBny8JogwLpOy2f+VEbO/f6bV2Mqtwuw= +github.com/containerd/containerd v1.5.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= @@ -263,13 +276,14 @@ github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJ github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= -github.com/containerd/stargz-snapshotter/estargz v0.7.0 h1:1d/rydzTywc76lnjJb6qbPCiTiCwts49AzKps/Ecblw= -github.com/containerd/stargz-snapshotter/estargz v0.7.0/go.mod h1:83VWDqHnurTKliEB0YvWMiCfLDwv4Cjj1X9Vk98GJZw= +github.com/containerd/stargz-snapshotter/estargz v0.10.0 h1:glqzafvxBBAMo+x2w2sdDjUDZeTqqLJmqZPY05qehCU= +github.com/containerd/stargz-snapshotter/estargz v0.10.0/go.mod h1:aE5PCyhFMwR8sbrErO5eM2GcvkyXTTJremG883D4qF0= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ= github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= @@ -310,12 +324,14 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= +github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= +github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= @@ -331,23 +347,20 @@ github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8l github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/distribution/distribution/v3 v3.0.0-20210804104954-38ab4c606ee3/go.mod h1:gt38b7cvVKazi5XkHvINNytZXgTEntyhtyM3HQz46Nk= -github.com/distribution/distribution/v3 v3.0.0-20210926092439-1563384b69df h1:zafDqOsnugdrReF9Pe0wybnfFtEIaegSyHNIvnwKPVk= -github.com/distribution/distribution/v3 v3.0.0-20210926092439-1563384b69df/go.mod h1:ZDZib/BOniVWcXcsy0voU8gR00znhe5VJm47d3H2Y5g= +github.com/distribution/distribution/v3 v3.0.0-20211118083504-a29a3c99a684 h1:DBZ2sN7CK6dgvHVpQsQj4sRMCbWTmd17l+5SUCjnQSY= +github.com/distribution/distribution/v3 v3.0.0-20211118083504-a29a3c99a684/go.mod h1:UfCu3YXJJCI+IdnqGgYP82dk2+Joxmv+mUTVBES6wac= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= -github.com/docker/cli v20.10.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/cli v20.10.9+incompatible h1:OJ7YkwQA+k2Oi51lmCojpjiygKpi76P7bg91b2eJxYU= -github.com/docker/cli v20.10.9+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v20.10.10+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v20.10.11+incompatible h1:tXU1ezXcruZQRrMP8RN2z9N91h+6egZTS1gsPsKantc= +github.com/docker/cli v20.10.11+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.9+incompatible h1:JlsVnETOjM2RLQa0Cc1XCIspUdXW3Zenq9P54uXBm6k= -github.com/docker/docker v20.10.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= +github.com/docker/docker v20.10.10+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.11+incompatible h1:OqzI/g/W54LczvhnccGqniFoQghHx3pklbLuhfXpqGo= +github.com/docker/docker v20.10.11+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o= github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= @@ -383,9 +396,9 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v0.0.0-20200808040245-162e5629780b/go.mod h1:NAJj0yf/KaRKURN6nyi7A9IZydMivZEm9oQLWNjfKDc= -github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -407,7 +420,6 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= -github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7 h1:LofdAjjjqCSXMwLGgOgnE+rdPuvX9DxCqaHwKy7i/ko= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -429,10 +441,10 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc= github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= -github.com/go-logr/zapr v0.4.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= @@ -534,6 +546,7 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -579,8 +592,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-containerregistry v0.6.0 h1:niQ+8XD//kKgArIFwDVBXsWVWbde16LPdHMyNwSC8h4= -github.com/google/go-containerregistry v0.6.0/go.mod h1:euCCtNbZ6tKqi1E72vwDj2xZcN5ttKpZLfa/wSo5iLw= +github.com/google/go-containerregistry v0.7.0 h1:u0onUUOcyoCDHEiJoyR1R1gx5er1+r06V5DBhUU5ndk= +github.com/google/go-containerregistry v0.7.0/go.mod h1:2zaoelrL0d08gGbpdP3LqyUuBmhWbpD6IOe2s9nLS2k= github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= @@ -601,6 +614,8 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= @@ -612,6 +627,7 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= @@ -625,7 +641,6 @@ github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= @@ -688,8 +703,8 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= -github.com/jmoiron/sqlx v1.3.1 h1:aLN7YINNZ7cYOPK3QC83dbM6KT0NMqVMw961TqrejlE= -github.com/jmoiron/sqlx v1.3.1/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= +github.com/jmoiron/sqlx v1.3.4 h1:wv+0IJZfL5z0uZoUjlpKgHkgaFSYD+r9CfrXjEXsO7w= +github.com/jmoiron/sqlx v1.3.4/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -717,8 +732,6 @@ github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0 github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.0/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= @@ -744,8 +757,9 @@ github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtB github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= +github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= @@ -888,8 +902,10 @@ github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go. github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.2-0.20210730191737-8e42a01fb1b7/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= +github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= @@ -1073,12 +1089,16 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1 github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= -github.com/ulikunitz/xz v0.5.7 h1:YvTNdFzX6+W5m9msiYg/zpkSURPPtOlzbqYjrFn7Yt4= github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ= +github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= +github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= @@ -1169,7 +1189,6 @@ go.uber.org/zap v0.0.0-20180814183419-67bc79d13d15/go.mod h1:vwi/ZaCAaUcBkycHslx go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1187,7 +1206,6 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -1196,10 +1214,9 @@ golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 h1:/pEO3GD/ABYAjuakUS6xSEmmlyVS4kxBNkeA9tLJiTI= +golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1296,10 +1313,10 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210913180222-943fd674d43e h1:+b/22bPvDYt4NPDcy4xAGCmON713ONAWFeY3Z7I3tR8= -golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211111160137-58aab5ef257a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1312,8 +1329,12 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c h1:pkQiBZBvdos9qq4wBAHqlzuZHEXo07pqV06ef90u1WI= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1423,10 +1444,13 @@ golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211013075003-97ac67df715c h1:taxlMj0D/1sOAuv/CbSD+MMDof2vbyPTqz5FNYKpXt8= -golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211110154304-99a53858aa08 h1:WecRHqgE09JBkh/584XIE6PMz5KKE/vER4izNUi30AQ= +golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1518,6 +1542,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1526,7 +1552,6 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= -gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= @@ -1554,6 +1579,12 @@ google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBz google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1610,8 +1641,21 @@ google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210719143636-1d5a45f8e492 h1:7yQQsvnwjfEahbNNEKcBHv3mR+HnB1ctGY/z1JXzx8M= -google.golang.org/genproto v0.0.0-20210719143636-1d5a45f8e492/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211111162719-482062a4217b h1:qvEQEwKjZRAg6rjY/jqfJ7T8/w/D7jTIFJGcaSka96k= +google.golang.org/genproto v0.0.0-20211111162719-482062a4217b/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1638,8 +1682,11 @@ google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0 h1:Klz8I9kdtkIN6EpHHUOMLCYhTn/2WAe5a0s1hcBkdTI= google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.42.0 h1:XT2/MFpuPFsEX2fWh3YQtHkZ+WYZFQRfaUgLZYj/p6A= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1704,8 +1751,8 @@ gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81 gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= -helm.sh/helm/v3 v3.7.1 h1:kED/HWx09QHHSJhYaJY6ttj/BhmzBmT1oupKslncibY= -helm.sh/helm/v3 v3.7.1/go.mod h1:3eOeBD3Z+O/ELiuu19zynZSN8jP1ErXLuyP21SZeMq8= +helm.sh/helm/v3 v3.6.1-0.20211119214113-6a1daecd0c29 h1:XV2RveKv7jaAhAO4nfDdwBvpZnuT47N69O9MNgYAFVw= +helm.sh/helm/v3 v3.6.1-0.20211119214113-6a1daecd0c29/go.mod h1:tplOkx4vact5IEykn7M9HmaBktm2RzwInMEQxlA4P/E= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1721,15 +1768,13 @@ k8s.io/api v0.18.8/go.mod h1:d/CXqwWv+Z2XEG1LgceeDmHQwpUJhROPx16SlxJgERY= k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= -k8s.io/api v0.22.1/go.mod h1:bh13rkTp3F1XEaLGykbyRD2QaTTzPm0e/BMd8ptFONY= -k8s.io/api v0.22.2 h1:M8ZzAD0V6725Fjg53fKeTJxGsJvRbk4TEm/fexHMtfw= -k8s.io/api v0.22.2/go.mod h1:y3ydYpLJAaDI+BbSe2xmGcqxiWHmWjkEeIbiwHvnPR8= +k8s.io/api v0.22.3 h1:wOoES2GoSkUsdped2RB4zYypPqWtvprGoKCENTOOjP4= +k8s.io/api v0.22.3/go.mod h1:azgiXFiXqiWyLCfI62/eYBOu19rj2LKmIhFPP4+33fs= k8s.io/apiextensions-apiserver v0.0.0-20190918161926-8f644eb6e783/go.mod h1:xvae1SZB3E17UpV59AWc271W/Ph25N+bjPyR63X6tPY= k8s.io/apiextensions-apiserver v0.17.2/go.mod h1:4KdMpjkEjjDI2pPfBA15OscyNldHWdBCfsWMDWAmSTs= k8s.io/apiextensions-apiserver v0.18.0/go.mod h1:18Cwn1Xws4xnWQNC00FLq1E350b9lUF+aOdIWDOZxgo= -k8s.io/apiextensions-apiserver v0.22.1/go.mod h1:HeGmorjtRmRLE+Q8dJu6AYRoZccvCMsghwS8XTUYb2c= -k8s.io/apiextensions-apiserver v0.22.2 h1:zK7qI8Ery7j2CaN23UCFaC1hj7dMiI87n01+nKuewd4= -k8s.io/apiextensions-apiserver v0.22.2/go.mod h1:2E0Ve/isxNl7tWLSUDgi6+cmwHi5fQRdwGVCxbC+KFA= +k8s.io/apiextensions-apiserver v0.22.3 h1:bKku7MqawIbtTZc084BZoMV4fz0WZuvCnB5E+yrQXGM= +k8s.io/apiextensions-apiserver v0.22.3/go.mod h1:f4plF+CXeqI89jAXL0Ml4LI/kSAZ54JS94+XOX1sae8= k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655/go.mod h1:nL6pwRT8NgfF8TT68DBI8uEePRt89cSvoXUVqbkWHq4= k8s.io/apimachinery v0.0.0-20191214185652-442f8fb2f03a/go.mod h1:Ng1IY8TS7sC44KJxT/WUR6qFRfWwahYYYpNXyYRKOCY= k8s.io/apimachinery v0.0.0-20191216025728-0ee8b4573e3a/go.mod h1:Ng1IY8TS7sC44KJxT/WUR6qFRfWwahYYYpNXyYRKOCY= @@ -1739,22 +1784,20 @@ k8s.io/apimachinery v0.18.8/go.mod h1:6sQd+iHEqmOtALqOFjSWp2KZ9F0wlU/nWm0ZgsYWMi k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= -k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= -k8s.io/apimachinery v0.22.2 h1:ejz6y/zNma8clPVfNDLnPbleBo6MpoFy/HBiBqCouVk= -k8s.io/apimachinery v0.22.2/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= +k8s.io/apimachinery v0.22.3 h1:mrvBG5CZnEfwgpVqWcrRKvdsYECTrhAR6cApAgdsflk= +k8s.io/apimachinery v0.22.3/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= k8s.io/apiserver v0.0.0-20190918160949-bfa5e2e684ad/go.mod h1:XPCXEwhjaFN29a8NldXA901ElnKeKLrLtREO9ZhFyhg= k8s.io/apiserver v0.17.2/go.mod h1:lBmw/TtQdtxvrTk0e2cgtOxHizXI+d0mmGQURIHQZlo= k8s.io/apiserver v0.18.0/go.mod h1:3S2O6FeBBd6XTo0njUrLxiqk8GNy6wWOftjhJcXYnjw= k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= -k8s.io/apiserver v0.22.1/go.mod h1:2mcM6dzSt+XndzVQJX21Gx0/Klo7Aen7i0Ai6tIa400= -k8s.io/apiserver v0.22.2 h1:TdIfZJc6YNhu2WxeAOWq1TvukHF0Sfx0+ln4XK9qnL4= -k8s.io/apiserver v0.22.2/go.mod h1:vrpMmbyjWrgdyOvZTSpsusQq5iigKNWv9o9KlDAbBHI= +k8s.io/apiserver v0.22.3 h1:x21xyLQ2qvPr5vjOTVOBaSJu8svnU2wfLOfSjNJEOdw= +k8s.io/apiserver v0.22.3/go.mod h1:oam7lH/F1Kto/WTamyQYrD68fS0mGUBORAFf6x/9Mxs= k8s.io/cli-runtime v0.0.0-20191214191754-e6dc6d5c8724/go.mod h1:wzlq80lvjgHW9if6MlE4OIGC86MDKsy5jtl9nxz/IYY= k8s.io/cli-runtime v0.17.2/go.mod h1:aa8t9ziyQdbkuizkNLAw3qe3srSyWh9zlSB7zTqRNPI= -k8s.io/cli-runtime v0.22.1 h1:WIueieKvT+IiSVSFosRLI6rkM0tyBGEGH1WUEztVjho= -k8s.io/cli-runtime v0.22.1/go.mod h1:YqwGrlXeEk15Yn3em2xzr435UGwbrCw5x+COQoTYfoo= +k8s.io/cli-runtime v0.22.3 h1:AeOgaDpb/k36amWsjyyIU+FLpLzzdmoLD5gn38c5fio= +k8s.io/cli-runtime v0.22.3/go.mod h1:um6JvCxV9Hrhq0zCUxcqYoY7/wF64g6IYgOViI8sg6Q= k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90/go.mod h1:J69/JveO6XESwVgG53q3Uz5OSfgsv4uxpScmmyYOOlk= k8s.io/client-go v0.0.0-20191214190045-a32a6f7a3052/go.mod h1:tAaoc/sYuIL0+njJefSAmE28CIcxyaFV4kbIujBlY2s= k8s.io/client-go v0.0.0-20191219150334-0b8da7416048/go.mod h1:ZEe8ZASDUAuqVGJ+UN0ka0PfaR+b6a6E1PGsSNZRui8= @@ -1764,15 +1807,13 @@ k8s.io/client-go v0.18.8/go.mod h1:HqFqMllQ5NnQJNwjro9k5zMyfhZlOwpuTLVrxjkYSxU= k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= -k8s.io/client-go v0.22.1/go.mod h1:BquC5A4UOo4qVDUtoc04/+Nxp1MeHcVc1HJm1KmG8kk= -k8s.io/client-go v0.22.2 h1:DaSQgs02aCC1QcwUdkKZWOeaVsQjYvWv8ZazcZ6JcHc= -k8s.io/client-go v0.22.2/go.mod h1:sAlhrkVDf50ZHx6z4K0S40wISNTarf1r800F+RlCF6U= +k8s.io/client-go v0.22.3 h1:6onkOSc+YNdwq5zXE0wFXicq64rrym+mXwHu/CPVGO4= +k8s.io/client-go v0.22.3/go.mod h1:ElDjYf8gvZsKDYexmsmnMQ0DYO8W9RwBjfQ1PI53yow= k8s.io/code-generator v0.0.0-20190912054826-cd179ad6a269/go.mod h1:V5BD6M4CyaN5m+VthcclXWsVcT1Hu+glwa1bi3MIsyE= k8s.io/code-generator v0.0.0-20191214185510-0b9b3c99f9f2/go.mod h1:BjGKcoq1MRUmcssvHiSxodCco1T6nVIt4YeCT5CMSao= k8s.io/code-generator v0.17.2/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s= k8s.io/code-generator v0.18.0/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= -k8s.io/code-generator v0.22.1/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o= -k8s.io/code-generator v0.22.2/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o= +k8s.io/code-generator v0.22.3/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o= k8s.io/component-base v0.0.0-20190918160511-547f6c5d7090/go.mod h1:933PBGtQFJky3TEwYx4aEPZ4IxqhWh3R6DCmzqIn1hA= k8s.io/component-base v0.0.0-20191214190519-d868452632e2/go.mod h1:wupxkh1T/oUDqyTtcIjiEfpbmIHGm8By/vqpSKC6z8c= k8s.io/component-base v0.17.2/go.mod h1:zMPW3g5aH7cHJpKYQ/ZsGMcgbsA/VyhEugF3QT1awLs= @@ -1780,10 +1821,9 @@ k8s.io/component-base v0.18.0/go.mod h1:u3BCg0z1uskkzrnAKFzulmYaEpZF7XC9Pf/uFyb1 k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= -k8s.io/component-base v0.22.1/go.mod h1:0D+Bl8rrnsPN9v0dyYvkqFfBeAd4u7n77ze+p8CMiPo= -k8s.io/component-base v0.22.2 h1:vNIvE0AIrLhjX8drH0BgCNJcR4QZxMXcJzBsDplDx9M= -k8s.io/component-base v0.22.2/go.mod h1:5Br2QhI9OTe79p+TzPe9JKNQYvEKbq9rTJDWllunGug= -k8s.io/component-helpers v0.22.1/go.mod h1:QvBcDbX+qU5I2tMZABBF5fRwAlQwiv771IGBHK9WYh4= +k8s.io/component-base v0.22.3 h1:/+hryAW03u3FpJQww+GSMsArJNUbGjH66lrgxaRynLU= +k8s.io/component-base v0.22.3/go.mod h1:kuybv1miLCMoOk3ebrqF93GbQHQx6W2287FC0YEQY6s= +k8s.io/component-helpers v0.22.3/go.mod h1:7OVySVH5elhHKuJKUOxZEfpT1Bm3ChmBQZHmuFfbGHk= k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= @@ -1801,8 +1841,9 @@ k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM= k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= +k8s.io/klog/v2 v2.30.0 h1:bUO6drIvCIsvZ/XFgfxoGFQU/a4Qkh0iAlvUR7vlHJw= +k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-aggregator v0.18.0/go.mod h1:ateewQ5QbjMZF/dihEFXwaEwoA4v/mayRvzfmvb6eqI= k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= @@ -1812,16 +1853,15 @@ k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAG k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kubectl v0.0.0-20191219154910-1528d4eea6dd/go.mod h1:9ehGcuUGjXVZh0qbYSB0vvofQw2JQe6c6cO0k4wu/Oo= -k8s.io/kubectl v0.22.1 h1:kpXO+ajPNTzAVLDM9pAzCsWH9MtCMr92zpcvXMt7P6E= -k8s.io/kubectl v0.22.1/go.mod h1:mjAOgEbMNMtZWxnfM6jd+nPjPsaoLqO5xanc78WcSbw= +k8s.io/kubectl v0.22.3 h1:xziSHHyFHg2nt9vE6A0XqW5dOePNSlzxG8z3z+IY63E= +k8s.io/kubectl v0.22.3/go.mod h1:gcpQHPOx+Jke9Og6Li7YxR/ZuaOtFUeJw7xHH617tHs= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/metrics v0.0.0-20191214191643-6b1944c9f765/go.mod h1:5V7rewilItwK0cz4nomU0b3XCcees2Ka5EBYWS1HBeM= -k8s.io/metrics v0.22.1/go.mod h1:i/ZNap89UkV1gLa26dn7fhKAdheJaKy+moOqJbiif7E= +k8s.io/metrics v0.22.3/go.mod h1:HbLFLRKtXzoC/6tHLQAlO9AeOBXZp2eB6SsgkbujoNI= k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210707171843-4b05e18ac7d9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a h1:8dYfu/Fc9Gz2rNJKB9IQRGgQOh2clmRzNIPPY1xLY5g= k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= @@ -1829,11 +1869,9 @@ modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= -oras.land/oras-go v0.4.0 h1:u6+7D+raZDYHwlz/uOwNANiRmyYDSSMW7A9E1xXycUQ= -oras.land/oras-go v0.4.0/go.mod h1:VJcU+VE4rkclUbum5C0O7deEZbBYnsnpbGSACwTjOcg= +oras.land/oras-go v1.0.0 h1:R5+g6OYqsOPGcdwHZkMpT0tpKvTiIB8zAer0Nv+WF3c= +oras.land/oras-go v1.0.0/go.mod h1:MZN6VbUUZjfWRF1EJKPbAWhJtE+R8JVicRmeYZp8qDg= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/letsencrypt v0.0.3 h1:H7xDfhkaFFSYEJlKeq38RwX2jYcnTeHuDQyT+mMNMwM= -rsc.io/letsencrypt v0.0.3/go.mod h1:buyQKZ6IXrRnB7TdkHP0RyEybLx18HHyOSoTyoOLqNY= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0= @@ -1842,8 +1880,6 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyz sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/cli-utils v0.16.0/go.mod h1:9Jqm9K2W6ShhCxsEuaz6HSRKKOXigPUx3ZfypGgxBLY= sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns= -sigs.k8s.io/controller-runtime v0.10.3 h1:s5Ttmw/B4AuIbwrXD3sfBkXwnPMMWrqpVj4WRt1dano= -sigs.k8s.io/controller-runtime v0.10.3/go.mod h1:CQp8eyUQZ/Q7PJvnIrB6/hgfTC1kBkGylwsLgOQi1WY= sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0= sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= sigs.k8s.io/kustomize/api v0.8.11 h1:LzQzlq6Z023b+mBtc6v72N2mSHYmN8x7ssgbf/hv0H8= @@ -1865,6 +1901,7 @@ sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3 sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/testing_frameworks v0.1.2/go.mod h1:ToQrwSC3s8Xf/lADdZp3Mktcql9CG0UAmdJG9th5i0w= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI= diff --git a/pkg/cache/cache.go b/internal/cache/cache.go similarity index 100% rename from pkg/cache/cache.go rename to internal/cache/cache.go diff --git a/pkg/cache/doc.go b/internal/cache/doc.go similarity index 100% rename from pkg/cache/doc.go rename to internal/cache/doc.go diff --git a/pkg/cache/filesystem.go b/internal/cache/filesystem.go similarity index 93% rename from pkg/cache/filesystem.go rename to internal/cache/filesystem.go index fd3c547..dc6e147 100644 --- a/pkg/cache/filesystem.go +++ b/internal/cache/filesystem.go @@ -7,7 +7,7 @@ import ( v1 "github.com/google/go-containerregistry/pkg/v1" - "github.com/rancherfederal/hauler/pkg/artifact/local" + "github.com/rancherfederal/hauler/internal/layer" ) type fs struct { @@ -37,14 +37,14 @@ func (f *fs) Put(l v1.Layer) (v1.Layer, error) { func (f *fs) Get(h v1.Hash) (v1.Layer, error) { opener := f.open(h) - l, err := local.LayerFromOpener(opener) + l, err := layer.FromOpener(opener) if os.IsNotExist(err) { return nil, ErrLayerNotFound } return l, err } -func (f *fs) open(h v1.Hash) local.Opener { +func (f *fs) open(h v1.Hash) layer.Opener { return func() (io.ReadCloser, error) { return os.Open(layerpath(f.root, h)) } diff --git a/internal/getter/directory.go b/internal/getter/directory.go new file mode 100644 index 0000000..250bdf9 --- /dev/null +++ b/internal/getter/directory.go @@ -0,0 +1,148 @@ +package getter + +import ( + "archive/tar" + "compress/gzip" + "context" + "io" + "net/url" + "os" + "path/filepath" + "time" + + "github.com/opencontainers/go-digest" + "github.com/pkg/errors" +) + +type directory struct { + file +} + +func (d directory) Open(ctx context.Context, u *url.URL) (io.ReadCloser, error) { + tmpfile, err := os.CreateTemp("", "hauler") + if err != nil { + return nil, err + } + + digester := digest.Canonical.Digester() + zw := gzip.NewWriter(io.MultiWriter(tmpfile, digester.Hash())) + defer zw.Close() + + tarDigester := digest.Canonical.Digester() + if err := tarDir(d.path(u), d.Name(u), io.MultiWriter(zw, tarDigester.Hash()), true); err != nil { + return nil, err + } + + if err := zw.Close(); err != nil { + return nil, err + } + if err := tmpfile.Sync(); err != nil { + return nil, err + } + + fi, err := os.Open(tmpfile.Name()) + if err != nil { + return nil, err + } + + // rc := &closer{ + // t: io.TeeReader(tmpfile, fi), + // closes: []func() error{fi.Close, tmpfile.Close, zw.Close}, + // } + return fi, nil +} + +func (d directory) Detect(u *url.URL) bool { + if len(d.path(u)) == 0 { + return false + } + + if fi, err := os.Stat(d.path(u)); err != nil { + return false + } else if !fi.IsDir() { + return false + } + return true +} + +func tarDir(root string, prefix string, w io.Writer, stripTimes bool) error { + tw := tar.NewWriter(w) + defer tw.Close() + if err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + // Rename path + name, err := filepath.Rel(root, path) + if err != nil { + return err + } + name = filepath.Join(prefix, name) + name = filepath.ToSlash(name) + + // Generate header + var link string + mode := info.Mode() + if mode&os.ModeSymlink != 0 { + if link, err = os.Readlink(path); err != nil { + return err + } + } + header, err := tar.FileInfoHeader(info, link) + if err != nil { + return errors.Wrap(err, path) + } + header.Name = name + header.Uid = 0 + header.Gid = 0 + header.Uname = "" + header.Gname = "" + + if stripTimes { + header.ModTime = time.Time{} + header.AccessTime = time.Time{} + header.ChangeTime = time.Time{} + } + + // Write file + if err := tw.WriteHeader(header); err != nil { + return errors.Wrap(err, "tar") + } + if mode.IsRegular() { + file, err := os.Open(path) + if err != nil { + return err + } + defer file.Close() + if _, err := io.Copy(tw, file); err != nil { + return errors.Wrap(err, path) + } + } + + return nil + }); err != nil { + return err + } + return nil +} + +type closer struct { + t io.Reader + closes []func() error +} + +func (c *closer) Read(p []byte) (n int, err error) { + return c.t.Read(p) +} + +func (c *closer) Close() error { + var err error + for _, c := range c.closes { + lastErr := c() + if err == nil { + err = lastErr + } + } + return err +} diff --git a/internal/getter/file.go b/internal/getter/file.go new file mode 100644 index 0000000..380ac0e --- /dev/null +++ b/internal/getter/file.go @@ -0,0 +1,45 @@ +package getter + +import ( + "context" + "io" + "net/url" + "os" + "path/filepath" + + "github.com/rancherfederal/hauler/pkg/artifact" +) + +type file struct{} + +func (f file) Name(u *url.URL) string { + return filepath.Base(f.path(u)) +} + +func (f file) Open(ctx context.Context, u *url.URL) (io.ReadCloser, error) { + return os.Open(f.path(u)) +} + +func (f file) Detect(u *url.URL) bool { + if len(f.path(u)) == 0 { + return false + } + + if fi, err := os.Stat(f.path(u)); err != nil { + return false + } else if fi.IsDir() { + return false + } + return true +} + +func (f file) path(u *url.URL) string { + return filepath.Join(u.Host, u.Path) +} + +func (f file) Config(u *url.URL) artifact.Config { + c := &config{ + Reference: u.String(), + } + return artifact.ToConfig(c) +} diff --git a/internal/getter/getter.go b/internal/getter/getter.go new file mode 100644 index 0000000..d582c30 --- /dev/null +++ b/internal/getter/getter.go @@ -0,0 +1,121 @@ +package getter + +import ( + "context" + "io" + "net/url" + + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/types" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" + "oras.land/oras-go/pkg/content" + + "github.com/rancherfederal/hauler/internal/layer" + "github.com/rancherfederal/hauler/pkg/artifact" + htypes "github.com/rancherfederal/hauler/pkg/consts" +) + +type Client struct { + Getters map[string]Getter + Options ClientOptions +} + +// TODO: Make some valid ClientOptions +type ClientOptions struct{} + +var ( + ErrGetterTypeUnknown = errors.New("no getter type found matching reference") +) + +type Getter interface { + Open(context.Context, *url.URL) (io.ReadCloser, error) + + Detect(*url.URL) bool + + Name(*url.URL) string + + Config(*url.URL) artifact.Config +} + +func NewClient(opts ClientOptions) *Client { + defaults := map[string]Getter{ + "file": new(file), + "directory": new(directory), + "http": new(https), + "https": new(https), + } + + c := &Client{ + Getters: defaults, + Options: opts, + } + return c +} + +func (c *Client) LayerFrom(ctx context.Context, source string) (v1.Layer, error) { + u, err := url.Parse(source) + if err != nil { + return nil, err + } + for _, g := range c.Getters { + if g.Detect(u) { + opener := func() (io.ReadCloser, error) { + return g.Open(ctx, u) + } + cfg := g.Config(u) + mt, err := cfg.MediaType() + if err != nil { + return nil, err + } + + annotations := make(map[string]string) + annotations[ocispec.AnnotationTitle] = g.Name(u) + + switch g.(type) { + case *directory: + annotations[content.AnnotationUnpack] = "true" + } + + l, err := layer.FromOpener(opener, + layer.WithMediaType(string(mt)), + layer.WithAnnotations(annotations)) + if err != nil { + return nil, err + } + return l, nil + } + } + return nil, errors.Wrapf(ErrGetterTypeUnknown, "%s", source) +} + +func (c *Client) Name(source string) string { + u, err := url.Parse(source) + if err != nil { + return source + } + for _, g := range c.Getters { + return g.Name(u) + } + return source +} + +func (c *Client) Config(source string) artifact.Config { + u, err := url.Parse(source) + if err != nil { + return nil + } + for _, g := range c.Getters { + return g.Config(u) + } + return nil +} + +type config struct { + Reference string `json:"reference"` + Annotations map[string]string `json:"annotations,omitempty"` +} + +func (c *config) MediaType() (types.MediaType, error) { + return htypes.FileConfigMediaType, nil +} diff --git a/internal/getter/https.go b/internal/getter/https.go new file mode 100644 index 0000000..82627bc --- /dev/null +++ b/internal/getter/https.go @@ -0,0 +1,58 @@ +package getter + +import ( + "context" + "io" + "mime" + "net/http" + "net/url" + "path/filepath" + "strings" + + "github.com/rancherfederal/hauler/pkg/artifact" +) + +type https struct{} + +func (h https) Name(u *url.URL) string { + resp, err := http.Head(u.String()) + if err != nil { + return "" + } + + contentType := resp.Header.Get("Content-Type") + for _, v := range strings.Split(contentType, ",") { + t, _, err := mime.ParseMediaType(v) + if err != nil { + break + } + // TODO: Identify known mimetypes for hints at a filename + _ = t + } + + // TODO: Not this + return filepath.Base(u.String()) +} + +func (h https) Open(ctx context.Context, u *url.URL) (io.ReadCloser, error) { + resp, err := http.Get(u.String()) + if err != nil { + return nil, err + } + return resp.Body, nil +} + +func (h https) Detect(u *url.URL) bool { + switch u.Scheme { + case "http", "https": + return true + } + return false +} + +func (h https) Config(u *url.URL) artifact.Config { + c := &config{ + Reference: u.String(), + } + return artifact.ToConfig(c) +} diff --git a/pkg/artifact/local/layer.go b/internal/layer/layer.go similarity index 85% rename from pkg/artifact/local/layer.go rename to internal/layer/layer.go index e25f24d..fdfbf6a 100644 --- a/pkg/artifact/local/layer.go +++ b/internal/layer/layer.go @@ -1,4 +1,4 @@ -package local +package layer import ( "io" @@ -6,16 +6,16 @@ import ( v1 "github.com/google/go-containerregistry/pkg/v1" gtypes "github.com/google/go-containerregistry/pkg/v1/types" - "github.com/rancherfederal/hauler/pkg/artifact/types" + "github.com/rancherfederal/hauler/pkg/consts" ) type Opener func() (io.ReadCloser, error) -func LayerFromOpener(opener Opener, opts ...LayerOption) (v1.Layer, error) { +func FromOpener(opener Opener, opts ...Option) (v1.Layer, error) { var err error layer := &layer{ - mediaType: types.UnknownLayer, + mediaType: consts.UnknownLayer, annotations: make(map[string]string, 1), } @@ -25,7 +25,7 @@ func LayerFromOpener(opener Opener, opts ...LayerOption) (v1.Layer, error) { if err != nil { return nil, err } - // TODO: actually compress this + return rc, nil } @@ -53,15 +53,15 @@ func compute(opener Opener) (v1.Hash, int64, error) { return v1.SHA256(rc) } -type LayerOption func(*layer) +type Option func(*layer) -func WithMediaType(mt string) LayerOption { +func WithMediaType(mt string) Option { return func(l *layer) { l.mediaType = mt } } -func WithAnnotations(annotations map[string]string) LayerOption { +func WithAnnotations(annotations map[string]string) Option { return func(l *layer) { if l.annotations == nil { l.annotations = make(map[string]string) diff --git a/internal/mapper/mapper.go b/internal/mapper/mapper.go new file mode 100644 index 0000000..bfeff2e --- /dev/null +++ b/internal/mapper/mapper.go @@ -0,0 +1,59 @@ +package mapper + +import ( + "fmt" + "os" + + "github.com/opencontainers/image-spec/specs-go/v1" + + "github.com/rancherfederal/hauler/pkg/consts" +) + +type Fn func(desc v1.Descriptor) (*os.File, error) + +type maps struct { +} + +func NewMapper() *maps { + return &maps{} +} + +type Mapper interface{} + +type image struct{} + +func (i *image) mapper() map[string]Fn { + m := make(map[string]Fn) + + manifestMapperFn := Fn(func(desc v1.Descriptor) (*os.File, error) { + return os.Create("manifest.json") + }) + + for _, l := range []string{consts.DockerManifestSchema2, consts.OCIManifestSchema1} { + m[l] = manifestMapperFn + } + + layerMapperFn := Fn(func(desc v1.Descriptor) (*os.File, error) { + n := fmt.Sprintf("%s.tar.gz", desc.Digest.String()) + return os.Create(n) + }) + + for _, l := range []string{consts.OCILayer, consts.DockerLayer} { + m[l] = layerMapperFn + } + + configMapperFn := Fn(func(desc v1.Descriptor) (*os.File, error) { + return os.Create("config.json") + }) + + for _, l := range []string{consts.DockerConfigJSON} { + m[l] = configMapperFn + } + + return m + +} + +func (i *image) identify() bool { + return false +} diff --git a/internal/mapper/store.go b/internal/mapper/store.go new file mode 100644 index 0000000..cf7c517 --- /dev/null +++ b/internal/mapper/store.go @@ -0,0 +1,187 @@ +package mapper + +import ( + "context" + "io" + "io/ioutil" + "os" + "strings" + "time" + + ccontent "github.com/containerd/containerd/content" + "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/remotes" + "github.com/opencontainers/go-digest" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" + "oras.land/oras-go/pkg/content" +) + +func NewStore(root string, mapper map[string]Fn) *store { + fs := content.NewFile(root) + return &store{ + File: fs, + mapper: mapper, + } +} + +func (s *store) Pusher(ctx context.Context, ref string) (remotes.Pusher, error) { + var tag, hash string + parts := strings.SplitN(ref, "@", 2) + if len(parts) > 0 { + tag = parts[0] + } + if len(parts) > 1 { + hash = parts[1] + } + return &pusher{ + tag: tag, + ref: hash, + mapper: s.mapper, + }, nil +} + +type store struct { + *content.File + mapper map[string]Fn +} + +type pusher struct { + tag string + ref string + mapper map[string]Fn +} + +func (s *pusher) Push(ctx context.Context, desc ocispec.Descriptor) (ccontent.Writer, error) { + now := time.Now() + + if _, ok := s.mapper[desc.MediaType]; !ok { + return content.NewIoContentWriter(ioutil.Discard, content.WithOutputHash(desc.Digest)), nil + } + + f, err := s.mapper[desc.MediaType](desc) + if err != nil { + return nil, err + } + + return &fileWriter{ + file: f, + digester: digest.Canonical.Digester(), + status: ccontent.Status{ + Ref: f.Name(), + Total: desc.Size, + StartedAt: now, + UpdatedAt: now, + }, + aftercommit: nil, + }, nil +} + +type fileWriter struct { + // store *content.File + file *os.File + digester digest.Digester + status ccontent.Status + aftercommit func() error +} + +// NewFileWriter will open a new file for writing, existing file will be truncated +func NewFileWriter(path string, perm os.FileMode, size int64) (*fileWriter, error) { + now := time.Now() + + f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, perm) + if err != nil { + return nil, err + } + + return &fileWriter{ + file: f, + digester: digest.Canonical.Digester(), + status: ccontent.Status{ + Ref: f.Name(), + Total: size, + StartedAt: now, + UpdatedAt: now, + }, + }, nil +} + +func (w *fileWriter) Write(p []byte) (n int, err error) { + n, err = w.file.Write(p) + w.digester.Hash().Write(p[:n]) + w.status.Offset += int64(len(p)) + w.status.UpdatedAt = time.Now() + return n, err +} + +func (w *fileWriter) Close() error { + if w.file == nil { + return nil + } + w.file.Sync() + err := w.file.Close() + w.file = nil + return err +} + +func (w *fileWriter) Digest() digest.Digest { + return w.digester.Digest() +} + +func (w *fileWriter) Commit(ctx context.Context, size int64, expected digest.Digest, opts ...ccontent.Opt) error { + var base ccontent.Info + for _, opt := range opts { + if err := opt(&base); err != nil { + return err + } + } + + if w.file == nil { + return errors.Wrap(errdefs.ErrFailedPrecondition, "cannot commit on closed writer") + } + file := w.file + w.file = nil + + if err := file.Sync(); err != nil { + file.Close() + return errors.Wrap(err, "sync failed") + } + + fileInfo, err := file.Stat() + if err != nil { + file.Close() + return errors.Wrap(err, "stat failed") + } + if err := file.Close(); err != nil { + return errors.Wrap(err, "failed to close file") + } + + if size > 0 && size != fileInfo.Size() { + return errors.Wrapf(errdefs.ErrFailedPrecondition, "unexpected commit size %d, expected %d", fileInfo.Size(), size) + } + if dgst := w.digester.Digest(); expected != "" && expected != dgst { + return errors.Wrapf(errdefs.ErrFailedPrecondition, "unexpected commit digest %s, expected %s", dgst, expected) + } + + // w.store.set(w.desc) + if w.aftercommit != nil { + return w.aftercommit() + } + return nil +} + +func (w *fileWriter) Status() (ccontent.Status, error) { + return w.status, nil +} + +func (w *fileWriter) Truncate(size int64) error { + if size != 0 { + return content.ErrUnsupportedSize + } + w.status.Offset = 0 + w.digester.Hash().Reset() + if _, err := w.file.Seek(0, io.SeekStart); err != nil { + return err + } + return w.file.Truncate(0) +} diff --git a/pkg/version/version.go b/internal/version/version.go similarity index 100% rename from pkg/version/version.go rename to internal/version/version.go diff --git a/pkg/apis/hauler.cattle.io/v1alpha1/chart.go b/pkg/apis/hauler.cattle.io/v1alpha1/chart.go index a6703ab..9ed897d 100644 --- a/pkg/apis/hauler.cattle.io/v1alpha1/chart.go +++ b/pkg/apis/hauler.cattle.io/v1alpha1/chart.go @@ -30,15 +30,20 @@ type ThickCharts struct { *metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec ChartSpec `json:"spec,omitempty"` + Spec ThickChartSpec `json:"spec,omitempty"` } type ThickChartSpec struct { - ThickCharts []ThickChart `json:"charts,omitempty"` + Charts []ThickChart `json:"charts,omitempty"` } type ThickChart struct { - Name string `json:"name"` - RepoURL string `json:"repoURL"` - Version string `json:"version"` + Name string `json:"name"` + RepoURL string `json:"repoURL"` + Version string `json:"version"` + ExtraImages []ChartImage `json:"extraImages"` +} + +type ChartImage struct { + Reference string `json:"ref"` } diff --git a/pkg/apis/hauler.cattle.io/v1alpha1/file.go b/pkg/apis/hauler.cattle.io/v1alpha1/file.go index f788a7f..9856da9 100644 --- a/pkg/apis/hauler.cattle.io/v1alpha1/file.go +++ b/pkg/apis/hauler.cattle.io/v1alpha1/file.go @@ -18,6 +18,5 @@ type FileSpec struct { } type File struct { - Ref string `json:"ref"` - Name string `json:"name,omitempty"` + Ref string `json:"ref"` } diff --git a/pkg/apis/hauler.cattle.io/v1alpha1/groupversion_info.go b/pkg/apis/hauler.cattle.io/v1alpha1/groupversion_info.go index 740b6cd..95f5132 100644 --- a/pkg/apis/hauler.cattle.io/v1alpha1/groupversion_info.go +++ b/pkg/apis/hauler.cattle.io/v1alpha1/groupversion_info.go @@ -2,7 +2,6 @@ package v1alpha1 import ( "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/controller-runtime/pkg/scheme" ) const ( @@ -13,7 +12,7 @@ const ( var ( ContentGroupVersion = schema.GroupVersion{Group: ContentGroup, Version: Version} - SchemeBuilder = &scheme.Builder{GroupVersion: ContentGroupVersion} + // SchemeBuilder = &scheme.Builder{GroupVersion: ContentGroupVersion} CollectionGroupVersion = schema.GroupVersion{Group: CollectionGroup, Version: Version} ) diff --git a/pkg/artifact/config.go b/pkg/artifact/config.go index 7b5155b..87ed1c5 100644 --- a/pkg/artifact/config.go +++ b/pkg/artifact/config.go @@ -1,10 +1,75 @@ package artifact -import v1 "github.com/google/go-containerregistry/pkg/v1" +import ( + "bytes" + + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/partial" + "github.com/google/go-containerregistry/pkg/v1/types" + "k8s.io/apimachinery/pkg/util/json" +) + +var _ partial.Describable = (*marshallableConfig)(nil) type Config interface { // Raw returns the config bytes Raw() ([]byte, error) - Descriptor() (v1.Descriptor, error) + Digest() (v1.Hash, error) + + MediaType() (types.MediaType, error) + + Size() (int64, error) +} + +type Marshallable interface { + MediaType() (types.MediaType, error) +} + +// ToConfig takes anything that is marshallabe and converts it into a Config +func ToConfig(i Marshallable) Config { + return &marshallableConfig{ + Marshallable: i, + } +} + +// marshallableConfig implements Config using helper methods +type marshallableConfig struct { + Marshallable + + hash v1.Hash + size int64 +} + +func (c *marshallableConfig) Raw() ([]byte, error) { + return json.Marshal(c.Marshallable) +} + +func (c *marshallableConfig) Digest() (v1.Hash, error) { + return Digest(c) +} + +func (c *marshallableConfig) Size() (int64, error) { + return Size(c) +} + +type WithRawConfig interface { + Raw() ([]byte, error) +} + +func Digest(c WithRawConfig) (v1.Hash, error) { + b, err := c.Raw() + if err != nil { + return v1.Hash{}, err + } + digest, _, err := v1.SHA256(bytes.NewReader(b)) + return digest, err +} + +func Size(c WithRawConfig) (int64, error) { + b, err := c.Raw() + if err != nil { + return -1, err + } + return int64(len(b)), nil } diff --git a/pkg/collection/chart/chart.go b/pkg/collection/chart/chart.go index 9ee9c6b..f82ff41 100644 --- a/pkg/collection/chart/chart.go +++ b/pkg/collection/chart/chart.go @@ -3,6 +3,7 @@ package chart import ( gname "github.com/google/go-containerregistry/pkg/name" + "github.com/rancherfederal/hauler/pkg/apis/hauler.cattle.io/v1alpha1" "github.com/rancherfederal/hauler/pkg/artifact" "github.com/rancherfederal/hauler/pkg/content/chart" "github.com/rancherfederal/hauler/pkg/content/image" @@ -12,26 +13,22 @@ var _ artifact.Collection = (*tchart)(nil) // tchart is a thick chart that includes all the dependent images as well as the chart itself type tchart struct { - name string - repo string - version string - chart *chart.Chart + chart *chart.Chart + config v1alpha1.ThickChart computed bool contents map[gname.Reference]artifact.OCI } -func NewChart(name, repo, version string) (artifact.Collection, error) { - o, err := chart.NewChart(name, repo, version) +func NewChart(cfg v1alpha1.ThickChart) (artifact.Collection, error) { + o, err := chart.NewChart(cfg.Name, cfg.RepoURL, cfg.Version) if err != nil { return nil, err } return &tchart{ - name: name, - repo: repo, - version: version, chart: o, + config: cfg, contents: make(map[gname.Reference]artifact.OCI), }, nil } @@ -51,27 +48,29 @@ func (c *tchart) compute() error { if err := c.dependentImages(); err != nil { return err } - if err := c.chartContents(); err != nil { return err } + if err := c.extraImages(); err != nil { + return err + } c.computed = true return nil } func (c *tchart) chartContents() error { - oci, err := chart.NewChart(c.name, c.repo, c.version) + oci, err := chart.NewChart(c.config.Name, c.config.RepoURL, c.config.Version) if err != nil { return err } - tag := c.version + tag := c.config.Version if tag == "" { tag = gname.DefaultTag } - ref, err := gname.ParseReference(c.name, gname.WithDefaultRegistry(""), gname.WithDefaultTag(tag)) + ref, err := gname.ParseReference(c.config.Name, gname.WithDefaultRegistry(""), gname.WithDefaultTag(tag)) if err != nil { return err } @@ -103,6 +102,21 @@ func (c *tchart) dependentImages() error { } c.contents[ref] = i } - + return nil +} + +func (c *tchart) extraImages() error { + for _, img := range c.config.ExtraImages { + ref, err := gname.ParseReference(img.Reference) + if err != nil { + return err + } + + i, err := image.NewImage(img.Reference) + if err != nil { + return err + } + c.contents[ref] = i + } return nil } diff --git a/pkg/collection/k3s/k3s.go b/pkg/collection/k3s/k3s.go index 873d821..26e5df9 100644 --- a/pkg/collection/k3s/k3s.go +++ b/pkg/collection/k3s/k3s.go @@ -12,6 +12,7 @@ import ( "github.com/google/go-containerregistry/pkg/name" + "github.com/rancherfederal/hauler/internal/getter" "github.com/rancherfederal/hauler/pkg/artifact" "github.com/rancherfederal/hauler/pkg/content/file" "github.com/rancherfederal/hauler/pkg/content/image" @@ -39,6 +40,7 @@ type k3s struct { computed bool contents map[name.Reference]artifact.OCI channels map[string]string + client *getter.Client } func NewK3s(version string) (artifact.Collection, error) { @@ -94,12 +96,9 @@ func (k *k3s) executable() error { return ErrExecutableNotfound } - f, err := file.NewFile(fref, "k3s") - if err != nil { - return err - } + f := file.NewFile(fref) - ref, err := name.ParseReference("hauler/k3s", name.WithDefaultTag(k.dnsCompliantVersion()), name.WithDefaultRegistry("")) + ref, err := name.ParseReference("k3s", name.WithDefaultTag(k.dnsCompliantVersion()), name.WithDefaultRegistry("")) if err != nil { return err } @@ -109,12 +108,10 @@ func (k *k3s) executable() error { } func (k *k3s) bootstrap() error { - f, err := file.NewFile(bootstrapUrl, "get-k3s.io") - if err != nil { - return err - } + namedBootstrapUrl := fmt.Sprintf("%s?filename=%s", bootstrapUrl, "k3s-init.sh") + f := file.NewFile(namedBootstrapUrl) - ref, err := name.ParseReference("hauler/get-k3s.io", name.WithDefaultRegistry(""), name.WithDefaultTag("latest")) + ref, err := name.ParseReference("k3s-init.sh", name.WithDefaultRegistry(""), name.WithDefaultTag("latest")) if err != nil { return err } diff --git a/pkg/collection/k3s/k3s_test.go b/pkg/collection/k3s/k3s_test.go index 194f882..4e70d57 100644 --- a/pkg/collection/k3s/k3s_test.go +++ b/pkg/collection/k3s/k3s_test.go @@ -22,9 +22,10 @@ func TestNewK3s(t *testing.T) { } defer os.Remove(tmpdir) - s := store.NewStore(ctx, tmpdir) - s.Open() - defer s.Close() + s, err := store.NewStore(tmpdir) + if err != nil { + t.Error(err) + } type args struct { version string diff --git a/pkg/artifact/types/types.go b/pkg/consts/types.go similarity index 78% rename from pkg/artifact/types/types.go rename to pkg/consts/types.go index c1e50fa..3cb8ce2 100644 --- a/pkg/artifact/types/types.go +++ b/pkg/consts/types.go @@ -1,10 +1,14 @@ -package types +package consts const ( OCIManifestSchema1 = "application/vnd.oci.image.manifest.v1+json" DockerManifestSchema2 = "application/vnd.docker.distribution.manifest.v2+json" - DockerConfigJSON = "application/vnd.docker.container.image.v1+json" + DockerConfigJSON = "application/vnd.docker.container.image.v1+json" + DockerLayer = "application/vnd.docker.image.rootfs.diff.tar.gzip" + DockerForeignLayer = "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip" + DockerUncompressedLayer = "application/vnd.docker.image.rootfs.diff.tar" + OCILayer = "application/vnd.oci.image.layer.v1.tar+gzip" // ChartConfigMediaType is the reserved media type for the Helm chart manifest config ChartConfigMediaType = "application/vnd.cncf.helm.config.v1+json" diff --git a/pkg/content/chart/chart.go b/pkg/content/chart/chart.go index eff4cc4..42906c4 100644 --- a/pkg/content/chart/chart.go +++ b/pkg/content/chart/chart.go @@ -16,9 +16,10 @@ import ( "helm.sh/helm/v3/pkg/chart/loader" "helm.sh/helm/v3/pkg/cli" + "github.com/rancherfederal/hauler/internal/layer" + "github.com/rancherfederal/hauler/internal/mapper" "github.com/rancherfederal/hauler/pkg/artifact" - "github.com/rancherfederal/hauler/pkg/artifact/local" - "github.com/rancherfederal/hauler/pkg/artifact/types" + "github.com/rancherfederal/hauler/pkg/consts" ) var _ artifact.OCI = (*Chart)(nil) @@ -46,7 +47,7 @@ func NewChart(name, repo, version string) (*Chart, error) { } func (h *Chart) MediaType() string { - return types.OCIManifestSchema1 + return consts.OCIManifestSchema1 } func (h *Chart) Manifest() (*gv1.Manifest, error) { @@ -94,7 +95,7 @@ func (h *Chart) configDescriptor() (gv1.Descriptor, error) { } return gv1.Descriptor{ - MediaType: types.ChartConfigMediaType, + MediaType: consts.ChartConfigMediaType, Size: size, Digest: hash, }, nil @@ -129,13 +130,30 @@ func (h *Chart) chartDataLayer() (gv1.Layer, error) { annotations := make(map[string]string) annotations[ocispec.AnnotationTitle] = filepath.Base(h.path) - return local.LayerFromOpener(chartOpener(h.path), - local.WithMediaType(types.ChartLayerMediaType), - local.WithAnnotations(annotations)) + return layer.FromOpener(chartOpener(h.path), + layer.WithMediaType(consts.ChartLayerMediaType), + layer.WithAnnotations(annotations)) } -func chartOpener(path string) local.Opener { +func chartOpener(path string) layer.Opener { return func() (io.ReadCloser, error) { return os.Open(path) } } + +func Mapper() map[string]mapper.Fn { + m := make(map[string]mapper.Fn) + + chartMapperFn := mapper.Fn(func(desc ocispec.Descriptor) (*os.File, error) { + // TODO: Fix this + return os.Create("chart.tar.gz") + }) + + provMapperFn := mapper.Fn(func(desc ocispec.Descriptor) (*os.File, error) { + return os.Create("prov.json") + }) + + m[consts.ChartLayerMediaType] = chartMapperFn + m[consts.ProvLayerMediaType] = provMapperFn + return m +} diff --git a/pkg/content/chart/chart_test.go b/pkg/content/chart/chart_test.go index 9696ebd..5ab6b39 100644 --- a/pkg/content/chart/chart_test.go +++ b/pkg/content/chart/chart_test.go @@ -25,9 +25,10 @@ func TestChart_Copy(t *testing.T) { } defer os.Remove(tmpdir) - s := store.NewStore(ctx, tmpdir) - s.Open() - defer s.Close() + s, err := store.NewStore(tmpdir) + if err != nil { + t.Error(err) + } type args struct { ctx context.Context @@ -48,7 +49,7 @@ func TestChart_Copy(t *testing.T) { }, args: args{ ctx: ctx, - registry: s.Registry(), + registry: "", }, wantErr: false, }, diff --git a/pkg/content/file/config.go b/pkg/content/file/config.go deleted file mode 100644 index 5511ea9..0000000 --- a/pkg/content/file/config.go +++ /dev/null @@ -1,82 +0,0 @@ -package file - -import ( - "bytes" - "encoding/json" - - 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" - - "github.com/rancherfederal/hauler/pkg/artifact/types" -) - -var _ partial.Describable = (*config)(nil) - -type config struct { - Reference string `json:"ref"` // Reference is the reference from where the file was sourced - Name string `json:"name"` // Name is the files name on disk - Annotations map[string]string `json:"annotations,omitempty"` - URLs []string `json:"urls,omitempty"` - - computed bool - size int64 - hash gv1.Hash -} - -func (c config) Descriptor() (gv1.Descriptor, error) { - if err := c.compute(); err != nil { - return gv1.Descriptor{}, err - } - - return gv1.Descriptor{ - MediaType: types.FileConfigMediaType, - Size: c.size, - Digest: c.hash, - URLs: c.URLs, - Annotations: c.Annotations, - // Platform: nil, - }, nil -} - -func (c config) Digest() (gv1.Hash, error) { - if err := c.compute(); err != nil { - return gv1.Hash{}, err - } - return c.hash, nil -} - -func (c config) MediaType() (gtypes.MediaType, error) { - return types.FileConfigMediaType, nil -} - -func (c config) Size() (int64, error) { - if err := c.compute(); err != nil { - return 0, err - } - return c.size, nil -} - -func (c *config) Raw() ([]byte, error) { - return json.Marshal(c) -} - -func (c *config) compute() error { - if c.computed { - return nil - } - - data, err := c.Raw() - if err != nil { - return err - } - - h, size, err := gv1.SHA256(bytes.NewBuffer(data)) - if err != nil { - return err - } - - c.size = size - c.hash = h - return nil -} diff --git a/pkg/content/file/file.go b/pkg/content/file/file.go index 70cd694..9e448b3 100644 --- a/pkg/content/file/file.go +++ b/pkg/content/file/file.go @@ -1,107 +1,122 @@ package file import ( - "io" - "net/http" + "context" "os" - "strings" 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" + "github.com/pkg/errors" + "github.com/rancherfederal/hauler/internal/getter" + "github.com/rancherfederal/hauler/internal/mapper" "github.com/rancherfederal/hauler/pkg/artifact" - "github.com/rancherfederal/hauler/pkg/artifact/local" - "github.com/rancherfederal/hauler/pkg/artifact/types" + "github.com/rancherfederal/hauler/pkg/consts" ) +// interface guard var _ artifact.OCI = (*file)(nil) type file struct { - blob gv1.Layer - config config - blobMap map[gv1.Hash]gv1.Layer - + ref string + client *getter.Client + computed bool + config artifact.Config + blob gv1.Layer + manifest *gv1.Manifest annotations map[string]string } -func NewFile(ref string, filename string) (*file, error) { - var getter local.Opener - if strings.HasPrefix(ref, "http") || strings.HasPrefix(ref, "https") { - getter = remoteOpener(ref) - } else { - getter = localOpener(ref) - } +func NewFile(ref string) *file { + // TODO: Allow user to configure this + client := getter.NewClient(getter.ClientOptions{}) - annotations := make(map[string]string) - annotations[ocispec.AnnotationTitle] = filename // For oras FileStore to recognize - annotations[ocispec.AnnotationSource] = ref - - blob, err := local.LayerFromOpener(getter, - local.WithMediaType(types.FileLayerMediaType), - local.WithAnnotations(annotations)) - if err != nil { - return nil, err + return &file{ + client: client, + ref: ref, } +} - f := &file{ - blob: blob, - config: config{ - Reference: ref, - Name: filename, - }, - } - return f, nil +func (f *file) Name(ref string) string { + return f.client.Name(ref) } func (f *file) MediaType() string { - return types.OCIManifestSchema1 + return consts.OCIManifestSchema1 } func (f *file) RawConfig() ([]byte, error) { + if err := f.compute(); err != nil { + return nil, err + } return f.config.Raw() } func (f *file) Layers() ([]gv1.Layer, error) { + if err := f.compute(); err != nil { + return nil, err + } var layers []gv1.Layer layers = append(layers, f.blob) return layers, nil } func (f *file) Manifest() (*gv1.Manifest, error) { - desc, err := partial.Descriptor(f.blob) - if err != nil { + if err := f.compute(); err != nil { return nil, err } - layerDescs := []gv1.Descriptor{*desc} + return f.manifest, nil +} - cfgDesc, err := f.config.Descriptor() - if err != nil { - return nil, err +func (f *file) compute() error { + if f.computed { + return nil } - return &gv1.Manifest{ + ctx := context.Background() + blob, err := f.client.LayerFrom(ctx, f.ref) + if err != nil { + return err + } + + layer, err := partial.Descriptor(blob) + if err != nil { + return err + } + + cfg := f.client.Config(f.ref) + cfgDesc, err := partial.Descriptor(cfg) + if err != nil { + return err + } + + m := &gv1.Manifest{ SchemaVersion: 2, MediaType: gtypes.MediaType(f.MediaType()), - Config: cfgDesc, - Layers: layerDescs, + Config: *cfgDesc, + Layers: []gv1.Descriptor{*layer}, Annotations: f.annotations, - }, nil -} - -func localOpener(path string) local.Opener { - return func() (io.ReadCloser, error) { - return os.Open(path) } + + f.manifest = m + f.config = cfg + f.blob = blob + f.computed = true + return nil } -func remoteOpener(url string) local.Opener { - return func() (io.ReadCloser, error) { - resp, err := http.Get(url) - if err != nil { - return nil, err +func Mapper() map[string]mapper.Fn { + m := make(map[string]mapper.Fn) + + blobMapperFn := mapper.Fn(func(desc ocispec.Descriptor) (*os.File, error) { + if _, ok := desc.Annotations[ocispec.AnnotationTitle]; !ok { + return nil, errors.Errorf("unkown file name") } - return resp.Body, nil - } + return os.Create(desc.Annotations[ocispec.AnnotationTitle]) + }) + + m[consts.FileLayerMediaType] = blobMapperFn + return m } diff --git a/pkg/content/file/file_test.go b/pkg/content/file/file_test.go index 87e00f0..647d7fa 100644 --- a/pkg/content/file/file_test.go +++ b/pkg/content/file/file_test.go @@ -1,188 +1,167 @@ package file_test -import ( - "context" - "fmt" - "net/http" - "net/http/httptest" - "os" - "path/filepath" - "reflect" - "testing" - - "github.com/google/go-containerregistry/pkg/name" - v1 "github.com/google/go-containerregistry/pkg/v1" - "github.com/opencontainers/go-digest" - ocispec "github.com/opencontainers/image-spec/specs-go/v1" - - "github.com/rancherfederal/hauler/pkg/apis/hauler.cattle.io/v1alpha1" - "github.com/rancherfederal/hauler/pkg/artifact/types" - "github.com/rancherfederal/hauler/pkg/content/file" - "github.com/rancherfederal/hauler/pkg/log" - "github.com/rancherfederal/hauler/pkg/store" -) - -func TestFile_Copy(t *testing.T) { - ctx := context.Background() - l := log.NewLogger(os.Stdout) - ctx = l.WithContext(ctx) - - tmpdir, err := os.MkdirTemp("", "hauler") - if err != nil { - t.Error(err) - } - defer os.Remove(tmpdir) - - // Make a temp file - f, err := os.CreateTemp(tmpdir, "tmp") - f.Write([]byte("content")) - defer f.Close() - - fs := newTestFileServer(tmpdir) - fs.Start() - defer fs.Stop() - - s := store.NewStore(ctx, tmpdir) - s.Open() - defer s.Close() - - type args struct { - ctx context.Context - registry string - } - - tests := []struct { - name string - cfg v1alpha1.File - args args - wantErr bool - }{ - { - name: "should copy a local file successfully without an explicit name", - cfg: v1alpha1.File{ - Ref: f.Name(), - Name: filepath.Base(f.Name()), - }, - args: args{ - ctx: ctx, - }, - }, - { - name: "should copy a local file successfully with an explicit name", - cfg: v1alpha1.File{ - Ref: f.Name(), - Name: "my-other-file", - }, - args: args{ - ctx: ctx, - }, - }, - { - name: "should fail to copy a local file successfully with a malformed explicit name", - cfg: v1alpha1.File{ - Ref: f.Name(), - Name: "my!invalid~@file", - }, - args: args{ - ctx: ctx, - }, - wantErr: true, - }, - { - name: "should copy a remote file successfully without an explicit name", - cfg: v1alpha1.File{ - Ref: fmt.Sprintf("%s/%s", fs.server.URL, filepath.Base(f.Name())), - }, - args: args{ - ctx: ctx, - }, - }, - { - name: "should copy a remote file successfully with an explicit name", - cfg: v1alpha1.File{ - Ref: fmt.Sprintf("%s/%s", fs.server.URL, filepath.Base(f.Name())), - Name: "my-other-file", - }, - args: args{ - ctx: ctx, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - f, err := file.NewFile(tt.cfg.Ref, tt.cfg.Name) - if err != nil { - t.Fatal(err) - } - - ref, err := name.ParseReference("myfile") - if err != nil { - t.Fatal(err) - } - - _, err = s.AddArtifact(ctx, f, ref) - if (err != nil) != tt.wantErr { - t.Error(err) - } - - // if err := validate(tt.cfg.Ref, tt.cfg.Name, m); err != nil { - // t.Error(err) - // } - }) - } -} - -type testFileServer struct { - server *httptest.Server -} - -func newTestFileServer(path string) *testFileServer { - s := httptest.NewUnstartedServer(http.FileServer(http.Dir(path))) - return &testFileServer{server: s} -} - -func (s *testFileServer) Start() *httptest.Server { - s.server.Start() - return s.server -} - -func (s *testFileServer) Stop() { - s.server.Close() -} - -// validate ensure -func validate(ref string, name string, got *v1.Manifest) error { - data, err := os.ReadFile(ref) - if err != nil { - return err - } - - d := digest.FromBytes(data) - - annotations := make(map[string]string) - annotations[ocispec.AnnotationTitle] = name - annotations[ocispec.AnnotationSource] = ref - - want := &v1.Manifest{ - SchemaVersion: 2, - MediaType: types.OCIManifestSchema1, - Config: v1.Descriptor{}, - Layers: []v1.Descriptor{ - { - MediaType: types.FileLayerMediaType, - Size: int64(len(data)), - Digest: v1.Hash{ - Algorithm: d.Algorithm().String(), - Hex: d.Hex(), - }, - Annotations: annotations, - }, - }, - Annotations: nil, - } - - if !reflect.DeepEqual(want.Layers, got.Layers) { - return fmt.Errorf("want = (%v) | got = (%v)", want, got) - } - return nil -} +// func TestFile_Copy(t *testing.T) { +// ctx := context.Background() +// l := log.NewLogger(os.Stdout) +// ctx = l.WithContext(ctx) +// +// tmpdir, err := os.MkdirTemp("", "hauler") +// if err != nil { +// t.Error(err) +// } +// defer os.Remove(tmpdir) +// +// // Make a temp file +// f, err := os.CreateTemp(tmpdir, "tmp") +// f.Write([]byte("content")) +// defer f.Close() +// +// fs := newTestFileServer(tmpdir) +// fs.Start() +// defer fs.Stop() +// +// s, err := store.NewBundle(tmpdir) +// if err != nil { +// t.Error(err) +// } +// +// type args struct { +// ctx context.Context +// registry string +// } +// +// tests := []struct { +// name string +// cfg v1alpha1.File +// args args +// wantErr bool +// }{ +// { +// name: "should copy a local file successfully without an explicit name", +// cfg: v1alpha1.File{ +// Ref: f.Name(), +// Name: filepath.Base(f.Name()), +// }, +// args: args{ +// ctx: ctx, +// }, +// }, +// { +// name: "should copy a local file successfully with an explicit name", +// cfg: v1alpha1.File{ +// Ref: f.Name(), +// Name: "my-other-file", +// }, +// args: args{ +// ctx: ctx, +// }, +// }, +// { +// name: "should fail to copy a local file successfully with a malformed explicit name", +// cfg: v1alpha1.File{ +// Ref: f.Name(), +// Name: "my!invalid~@file", +// }, +// args: args{ +// ctx: ctx, +// }, +// wantErr: true, +// }, +// { +// name: "should copy a remote file successfully without an explicit name", +// cfg: v1alpha1.File{ +// Ref: fmt.Sprintf("%s/%s", fs.server.URL, filepath.Base(f.Name())), +// }, +// args: args{ +// ctx: ctx, +// }, +// }, +// { +// name: "should copy a remote file successfully with an explicit name", +// cfg: v1alpha1.File{ +// Ref: fmt.Sprintf("%s/%s", fs.server.URL, filepath.Base(f.Name())), +// Name: "my-other-file", +// }, +// args: args{ +// ctx: ctx, +// }, +// }, +// } +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// f, err := file.NewFile(tt.cfg.Ref, tt.cfg.Name) +// if err != nil { +// t.Fatal(err) +// } +// +// ref, err := name.ParseReference("myfile") +// if err != nil { +// t.Fatal(err) +// } +// +// _, err = s.AddArtifact(ctx, f, ref) +// if (err != nil) != tt.wantErr { +// t.Error(err) +// } +// +// // if err := validate(tt.cfg.Ref, tt.cfg.Name, m); err != nil { +// // t.Error(err) +// // } +// }) +// } +// } +// +// type testFileServer struct { +// server *httptest.Server +// } +// +// func newTestFileServer(path string) *testFileServer { +// s := httptest.NewUnstartedServer(http.FileServer(http.Dir(path))) +// return &testFileServer{server: s} +// } +// +// func (s *testFileServer) Start() *httptest.Server { +// s.server.Start() +// return s.server +// } +// +// func (s *testFileServer) Stop() { +// s.server.Close() +// } +// +// // validate ensure +// func validate(ref string, name string, got *v1.Manifest) error { +// data, err := os.ReadFile(ref) +// if err != nil { +// return err +// } +// +// d := digest.FromBytes(data) +// +// annotations := make(map[string]string) +// annotations[ocispec.AnnotationTitle] = name +// annotations[ocispec.AnnotationSource] = ref +// +// want := &v1.Manifest{ +// SchemaVersion: 2, +// MediaType: types.OCIManifestSchema1, +// Config: v1.Descriptor{}, +// Layers: []v1.Descriptor{ +// { +// MediaType: types.FileLayerMediaType, +// Size: int64(len(data)), +// Digest: v1.Hash{ +// Algorithm: d.Algorithm().String(), +// Hex: d.Hex(), +// }, +// Annotations: annotations, +// }, +// }, +// Annotations: nil, +// } +// +// if !reflect.DeepEqual(want.Layers, got.Layers) { +// return fmt.Errorf("want = (%v) | got = (%v)", want, got) +// } +// return nil +// } diff --git a/pkg/content/file/reference.go b/pkg/content/file/reference.go new file mode 100644 index 0000000..9d0a68a --- /dev/null +++ b/pkg/content/file/reference.go @@ -0,0 +1,114 @@ +package file + +import ( + "errors" + "io" + "net/http" + "net/url" + "os" + "path/filepath" + + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + + "github.com/rancherfederal/hauler/internal/layer" +) + +// Locatable represents content that can be referenced by a +type Locatable interface { + Locate() string + + Name() string + + Annotate() map[string]string + + Open() layer.Opener +} + +var ( + _ Locatable = (*localFile)(nil) + _ Locatable = (*remoteFile)(nil) +) + +func NewLocatableLocalFile(path string) (*localFile, error) { + if _, err := os.Stat(path); err != nil { + return nil, err + } + abs, err := filepath.Abs(path) + if err != nil { + return nil, err + } + return &localFile{ + path: abs, + }, nil +} + +type localFile struct { + path string +} + +func (l localFile) Locate() string { + return l.path +} + +func (l localFile) Name() string { + return filepath.Base(l.path) +} + +func (l localFile) Annotate() map[string]string { + annotations := make(map[string]string) + annotations[ocispec.AnnotationTitle] = l.Name() // For oras FileStore to recognize + annotations[ocispec.AnnotationSource] = l.Locate() + return annotations +} + +func (l localFile) Open() layer.Opener { + return func() (io.ReadCloser, error) { + return os.Open(l.Locate()) + } +} + +func NewLocatableRemoteFile(ref string) (*remoteFile, error) { + u, err := url.Parse(ref) + if err != nil { + return nil, err + } + if u.Scheme != "https" && u.Scheme != "http" { + return nil, errors.New("not a valid remote file protocol (http or https)") + } + return &remoteFile{ + ref: ref, + }, err +} + +type remoteFile struct { + ref string + name string +} + +func (r remoteFile) Locate() string { + return r.ref +} + +func (r remoteFile) Name() string { + if r.name != "" { + return r.name + } + return filepath.Base(r.ref) +} + +func (r remoteFile) Annotate() map[string]string { + annotations := make(map[string]string) + annotations[ocispec.AnnotationTitle] = r.Name() + annotations[ocispec.AnnotationSource] = r.Locate() + return annotations +} + +func (r remoteFile) Open() layer.Opener { + return func() (io.ReadCloser, error) { + resp, err := http.Get(r.ref) + if err != nil { + return nil, err + } + return resp.Body, nil + } +} diff --git a/pkg/content/image/image.go b/pkg/content/image/image.go index 4ac8ab8..23782b4 100644 --- a/pkg/content/image/image.go +++ b/pkg/content/image/image.go @@ -1,11 +1,17 @@ package image import ( + "fmt" + "os" + "github.com/google/go-containerregistry/pkg/name" gv1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/remote" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/rancherfederal/hauler/internal/mapper" "github.com/rancherfederal/hauler/pkg/artifact" + "github.com/rancherfederal/hauler/pkg/consts" ) var _ artifact.OCI = (*image)(nil) @@ -41,3 +47,34 @@ func NewImage(ref string) (*image, error) { Image: img, }, nil } + +func Mapper() map[string]mapper.Fn { + m := make(map[string]mapper.Fn) + + manifestMapperFn := mapper.Fn(func(desc ocispec.Descriptor) (*os.File, error) { + return os.Create("manifest.json") + }) + + for _, l := range []string{consts.DockerManifestSchema2, consts.OCIManifestSchema1} { + m[l] = manifestMapperFn + } + + layerMapperFn := mapper.Fn(func(desc ocispec.Descriptor) (*os.File, error) { + n := fmt.Sprintf("%s.tar.gz", desc.Digest.String()) + return os.Create(n) + }) + + for _, l := range []string{consts.OCILayer, consts.DockerLayer} { + m[l] = layerMapperFn + } + + configMapperFn := mapper.Fn(func(desc ocispec.Descriptor) (*os.File, error) { + return os.Create("config.json") + }) + + for _, l := range []string{consts.DockerConfigJSON} { + m[l] = configMapperFn + } + + return m +} diff --git a/pkg/content/image/image_test.go b/pkg/content/image/image_test.go index 1c16763..d7ca3d6 100644 --- a/pkg/content/image/image_test.go +++ b/pkg/content/image/image_test.go @@ -26,9 +26,10 @@ func TestImage_Copy(t *testing.T) { } defer os.Remove(tmpdir) - s := store.NewStore(ctx, tmpdir) - s.Open() - defer s.Close() + s, err := store.NewStore(tmpdir) + if err != nil { + t.Error(err) + } type args struct { ctx context.Context diff --git a/pkg/layout/artifact.go b/pkg/layout/artifact.go deleted file mode 100644 index 1503ce5..0000000 --- a/pkg/layout/artifact.go +++ /dev/null @@ -1,146 +0,0 @@ -package layout - -import ( - "bytes" - "encoding/json" - "io" - "os" - "strings" - - "github.com/google/go-containerregistry/pkg/name" - gv1 "github.com/google/go-containerregistry/pkg/v1" - "github.com/google/go-containerregistry/pkg/v1/empty" - "github.com/google/go-containerregistry/pkg/v1/layout" - gtypes "github.com/google/go-containerregistry/pkg/v1/types" - "github.com/opencontainers/go-digest" - ocispec "github.com/opencontainers/image-spec/specs-go/v1" - "golang.org/x/sync/errgroup" - - "github.com/rancherfederal/hauler/pkg/artifact" -) - -// Path is a wrapper around layout.Path -type Path struct { - layout.Path -} - -// FromPath returns a new Path or creates one if one doesn't exist -func FromPath(path string) (Path, error) { - p, err := layout.FromPath(path) - if os.IsNotExist(err) { - p, err = layout.Write(path, empty.Index) - if err != nil { - return Path{}, err - } - } - return Path{Path: p}, err -} - -// WriteOci will write oci content (artifact.OCI) to the given Path -func (l Path) WriteOci(o artifact.OCI, reference name.Reference) (ocispec.Descriptor, error) { - layers, err := o.Layers() - if err != nil { - return ocispec.Descriptor{}, err - } - - // Write layers concurrently - var g errgroup.Group - for _, layer := range layers { - layer := layer - g.Go(func() error { - return l.writeLayer(layer) - }) - } - if err := g.Wait(); err != nil { - return ocispec.Descriptor{}, err - } - - // Write the config - cfgBlob, err := o.RawConfig() - if err != nil { - return ocispec.Descriptor{}, err - } - - if err = l.writeBlob(cfgBlob); err != nil { - return ocispec.Descriptor{}, err - } - - m, err := o.Manifest() - if err != nil { - return ocispec.Descriptor{}, err - } - - manifest, err := json.Marshal(m) - if err != nil { - return ocispec.Descriptor{}, err - } - - if err := l.writeBlob(manifest); err != nil { - return ocispec.Descriptor{}, err - } - - desc := ocispec.Descriptor{ - MediaType: o.MediaType(), - Size: int64(len(manifest)), - Digest: digest.FromBytes(manifest), - Annotations: map[string]string{ - ocispec.AnnotationRefName: reference.Name(), - ocispec.AnnotationTitle: deregistry(reference).Name(), - }, - } - - if err := l.appendDescriptor(desc); err != nil { - return ocispec.Descriptor{}, err - } - - return desc, nil -} - -// writeBlob differs from layer.WriteBlob in that it requires data instead -func (l Path) writeBlob(data []byte) error { - h, _, err := gv1.SHA256(bytes.NewReader(data)) - if err != nil { - return err - } - - return l.WriteBlob(h, io.NopCloser(bytes.NewReader(data))) -} - -// writeLayer is a verbatim reimplementation of layout.writeLayer -func (l Path) writeLayer(layer gv1.Layer) error { - d, err := layer.Digest() - if err != nil { - return err - } - - r, err := layer.Compressed() - if err != nil { - return err - } - - return l.WriteBlob(d, r) -} - -// appendDescriptor is a helper that translates a ocispec.Descriptor into a gv1.Descriptor -func (l Path) appendDescriptor(desc ocispec.Descriptor) error { - gdesc := gv1.Descriptor{ - MediaType: gtypes.MediaType(desc.MediaType), - Size: desc.Size, - Digest: gv1.Hash{ - Algorithm: desc.Digest.Algorithm().String(), - Hex: desc.Digest.Hex(), - }, - URLs: desc.URLs, - Annotations: desc.Annotations, - } - - return l.AppendDescriptor(gdesc) -} - -// deregistry removes the registry content from a name.Reference -func deregistry(ref name.Reference) name.Reference { - // No error checking b/c at this point we're already assumed to have a valid enough reference - dereg := strings.TrimLeft(strings.ReplaceAll(ref.Name(), ref.Context().RegistryStr(), ""), "/") - deref, _ := name.ParseReference(dereg, name.WithDefaultRegistry("")) - return deref -} diff --git a/pkg/layout/store.go b/pkg/layout/store.go deleted file mode 100644 index 1dd4892..0000000 --- a/pkg/layout/store.go +++ /dev/null @@ -1,191 +0,0 @@ -package layout - -import ( - "context" - "encoding/json" - "fmt" - "os" - "path/filepath" - - "github.com/containerd/containerd/content" - "github.com/containerd/containerd/content/local" - "github.com/containerd/containerd/remotes/docker" - "github.com/google/go-containerregistry/pkg/name" - "github.com/opencontainers/go-digest" - ocispec "github.com/opencontainers/image-spec/specs-go/v1" - orascontent "oras.land/oras-go/pkg/content" - "oras.land/oras-go/pkg/oras" - - "github.com/rancherfederal/hauler/pkg/artifact/types" -) - -// interface guards -var ( - _ content.Provider = (*OCIStore)(nil) - _ content.Ingester = (*OCIStore)(nil) -) - -// OCIStore represents a content compatible store adhering by the oci-layout spec -type OCIStore struct { - content.Store - - root string - index *ocispec.Index - digestMap map[string]ocispec.Descriptor -} - -// Copy placeholder until we migrate to oras 0.5 -// Will loop through each appropriately named index and copy the contents to the desired registry -func Copy(ctx context.Context, s *OCIStore, registry string) error { - for _, desc := range s.index.Manifests { - manifestBlobPath, err := s.blobPath(desc.Digest) - if err != nil { - return err - } - - manifestData, err := os.ReadFile(manifestBlobPath) - if err != nil { - return err - } - - m, mdesc, err := loadManifest(manifestData) - if err != nil { - return err - } - - refName, ok := desc.Annotations[ocispec.AnnotationRefName] - if !ok { - return fmt.Errorf("no name found to push image") - } - - rref, err := RelocateReference(refName, registry) - if err != nil { - return err - } - - resolver := docker.NewResolver(docker.ResolverOptions{}) - _, err = oras.Push(ctx, resolver, rref.Name(), s, m.Layers, - oras.WithConfig(m.Config), oras.WithNameValidation(nil), oras.WithManifest(mdesc)) - if err != nil { - return err - } - } - - return nil -} - -// NewOCIStore will return a new OCIStore given a path to an oci-layout compatible directory -func NewOCIStore(path string) (*OCIStore, error) { - fs, err := local.NewStore(path) - if err != nil { - return nil, err - } - - store := &OCIStore{ - Store: fs, - - root: path, - } - - if err := store.validateOCILayout(); err != nil { - return nil, err - } - if err := store.LoadIndex(); err != nil { - return nil, nil - } - - return store, nil -} - -// LoadIndex will load an oci-layout compatible directory -func (s *OCIStore) LoadIndex() error { - path := filepath.Join(s.root, types.OCIImageIndexFile) - indexFile, err := os.Open(path) - if err != nil { - // TODO: Don't just bomb out? - return err - } - defer indexFile.Close() - - if err := json.NewDecoder(indexFile).Decode(&s.index); err != nil { - return err - } - - s.digestMap = make(map[string]ocispec.Descriptor) - for _, desc := range s.index.Manifests { - if name := desc.Annotations[ocispec.AnnotationRefName]; name != "" { - s.digestMap[name] = desc - } - } - - return nil -} - -func (s *OCIStore) validateOCILayout() error { - layoutFilePath := filepath.Join(s.root, ocispec.ImageLayoutFile) - layoutFile, err := os.Open(layoutFilePath) - if err != nil { - return err - } - defer layoutFile.Close() - - var layout *ocispec.ImageLayout - if err := json.NewDecoder(layoutFile).Decode(&layout); err != nil { - return err - } - - if layout.Version != ocispec.ImageLayoutVersion { - return orascontent.ErrUnsupportedVersion - } - - return nil -} - -func (s *OCIStore) blobPath(d digest.Digest) (string, error) { - if err := d.Validate(); err != nil { - return "", err - } - - return filepath.Join(s.root, "blobs", d.Algorithm().String(), d.Hex()), nil -} - -// manifest is a field wrapper around ocispec.Manifest that contains the mediaType field -type manifest struct { - ocispec.Manifest `json:",inline"` - - MediaType string `json:"mediaType"` -} - -// loadManifest -func loadManifest(data []byte) (ocispec.Manifest, ocispec.Descriptor, error) { - var m manifest - if err := json.Unmarshal(data, &m); err != nil { - return ocispec.Manifest{}, ocispec.Descriptor{}, err - } - - desc := ocispec.Descriptor{ - MediaType: m.MediaType, - Digest: digest.FromBytes(data), - Size: int64(len(data)), - } - - return m.Manifest, desc, nil -} - -// RelocateReference returns a name.Reference given a reference and registry -func RelocateReference(reference string, registry string) (name.Reference, error) { - ref, err := name.ParseReference(reference) - if err != nil { - return nil, err - } - - relocated, err := name.ParseReference(ref.Context().RepositoryStr(), name.WithDefaultRegistry(registry)) - if err != nil { - return nil, err - } - - if _, err := name.NewDigest(ref.Name()); err == nil { - return relocated.Context().Digest(ref.Identifier()), nil - } - return relocated.Context().Tag(ref.Identifier()), nil -} diff --git a/pkg/server/file.go b/pkg/server/file.go new file mode 100644 index 0000000..b12d4c2 --- /dev/null +++ b/pkg/server/file.go @@ -0,0 +1,27 @@ +package server + +import ( + "context" + "net/http" + "os" + "time" + + "github.com/gorilla/handlers" + "github.com/gorilla/mux" +) + +func NewFile(ctx context.Context, root string) (Server, error) { + http.FileServer(http.Dir(root)) + + r := mux.NewRouter() + r.Handle("/", handlers.LoggingHandler(os.Stdout, http.FileServer(http.Dir(root)))) + + srv := &http.Server{ + Handler: r, + Addr: ":8000", + WriteTimeout: 15 * time.Second, + ReadTimeout: 15 * time.Second, + } + + return srv, nil +} diff --git a/pkg/server/registry.go b/pkg/server/registry.go new file mode 100644 index 0000000..58d506f --- /dev/null +++ b/pkg/server/registry.go @@ -0,0 +1,118 @@ +package server + +import ( + "context" + "fmt" + "net/http" + "net/http/httptest" + "strings" + "time" + + "github.com/distribution/distribution/v3/configuration" + "github.com/distribution/distribution/v3/registry" + "github.com/distribution/distribution/v3/registry/handlers" + "github.com/docker/go-metrics" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +func NewRegistry(ctx context.Context, cfg *configuration.Configuration) (*registry.Registry, error) { + r, err := registry.NewRegistry(ctx, cfg) + if err != nil { + return nil, err + } + + if cfg.HTTP.Debug.Prometheus.Enabled { + path := cfg.HTTP.Debug.Prometheus.Path + if path == "" { + path = "/metrics" + } + http.Handle(path, metrics.Handler()) + } + + return r, nil +} + +type tmpRegistryServer struct { + *httptest.Server +} + +func NewTempRegistry(ctx context.Context, root string) *tmpRegistryServer { + cfg := &configuration.Configuration{ + Version: "0.1", + Storage: configuration.Storage{ + "cache": configuration.Parameters{"blobdescriptor": "inmemory"}, + "filesystem": configuration.Parameters{"rootdirectory": root}, + }, + } + cfg.Log.Level = "error" + cfg.HTTP.Headers = http.Header{ + "X-Content-Type-Options": []string{"nosniff"}, + } + + l, err := logrus.ParseLevel("panic") + if err != nil { + l = logrus.ErrorLevel + } + logrus.SetLevel(l) + + app := handlers.NewApp(ctx, cfg) + app.RegisterHealthChecks() + handler := alive("/", app) + + s := httptest.NewUnstartedServer(handler) + return &tmpRegistryServer{ + Server: s, + } +} + +func (t *tmpRegistryServer) Registry() string { + return strings.Replace(t.Server.URL, "http://", "", 1) +} + +func (t *tmpRegistryServer) Start() error { + t.Server.Start() + + err := retry(5, 1*time.Second, func() (err error) { + resp, err := http.Get(t.Server.URL + "/v2") + if err != nil { + return err + } + resp.Body.Close() + if resp.StatusCode == http.StatusOK { + return nil + } + return errors.New("to start temporary registry") + + }) + return err +} + +func (t *tmpRegistryServer) Stop() { + t.Server.Close() +} + +func alive(path string, handler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == path { + w.Header().Set("Cache-Control", "no-cache") + w.WriteHeader(http.StatusOK) + return + } + handler.ServeHTTP(w, r) + }) +} + +func retry(attempts int, sleep time.Duration, f func() error) (err error) { + for i := 0; i < attempts; i++ { + if i > 0 { + time.Sleep(sleep) + sleep *= 2 + } + err = f() + if err == nil { + return nil + } + } + return fmt.Errorf("after %d attempts, last error: %s", attempts, err) +} diff --git a/pkg/server/server.go b/pkg/server/server.go new file mode 100644 index 0000000..6c20e09 --- /dev/null +++ b/pkg/server/server.go @@ -0,0 +1,5 @@ +package server + +type Server interface { + ListenAndServe() error +} diff --git a/pkg/store/add.go b/pkg/store/add.go deleted file mode 100644 index 76fd1c2..0000000 --- a/pkg/store/add.go +++ /dev/null @@ -1,146 +0,0 @@ -package store - -import ( - "context" - "io/ioutil" - "os" - "path/filepath" - - "github.com/google/go-containerregistry/pkg/name" - ocispec "github.com/opencontainers/image-spec/specs-go/v1" - - "github.com/rancherfederal/hauler/pkg/artifact" - "github.com/rancherfederal/hauler/pkg/cache" - "github.com/rancherfederal/hauler/pkg/layout" -) - -// AddArtifact will add an artifact.OCI to the store -// The method to achieve this is to save artifact.OCI to a temporary directory in an OCI layout compatible form. Once -// 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 name.Reference) (ocispec.Descriptor, error) { - if err := s.precheck(); err != nil { - return ocispec.Descriptor{}, err - } - - stg, err := newOciStage() - if err != nil { - return ocispec.Descriptor{}, err - } - - if s.cache != nil { - cached := cache.Oci(oci, s.cache) - oci = cached - } - - pdesc, err := stg.add(ctx, oci, reference) - if err != nil { - return ocispec.Descriptor{}, err - } - - if err := stg.commit(ctx, s); err != nil { - return ocispec.Descriptor{}, nil - } - - return pdesc, nil -} - -// Flush is a fancy name for delete-all-the-things, in this case it's as trivial as deleting everything in the underlying store directory -// This can be a highly destructive operation if the store's directory happens to be inline with other non-store contents -// To reduce the blast radius and likelihood of deleting things we don't own, Flush explicitly includes docker/registry/v2 -// in the search dir -func (s *Store) Flush(ctx context.Context) error { - contentDir := filepath.Join(s.DataDir, "docker", "registry", "v2") - fs, err := ioutil.ReadDir(contentDir) - if !os.IsNotExist(err) && err != nil { - return err - } - - for _, f := range fs { - err := os.RemoveAll(filepath.Join(contentDir, f.Name())) - if err != nil { - return err - } - } - - return nil -} - -// AddCollection . -func (s *Store) AddCollection(ctx context.Context, coll artifact.Collection) ([]ocispec.Descriptor, error) { - if err := s.precheck(); err != nil { - return nil, err - } - - cnts, err := coll.Contents() - if err != nil { - return nil, err - } - - for ref, o := range cnts { - if _, err := s.AddArtifact(ctx, o, ref); err != nil { - return nil, nil - } - } - - return nil, err -} - -type stager interface { - // add adds an artifact.OCI to the stage - add(artifact.OCI) error - - // commit pushes all the staged contents into the store and closes the stage - commit(*Store) error - - // close flushes and closes the stage - close() error -} - -type oci struct { - layout layout.Path - root string -} - -func (o *oci) add(ctx context.Context, oci artifact.OCI, reference name.Reference) (ocispec.Descriptor, error) { - mdesc, err := o.layout.WriteOci(oci, reference) - if err != nil { - return ocispec.Descriptor{}, err - } - return mdesc, err -} - -func (o *oci) commit(ctx context.Context, s *Store) error { - defer o.close() - ts, err := layout.NewOCIStore(o.root) - if err != nil { - return err - } - - if err = layout.Copy(ctx, ts, s.Registry()); err != nil { - return err - } - return err -} - -func (o *oci) close() error { - return os.RemoveAll(o.root) -} - -func newOciStage() (*oci, error) { - tmpdir, err := os.MkdirTemp("", "hauler") - if err != nil { - return nil, err - } - - l, err := layout.FromPath(tmpdir) - if err != nil { - return nil, err - } - - return &oci{ - layout: l, - root: tmpdir, - }, nil -} diff --git a/pkg/store/oci.go b/pkg/store/oci.go new file mode 100644 index 0000000..7b15732 --- /dev/null +++ b/pkg/store/oci.go @@ -0,0 +1,233 @@ +package store + +import ( + "context" + "encoding/json" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "sync" + + ccontent "github.com/containerd/containerd/content" + "github.com/containerd/containerd/content/local" + "github.com/containerd/containerd/remotes" + "github.com/opencontainers/image-spec/specs-go" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "oras.land/oras-go/pkg/content" + "oras.land/oras-go/pkg/target" + + "github.com/rancherfederal/hauler/internal/mapper" + "github.com/rancherfederal/hauler/pkg/consts" +) + +var _ target.Target = (*oci)(nil) + +type oci struct { + ccontent.Store + + root string + index *ocispec.Index + nameMap *sync.Map // map[string]ocispec.Descriptor +} + +func NewOCI(root string) (*oci, error) { + fs, err := local.NewStore(root) + if err != nil { + return nil, err + } + + return &oci{ + Store: fs, + + root: root, + nameMap: &sync.Map{}, + }, nil +} + +func (o *oci) LoadIndex() error { + path := o.path(consts.OCIImageIndexFile) + idx, err := os.Open(path) + if err != nil { + if !os.IsNotExist(err) { + return err + } + o.index = &ocispec.Index{ + Versioned: specs.Versioned{ + SchemaVersion: 2, + }, + } + return nil + } + defer idx.Close() + + if err := json.NewDecoder(idx).Decode(&o.index); err != nil { + return err + } + + for _, desc := range o.index.Manifests { + if name := desc.Annotations[ocispec.AnnotationRefName]; name != "" { + o.nameMap.Store(name, desc) + } + } + return nil +} + +func (o *oci) SaveIndex() error { + var descs []ocispec.Descriptor + o.nameMap.Range(func(name, desc interface{}) bool { + n := name.(string) + d := desc.(ocispec.Descriptor) + + if d.Annotations == nil { + d.Annotations = make(map[string]string) + } + d.Annotations[ocispec.AnnotationRefName] = n + descs = append(descs, d) + return true + }) + o.index.Manifests = descs + data, err := json.Marshal(o.index) + if err != nil { + return err + } + return os.WriteFile(o.path(consts.OCIImageIndexFile), data, 0644) +} + +// Resolve attempts to resolve the reference into a name and descriptor. +// +// The argument `ref` should be a scheme-less URI representing the remote. +// Structurally, it has a host and path. The "host" can be used to directly +// reference a specific host or be matched against a specific handler. +// +// The returned name should be used to identify the referenced entity. +// Dependending on the remote namespace, this may be immutable or mutable. +// While the name may differ from ref, it should itself be a valid ref. +// +// If the resolution fails, an error will be returned. +func (o *oci) Resolve(ctx context.Context, ref string) (name string, desc ocispec.Descriptor, err error) { + if err := o.LoadIndex(); err != nil { + return "", ocispec.Descriptor{}, err + } + d, ok := o.nameMap.Load(ref) + if !ok { + return "", ocispec.Descriptor{}, err + } + desc = d.(ocispec.Descriptor) + return ref, desc, nil +} + +// Fetcher returns a new fetcher for the provided reference. +// All content fetched from the returned fetcher will be +// from the namespace referred to by ref. +func (o *oci) Fetcher(ctx context.Context, ref string) (remotes.Fetcher, error) { + if err := o.LoadIndex(); err != nil { + return nil, err + } + if _, ok := o.nameMap.Load(ref); !ok { + return nil, nil + } + return o, nil +} + +func (o *oci) Fetch(ctx context.Context, desc ocispec.Descriptor) (io.ReadCloser, error) { + readerAt, err := o.blobReaderAt(desc) + if err != nil { + return nil, err + } + return readerAt, nil +} + +// Pusher returns a new pusher for the provided reference +// The returned Pusher should satisfy content.Ingester and concurrent attempts +// to push the same blob using the Ingester API should result in ErrUnavailable. +func (o *oci) Pusher(ctx context.Context, ref string) (remotes.Pusher, error) { + if err := o.LoadIndex(); err != nil { + return nil, err + } + + var ( + baseRef, hash string + ) + parts := strings.SplitN(ref, "@", 2) + baseRef = parts[0] + if len(parts) > 1 { + hash = parts[1] + } + return &ociPusher{ + oci: o, + ref: baseRef, + digest: hash, + }, nil +} + +func (o *oci) blobReaderAt(desc ocispec.Descriptor) (*os.File, error) { + blobPath, err := o.ensureBlob(desc) + if err != nil { + return nil, err + } + return os.Open(blobPath) +} + +func (o *oci) blobWriterAt(desc ocispec.Descriptor) (*os.File, error) { + blobPath, err := o.ensureBlob(desc) + if err != nil { + return nil, err + } + return os.OpenFile(blobPath, os.O_WRONLY|os.O_CREATE, 0644) +} + +func (o *oci) ensureBlob(desc ocispec.Descriptor) (string, error) { + dir := o.path("blobs", desc.Digest.Algorithm().String()) + if err := os.MkdirAll(dir, os.ModePerm); err != nil && !os.IsExist(err) { + return "", err + } + return filepath.Join(dir, desc.Digest.Hex()), nil +} + +func (o *oci) path(elem ...string) string { + complete := []string{string(o.root)} + return filepath.Join(append(complete, elem...)...) +} + +type ociPusher struct { + oci *oci + ref string + digest string +} + +// Push returns a content writer for the given resource identified +// by the descriptor. +func (p *ociPusher) Push(ctx context.Context, d ocispec.Descriptor) (ccontent.Writer, error) { + switch d.MediaType { + case ocispec.MediaTypeImageManifest, ocispec.MediaTypeImageIndex: + // if the hash of the content matches that which was provided as the hash for the root, mark it + if p.digest != "" && p.digest == d.Digest.String() { + if err := p.oci.LoadIndex(); err != nil { + return nil, err + } + p.oci.nameMap.Store(p.ref, d) + if err := p.oci.SaveIndex(); err != nil { + return nil, err + } + } + } + + blobPath, err := p.oci.ensureBlob(d) + if err != nil { + return nil, err + } + + if _, err := os.Stat(blobPath); err == nil { + // file already exists, discard (but validate digest) + return content.NewIoContentWriter(ioutil.Discard, content.WithOutputHash(d.Digest)), nil + } + + writer, err := mapper.NewFileWriter(blobPath, 0644, d.Size) + if err != nil { + return nil, err + } + + return writer, nil +} diff --git a/pkg/store/options.go b/pkg/store/options.go index 8ee91aa..f114403 100644 --- a/pkg/store/options.go +++ b/pkg/store/options.go @@ -1,20 +1,13 @@ package store -import "github.com/rancherfederal/hauler/pkg/cache" +import ( + "github.com/rancherfederal/hauler/internal/cache" +) -// Options defines options for Store type Options func(*Store) -// WithCache initializes a Store with a cache.Cache, all content added to the Store will first be cached func WithCache(c cache.Cache) Options { - return func(s *Store) { - s.cache = c - } -} - -// WithDefaultRepository sets the default repository to use when none is specified (defaults to "library") -func WithDefaultRepository(repo string) Options { - return func(s *Store) { - s.DefaultRepository = repo + return func(b *Store) { + b.cache = c } } diff --git a/pkg/store/stager.go b/pkg/store/stager.go new file mode 100644 index 0000000..6a30eb7 --- /dev/null +++ b/pkg/store/stager.go @@ -0,0 +1,204 @@ +package store + +import ( + "context" + "encoding/json" + "io" + "os" + "path/filepath" + + "github.com/google/go-containerregistry/pkg/name" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/static" + "github.com/opencontainers/go-digest" + "github.com/opencontainers/image-spec/specs-go" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" + "golang.org/x/sync/errgroup" + "oras.land/oras-go/pkg/oras" + + "github.com/rancherfederal/hauler/pkg/artifact" + "github.com/rancherfederal/hauler/pkg/consts" +) + +type stager interface { + add(ctx context.Context, oci artifact.OCI, ref name.Reference) error + + commit(ctx context.Context, b *Store) (ocispec.Descriptor, error) + + flush(ctx context.Context) error +} + +var _ stager = (*layout)(nil) + +func newLayout() (*layout, error) { + tmpdir, err := os.MkdirTemp("", "hauler") + if err != nil { + return nil, err + } + return &layout{ + root: tmpdir, + blobs: make(map[digest.Digest]v1.Layer), + }, nil +} + +type layout struct { + root string + + descs []ocispec.Descriptor + blobs map[digest.Digest]v1.Layer +} + +func (l *layout) commit(ctx context.Context, b *Store) (ocispec.Descriptor, error) { + defer l.flush(ctx) + + var g errgroup.Group + for d, blob := range l.blobs { + blob := blob + d := d + g.Go(func() error { + rc, err := blob.Compressed() + if err != nil { + return err + } + return l.writeBlob(d, rc) + }) + } + if err := g.Wait(); err != nil { + return ocispec.Descriptor{}, err + } + + idx := ocispec.Index{ + Versioned: specs.Versioned{SchemaVersion: 2}, + Manifests: l.descs, + Annotations: nil, + } + + data, err := json.Marshal(idx) + if err != nil { + return ocispec.Descriptor{}, err + } + if err := os.WriteFile(l.path("index.json"), data, 0666); err != nil { + return ocispec.Descriptor{}, err + } + + // src, err := content.NewOCI(l.path("")) + src, err := NewOCI(l.path("")) + if err != nil { + return ocispec.Descriptor{}, err + } + + // dst, err := content.NewOCI(b.root) + dst, err := NewOCI(b.root) + if err != nil { + return ocispec.Descriptor{}, err + } + + if err := src.LoadIndex(); err != nil { + return ocispec.Descriptor{}, err + } + ref := src.index.Manifests[0].Annotations[ocispec.AnnotationRefName] + // refs := src.ListReferences() + // if len(refs) > 1 { + // return ocispec.Descriptor{}, errors.Errorf("expected 1 reference to commit, got %d", len(refs)) + // } + // + // var ref string + // for r := range refs { + // ref = r + // } + + desc, err := oras.Copy(ctx, src, ref, dst, "", + oras.WithAdditionalCachedMediaTypes(consts.DockerManifestSchema2)) + if err != nil { + return ocispec.Descriptor{}, errors.Wrap(err, "comitting staging layout") + } + + // TODO: This is hacky but needed since oras.Copy between oci layouts doesn't update the index + // dst.AddReference(ref, desc) + + if err := dst.SaveIndex(); err != nil { + return ocispec.Descriptor{}, err + } + + return desc, err +} + +func (l *layout) flush(ctx context.Context) error { + return os.RemoveAll(l.path("")) +} + +func (l *layout) add(ctx context.Context, oci artifact.OCI, ref name.Reference) error { + m, err := oci.Manifest() + if err != nil { + return err + } + mdata, err := json.Marshal(m) + if err != nil { + return err + } + mdigest := digest.FromBytes(mdata) + l.blobs[mdigest] = static.NewLayer(mdata, m.MediaType) + + cdata, err := oci.RawConfig() + if err != nil { + return err + } + + l.blobs[digest.FromBytes(cdata)] = static.NewLayer(cdata, "") + + layers, err := oci.Layers() + if err != nil { + return err + } + + for _, layer := range layers { + h, err := layer.Digest() + if err != nil { + return err + } + + d := digest.NewDigestFromHex(h.Algorithm, h.Hex) + l.blobs[d] = layer + } + + mdesc := ocispec.Descriptor{ + MediaType: oci.MediaType(), + Digest: mdigest, + Size: int64(len(mdata)), + Annotations: map[string]string{ + ocispec.AnnotationRefName: ref.Name(), + }, + + URLs: nil, + Platform: nil, + } + l.descs = append(l.descs, mdesc) + return nil +} + +func (l *layout) writeBlob(d digest.Digest, rc io.ReadCloser) error { + dir := l.path("blobs", d.Algorithm().String()) + if err := os.MkdirAll(dir, os.ModePerm); err != nil && !os.IsExist(err) { + return err + } + + file := filepath.Join(dir, d.Hex()) + if _, err := os.Stat(file); err == nil { + return err + } + + w, err := os.Create(file) + if err != nil { + return err + } + defer w.Close() + + _, err = io.Copy(w, rc) + return err +} + +func (l *layout) path(elem ...string) string { + complete := []string{string(l.root)} + return filepath.Join(append(complete, elem...)...) +} diff --git a/pkg/store/store.go b/pkg/store/store.go index b449956..1a20d81 100644 --- a/pkg/store/store.go +++ b/pkg/store/store.go @@ -2,212 +2,183 @@ package store import ( "context" + "encoding/json" "fmt" - "io" - "net/http" - "net/http/httptest" - "regexp" - "strconv" - "time" + "os" + "path/filepath" - "github.com/distribution/distribution/v3/configuration" - dcontext "github.com/distribution/distribution/v3/context" - "github.com/distribution/distribution/v3/reference" - "github.com/distribution/distribution/v3/registry/client" - "github.com/distribution/distribution/v3/registry/handlers" "github.com/google/go-containerregistry/pkg/name" - "github.com/sirupsen/logrus" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "oras.land/oras-go/pkg/content" + "oras.land/oras-go/pkg/oras" + "oras.land/oras-go/pkg/target" - // Init filesystem distribution storage driver - _ "github.com/distribution/distribution/v3/registry/storage/driver/filesystem" - - "github.com/rancherfederal/hauler/pkg/cache" + "github.com/rancherfederal/hauler/internal/cache" + "github.com/rancherfederal/hauler/pkg/artifact" + "github.com/rancherfederal/hauler/pkg/consts" ) -var ( - httpRegex = regexp.MustCompile("https?://") -) - -// Store is a simple wrapper around distribution/distribution to enable hauler's use case type Store struct { - DataDir string - DefaultRepository string - - config *configuration.Configuration - handler http.Handler - server *httptest.Server - cache cache.Cache + root string + store *content.OCI + cache cache.Cache } -// NewStore creates a new registry store, designed strictly for use within haulers embedded operations and _not_ for serving -func NewStore(ctx context.Context, dataDir string, opts ...Options) *Store { - cfg := &configuration.Configuration{ - Version: "0.1", - Storage: configuration.Storage{ - "cache": configuration.Parameters{"blobdescriptor": "inmemory"}, - "filesystem": configuration.Parameters{"rootdirectory": dataDir}, - }, - } - cfg.Log.Level = "panic" - cfg.HTTP.Headers = http.Header{"X-Content-Type-Options": []string{"nosniff"}} - - handler := setupHandler(ctx, cfg) - - s := &Store{ - DataDir: dataDir, - config: cfg, - handler: handler, - } - - for _, opt := range opts { - opt(s) - } - - return s -} - -// Open will create a new server and start it, it's up to the consumer to close it -func (s *Store) Open() *httptest.Server { - server := httptest.NewServer(s.handler) - s.server = server - return server -} - -// Close stops the server -func (s *Store) Close() { - s.server.Close() - s.server = nil - return -} - -// List will list all known content tags in the registry -// TODO: This fn is messy and needs cleanup, this is arguably easier with the catalog api as well -func (s *Store) List(ctx context.Context) ([]string, error) { - reg, err := client.NewRegistry(s.RegistryURL(), nil) +func NewStore(rootdir string, opts ...Options) (*Store, error) { + ociStore, err := content.NewOCI(rootdir) if err != nil { return nil, err } - entries := make(map[string]reference.Named) - last := "" - for { - chunk := make([]string, 20) // randomly chosen number... - nf, err := reg.Repositories(ctx, chunk, last) - last = strconv.Itoa(nf) - - for _, e := range chunk { - if e == "" { - continue - } - - ref, err := reference.WithName(e) - if err != nil { - return nil, err - } - entries[e] = ref - } - if err == io.EOF { - break - } + b := &Store{ + root: rootdir, + store: ociStore, } - var refs []string - for ref, named := range entries { - repo, err := client.NewRepository(named, s.RegistryURL(), nil) + for _, opt := range opts { + opt(b) + } + return b, nil +} + +// AddArtifact will add an artifact.OCI to the store +// The method to achieve this is to save artifact.OCI to a temporary directory in an OCI layout compatible form. Once +// 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 name.Reference) (ocispec.Descriptor, error) { + stage, err := newLayout() + if err != nil { + return ocispec.Descriptor{}, err + } + + if s.cache != nil { + cached := cache.Oci(oci, s.cache) + oci = cached + } + + if err := stage.add(ctx, oci, reference); err != nil { + return ocispec.Descriptor{}, err + } + return stage.commit(ctx, s) +} + +// AddCollection . +func (s *Store) AddCollection(ctx context.Context, coll artifact.Collection) ([]ocispec.Descriptor, error) { + cnts, err := coll.Contents() + if err != nil { + return nil, err + } + + var descs []ocispec.Descriptor + for ref, oci := range cnts { + ds, err := s.AddArtifact(ctx, oci, ref) if err != nil { return nil, err } - - tsvc := repo.Tags(ctx) - ts, err := tsvc.All(ctx) - if err != nil { - continue - } - - for _, t := range ts { - ref, err := name.ParseReference(ref, name.WithDefaultRegistry(""), name.WithDefaultTag(t)) - if err != nil { - return nil, err - } - refs = append(refs, ref.Name()) - } + descs = append(descs, ds) } - return refs, nil + return descs, nil } -// precheck checks whether server is appropriately started and errors if it's not -// used to safely run Store operations without fear of panics -func (s *Store) precheck() error { - if s.server == nil || s.server.URL == "" { - return fmt.Errorf("server is not started yet") +// Flush is a fancy name for delete-all-the-things, in this case it's as trivial as deleting oci-layout content +// This can be a highly destructive operation if the store's directory happens to be inline with other non-store contents +// To reduce the blast radius and likelihood of deleting things we don't own, Flush explicitly deletes oci-layout content only +func (s *Store) Flush(ctx context.Context) error { + blobs := filepath.Join(s.root, "blobs") + if err := os.RemoveAll(blobs); err != nil { + return err + } + + index := filepath.Join(s.root, "index.json") + if err := os.RemoveAll(index); err != nil { + return err + } + + layout := filepath.Join(s.root, "oci-layout") + if err := os.RemoveAll(layout); err != nil { + return err + } + + return nil +} + +func (s *Store) List(ctx context.Context) ([]ocispec.Descriptor, error) { + refs := s.store.ListReferences() + + var descs []ocispec.Descriptor + for _, desc := range refs { + descs = append(descs, desc) + } + return descs, nil +} + +func (s *Store) Get(ctx context.Context, to target.Target, reference string) error { + _, err := oras.Copy(ctx, s.store, reference, to, "", + oras.WithAdditionalCachedMediaTypes(consts.DockerManifestSchema2)) + if err != nil { + return err } return nil } -// Registry returns the registries URL without the protocol, suitable for image relocation operations -func (s *Store) Registry() string { - return httpRegex.ReplaceAllString(s.server.URL, "") -} - -// RegistryURL returns the registries URL -func (s *Store) RegistryURL() string { - return s.server.URL -} - -func alive(path string, handler http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.Path == path { - w.Header().Set("Cache-Control", "no-cache") - w.WriteHeader(http.StatusOK) - return - } - handler.ServeHTTP(w, r) - }) -} - -// setupHandler will set up the registry handler -func setupHandler(ctx context.Context, config *configuration.Configuration) http.Handler { - ctx, _ = configureLogging(ctx, config) - - app := handlers.NewApp(ctx, config) - app.RegisterHealthChecks() - handler := alive("/", app) - - return handler -} - -func configureLogging(ctx context.Context, cfg *configuration.Configuration) (context.Context, context.CancelFunc) { - logrus.SetLevel(logLevel(cfg.Log.Level)) - - formatter := cfg.Log.Formatter - if formatter == "" { - formatter = "text" - } - - logrus.SetFormatter(&logrus.TextFormatter{ - TimestampFormat: time.RFC3339Nano, - }) - - if len(cfg.Log.Fields) > 0 { - var fields []interface{} - for k := range cfg.Log.Fields { - fields = append(fields, k) +// Copy performs bulk copy operations on the stores oci layout to a provided target.Target +func (s *Store) Copy(ctx context.Context, to target.Target, toMapper func(string) (string, error)) error { + for ref := range s.store.ListReferences() { + toRef := "" + if toMapper != nil { + tr, err := toMapper(ref) + if err != nil { + return err + } + toRef = tr } - ctx = dcontext.WithValues(ctx, cfg.Log.Fields) - ctx = dcontext.WithLogger(ctx, dcontext.GetLogger(ctx, fields...)) + fmt.Println("copying to: ", toRef) + _, err := oras.Copy(ctx, s.store, ref, to, toRef, + oras.WithAdditionalCachedMediaTypes(consts.DockerManifestSchema2)) + if err != nil { + return err + } } - - dcontext.SetDefaultLogger(dcontext.GetLogger(ctx)) - return context.WithCancel(ctx) + return nil } -func logLevel(level configuration.Loglevel) logrus.Level { - l, err := logrus.ParseLevel(string(level)) +// Identify is a helper function that will identify the content type given a descriptor +func (s *Store) Identify(ctx context.Context, desc ocispec.Descriptor) string { + rc, err := s.store.Fetch(ctx, desc) if err != nil { - l = logrus.InfoLevel - logrus.Warnf("error parsing log level %q: %v, using %q", level, err, l) + return "" } - return l + defer rc.Close() + + m := struct { + Config struct { + MediaType string `json:"mediaType"` + } `json:"config"` + }{} + if err := json.NewDecoder(rc).Decode(&m); err != nil { + return "" + } + + return m.Config.MediaType +} + +// RelocateReference returns a name.Reference given a reference and registry +func RelocateReference(reference string, registry string) (name.Reference, error) { + ref, err := name.ParseReference(reference) + if err != nil { + return nil, err + } + + relocated, err := name.ParseReference(ref.Context().RepositoryStr(), name.WithDefaultRegistry(registry)) + if err != nil { + return nil, err + } + + if _, err := name.NewDigest(ref.Name()); err == nil { + return relocated.Context().Digest(ref.Identifier()), nil + } + return relocated.Context().Tag(ref.Identifier()), nil } diff --git a/pkg/store/store_test.go b/pkg/store/store_test.go index 7aba472..936336d 100644 --- a/pkg/store/store_test.go +++ b/pkg/store/store_test.go @@ -1,87 +1,68 @@ -package store +package store_test import ( "context" "os" + "reflect" "testing" "github.com/google/go-containerregistry/pkg/name" - v1 "github.com/google/go-containerregistry/pkg/v1" - "github.com/google/go-containerregistry/pkg/v1/random" - "github.com/google/go-containerregistry/pkg/v1/remote" + v1 "github.com/opencontainers/image-spec/specs-go/v1" + + "github.com/rancherfederal/hauler/pkg/artifact" + "github.com/rancherfederal/hauler/pkg/content/image" + "github.com/rancherfederal/hauler/pkg/store" ) -func TestStore_List(t *testing.T) { +func TestStore_AddArtifact(t *testing.T) { ctx := context.Background() - s, err := testStore(ctx) + tmpdir, err := os.MkdirTemp("", "hauler") if err != nil { t.Fatal(err) } - s.Open() - defer s.Close() + s, err := store.NewStore(tmpdir) + if err != nil { + t.Fatal(err) + } - r := randomImage(t) - addImageToStore(t, s, r, "hauler/tester:latest") - addImageToStore(t, s, r, "hauler/tester:non") - addImageToStore(t, s, r, "other/ns:more") - addImageToStore(t, s, r, "unique/donkey:v1.2.2") + img, _ := image.NewImage("ghcr.io/stefanprodan/podinfo:6.0.3") + ref, _ := name.ParseReference("ghcr.io/stephanprodan/podinfo:6.0.3") type args struct { - ctx context.Context + ctx context.Context + oci artifact.OCI + reference name.Reference } tests := []struct { name string args args + want v1.Descriptor wantErr bool }{ { - name: "should list", - args: args{}, + name: "should add artifact", + args: args{ + ctx: ctx, + oci: img, + reference: ref, + }, + want: v1.Descriptor{}, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - refs, err := s.List(ctx) - if (err != nil) != tt.wantErr { - t.Errorf("List() error = %v, wantErr %v", err, tt.wantErr) - } - // TODO: Make this more robust - if len(refs) != 4 { - t.Errorf("Expected 4, got %d", len(refs)) + got, err := s.AddArtifact(tt.args.ctx, tt.args.oci, tt.args.reference) + if (err != nil) != tt.wantErr { + t.Errorf("AddArtifact() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("AddArtifact() got = %v, want %v", got, tt.want) } }) } } - -func testStore(ctx context.Context) (*Store, error) { - tmpdir, err := os.MkdirTemp("", "hauler") - if err != nil { - return nil, err - } - - s := NewStore(ctx, tmpdir) - return s, nil -} - -func randomImage(t *testing.T) v1.Image { - r, err := random.Image(1024, 3) - if err != nil { - t.Fatalf("random.Image() = %v", err) - } - return r -} - -func addImageToStore(t *testing.T, s *Store, image v1.Image, reference string) { - ref, err := name.ParseReference(reference, name.WithDefaultRegistry(s.Registry())) - if err != nil { - t.Error(err) - } - - if err := remote.Write(ref, image); err != nil { - t.Error(err) - } -} diff --git a/testdata/chart-collection.yaml b/testdata/chart-collection.yaml index 10853f5..c597211 100644 --- a/testdata/chart-collection.yaml +++ b/testdata/chart-collection.yaml @@ -7,5 +7,17 @@ spec: charts: # charts are also fetched and served as OCI content (currently experimental in helm) # HELM_EXPERIMENTAL_OCI=1 helm chart pull /loki:2.6.2 - - name: loki - repoURL: https://grafana.github.io/helm-charts +# - name: loki +# repoURL: https://grafana.github.io/helm-charts + +# - name: longhorn +# repoURL: https://charts.longhorn.io + +# - name: cert-manager +# repoURL: https://charts.jetstack.io +# version: v1.6.1 +# extraImages: +# - ref: quay.io/jetstack/cert-manager-cainjector:v1.6.1 + + - name: podinfo + repoURL: https://stefanprodan.github.io/podinfo diff --git a/testdata/contents.yaml b/testdata/contents.yaml index 222e111..b68b376 100644 --- a/testdata/contents.yaml +++ b/testdata/contents.yaml @@ -15,9 +15,7 @@ spec: - ref: "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 - # this will still work, but default to a filename of "get.k3s.io" - - ref: https://get.k3s.io - name: get-k3s.sh + - ref: https://get.k3s.io?filename=get-k3s.sh --- apiVersion: content.hauler.cattle.io/v1alpha1