Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package go-containerregistry for openSUSE:Factory checked in at 2023-12-28 23:02:52 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/go-containerregistry (Old) and /work/SRC/openSUSE:Factory/.go-containerregistry.new.28375 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "go-containerregistry" Thu Dec 28 23:02:52 2023 rev:5 rq:1135385 version:0.17.0 Changes: -------- --- /work/SRC/openSUSE:Factory/go-containerregistry/go-containerregistry.changes 2023-11-26 19:38:09.612048008 +0100 +++ /work/SRC/openSUSE:Factory/.go-containerregistry.new.28375/go-containerregistry.changes 2023-12-28 23:04:36.673345163 +0100 @@ -1,0 +2,13 @@ +Wed Dec 27 21:21:47 UTC 2023 - Dirk Müller <dmuel...@suse.com> + +- update to 0.17.0: + * Validate index architectures match children + * Set Content-Length for blob uploads + * Don't wrap DefaultKeychain with refreshes + * Build releases with Go 1.21 + * fix: mimic oci-layout in diskblobhandler + * tag: add command explanation to the long help + * feat: implement gc command + * feat: allow port and disk path to be overriden + +------------------------------------------------------------------- Old: ---- go-containerregistry-0.16.1.tar.gz New: ---- go-containerregistry-0.17.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ go-containerregistry.spec ++++++ --- /var/tmp/diff_new_pack.ajCo6G/_old 2023-12-28 23:04:37.421372502 +0100 +++ /var/tmp/diff_new_pack.ajCo6G/_new 2023-12-28 23:04:37.425372648 +0100 @@ -17,9 +17,8 @@ # nodebuginfo -%global goipath github.com/google/go-containerregistry Name: go-containerregistry -Version: 0.16.1 +Version: 0.17.0 Release: 0 Summary: Container Library and tools for working with container registries License: Apache-2.0 @@ -28,7 +27,7 @@ Source: https://github.com/google/go-containerregistry/archive/refs/tags/v%{version}.tar.gz#/%{name}-%{version}.tar.gz Source1: vendor.tar.gz BuildRequires: golang-packaging -BuildRequires: golang(API) = 1.20 +BuildRequires: golang(API) = 1.21 Conflicts: distribution-registry %description @@ -71,17 +70,18 @@ %autopatch -p1 %build -%{goprep} %{goipath} - -export CGO_ENABLED=0 - -%{gobuild} -mod vendor ./... +for i in crane gcrane registry; do + go build -mod=vendor -buildmode=pie -trimpath ./cmd/$i +done %install -%{goinstall} +find -name crane +for bin in crane gcrane registry; do + install $bin -D %{buildroot}/%{_bindir}/$bin +done # "only one tool per thing" SLE15 policy conflicts %if 0%{?suse_version} && %{?suse_version} < 1550 -rm -v %{buildroot}/%{_bindir}/{registry,help} +rm -v %{buildroot}/%{_bindir}/registry %endif %if %{?suse_version} > 1500 @@ -89,7 +89,6 @@ %license LICENSE %doc README.md %{_bindir}/registry -%exclude %{_bindir}/help %endif %files -n crane ++++++ go-containerregistry-0.16.1.tar.gz -> go-containerregistry-0.17.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-containerregistry-0.16.1/.gitattributes new/go-containerregistry-0.17.0/.gitattributes --- old/go-containerregistry-0.16.1/.gitattributes 2023-08-02 22:59:06.000000000 +0200 +++ new/go-containerregistry-0.17.0/.gitattributes 2023-11-29 22:32:21.000000000 +0100 @@ -5,3 +5,4 @@ **/zz_deepcopy_generated.go linguist-generated=true cmd/crane/doc/crane*.md linguist-generated=true go.sum linguist-generated=true +**/testdata/** ignore-lint=true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-containerregistry-0.16.1/.github/workflows/release.yml new/go-containerregistry-0.17.0/.github/workflows/release.yml --- old/go-containerregistry-0.16.1/.github/workflows/release.yml 2023-08-02 22:59:06.000000000 +0200 +++ new/go-containerregistry-0.17.0/.github/workflows/release.yml 2023-11-29 22:32:21.000000000 +0100 @@ -15,7 +15,7 @@ run: git fetch --prune --unshallow - uses: actions/setup-go@v4 with: - go-version: 1.18 + go-version: 1.21 check-latest: true - uses: goreleaser/goreleaser-action@v4.2.0 id: run-goreleaser diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-containerregistry-0.16.1/cmd/crane/cmd/gc.go new/go-containerregistry-0.17.0/cmd/crane/cmd/gc.go --- old/go-containerregistry-0.16.1/cmd/crane/cmd/gc.go 1970-01-01 01:00:00.000000000 +0100 +++ new/go-containerregistry-0.17.0/cmd/crane/cmd/gc.go 2023-11-29 22:32:21.000000000 +0100 @@ -0,0 +1,66 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "fmt" + "os" + + "github.com/google/go-containerregistry/pkg/v1/layout" + "github.com/spf13/cobra" +) + +func NewCmdLayout() *cobra.Command { + cmd := &cobra.Command{ + Use: "layout", + } + cmd.AddCommand(newCmdGc()) + return cmd +} + +// NewCmdGc creates a new cobra.Command for the pull subcommand. +func newCmdGc() *cobra.Command { + cmd := &cobra.Command{ + Use: "gc OCI-LAYOUT", + Short: "Garbage collect unreferenced blobs in a local oci-layout", + Args: cobra.ExactArgs(1), + Hidden: true, // TODO: promote to public once theres some milage + RunE: func(_ *cobra.Command, args []string) error { + path := args[0] + + p, err := layout.FromPath(path) + + if err != nil { + return err + } + + blobs, err := p.GarbageCollect() + if err != nil { + return err + } + + for _, blob := range blobs { + if err := p.RemoveBlob(blob); err != nil { + return err + } + fmt.Fprintf(os.Stderr, "garbage collecting: %s\n", blob.String()) + } + + return nil + }, + } + + return cmd +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-containerregistry-0.16.1/cmd/crane/cmd/root.go new/go-containerregistry-0.17.0/cmd/crane/cmd/root.go --- old/go-containerregistry-0.16.1/cmd/crane/cmd/root.go 2023-08-02 22:59:06.000000000 +0200 +++ new/go-containerregistry-0.17.0/cmd/crane/cmd/root.go 2023-11-29 22:32:21.000000000 +0100 @@ -129,7 +129,8 @@ NewCmdTag(&options), NewCmdValidate(&options), NewCmdVersion(), - newCmdRegistry(), + NewCmdRegistry(), + NewCmdLayout(), ) root.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Enable debug logs") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-containerregistry-0.16.1/cmd/crane/cmd/serve.go new/go-containerregistry-0.17.0/cmd/crane/cmd/serve.go --- old/go-containerregistry-0.16.1/cmd/crane/cmd/serve.go 2023-08-02 22:59:06.000000000 +0200 +++ new/go-containerregistry-0.17.0/cmd/crane/cmd/serve.go 2023-11-29 22:32:21.000000000 +0100 @@ -28,7 +28,7 @@ "github.com/google/go-containerregistry/pkg/registry" ) -func newCmdRegistry() *cobra.Command { +func NewCmdRegistry() *cobra.Command { cmd := &cobra.Command{ Use: "registry", } @@ -37,15 +37,16 @@ } func newCmdServe() *cobra.Command { - var disk bool + var address, disk string + var blobsToDisk bool cmd := &cobra.Command{ Use: "serve", - Short: "Serve an in-memory registry implementation", - Long: `This sub-command serves an in-memory registry implementation on an automatically chosen port (or $PORT) + Short: "Serve a registry implementation", + Long: `This sub-command serves a registry implementation on an automatically chosen port (:0), $PORT or --address The command blocks while the server accepts pushes and pulls. -Contents are only stored in memory, and when the process exits, pushed data is lost.`, +Contents are can be stored in memory (when the process exits, pushed data is lost.), and disk (--disk).`, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() @@ -54,7 +55,12 @@ if port == "" { port = "0" } - listener, err := net.Listen("tcp", ":"+port) + listenOn := ":" + port + if address != "" { + listenOn = address + } + + listener, err := net.Listen("tcp", listenOn) if err != nil { log.Fatalln(err) } @@ -62,10 +68,21 @@ port = fmt.Sprintf("%d", porti) bh := registry.NewInMemoryBlobHandler() - if disk { - tmp := os.TempDir() - log.Printf("storing blobs in %s", tmp) - bh = registry.NewDiskBlobHandler(tmp) + + diskp := disk + if cmd.Flags().Changed("blobs-to-disk") { + if disk != "" { + return fmt.Errorf("--disk and --blobs-to-disk can't be used together") + } + diskp, err = os.MkdirTemp(os.TempDir(), "craneregistry*") + if err != nil { + return err + } + } + + if diskp != "" { + log.Printf("storing blobs in %s", diskp) + bh = registry.NewDiskBlobHandler(diskp) } s := &http.Server{ @@ -89,7 +106,12 @@ return nil }, } - cmd.Flags().BoolVar(&disk, "blobs-to-disk", false, "Store blobs on disk") + // TODO: remove --blobs-to-disk in a future release. + cmd.Flags().BoolVarP(&blobsToDisk, "blobs-to-disk", "", false, "Store blobs on disk on tmpdir") cmd.Flags().MarkHidden("blobs-to-disk") + cmd.Flags().MarkDeprecated("blobs-to-disk", "and will stop working in a future release. use --disk=$(mktemp -d) instead.") + cmd.Flags().StringVarP(&disk, "disk", "", "", "Path to a directory where blobs will be stored") + cmd.Flags().StringVar(&address, "address", "", "Address to listen on") + return cmd } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-containerregistry-0.16.1/cmd/crane/cmd/tag.go new/go-containerregistry-0.17.0/cmd/crane/cmd/tag.go --- old/go-containerregistry-0.16.1/cmd/crane/cmd/tag.go 2023-08-02 22:59:06.000000000 +0200 +++ new/go-containerregistry-0.17.0/cmd/crane/cmd/tag.go 2023-11-29 22:32:21.000000000 +0100 @@ -24,7 +24,9 @@ return &cobra.Command{ Use: "tag IMG TAG", Short: "Efficiently tag a remote image", - Long: `This differs slightly from the "copy" command in a couple subtle ways: + Long: `Tag remote image without downloading it. + +This differs slightly from the "copy" command in a couple subtle ways: 1. You don't have to specify the entire repository for the tag you're adding. For example, these two commands are functionally equivalent: ` + "```" + ` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-containerregistry-0.16.1/cmd/crane/cmd/validate.go new/go-containerregistry-0.17.0/cmd/crane/cmd/validate.go --- old/go-containerregistry-0.16.1/cmd/crane/cmd/validate.go 2023-08-02 22:59:06.000000000 +0200 +++ new/go-containerregistry-0.17.0/cmd/crane/cmd/validate.go 2023-11-29 22:32:21.000000000 +0100 @@ -18,7 +18,6 @@ "fmt" "github.com/google/go-containerregistry/pkg/crane" - v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/tarball" "github.com/google/go-containerregistry/pkg/v1/validate" "github.com/spf13/cobra" @@ -36,28 +35,56 @@ Short: "Validate that an image is well-formed", Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { - for flag, maker := range map[string]func(string, ...crane.Option) (v1.Image, error){ - tarballPath: makeTarball, - remoteRef: crane.Pull, - } { - if flag == "" { - continue - } - img, err := maker(flag, *options...) + if tarballPath != "" { + img, err := tarball.ImageFromPath(tarballPath, nil) if err != nil { - return fmt.Errorf("failed to read image %s: %w", flag, err) + return fmt.Errorf("failed to read image %s: %w", tarballPath, err) } - opt := []validate.Option{} if fast { opt = append(opt, validate.Fast) } if err := validate.Image(img, opt...); err != nil { - fmt.Fprintf(cmd.OutOrStdout(), "FAIL: %s: %v\n", flag, err) + fmt.Fprintf(cmd.OutOrStdout(), "FAIL: %s: %v\n", tarballPath, err) return err } - fmt.Fprintf(cmd.OutOrStdout(), "PASS: %s\n", flag) + fmt.Fprintf(cmd.OutOrStdout(), "PASS: %s\n", tarballPath) + } + + if remoteRef != "" { + rmt, err := crane.Get(remoteRef, *options...) + if err != nil { + return fmt.Errorf("failed to read image %s: %w", remoteRef, err) + } + + o := crane.GetOptions(*options...) + + opt := []validate.Option{} + if fast { + opt = append(opt, validate.Fast) + } + if rmt.MediaType.IsIndex() && o.Platform == nil { + idx, err := rmt.ImageIndex() + if err != nil { + return fmt.Errorf("reading index: %w", err) + } + if err := validate.Index(idx, opt...); err != nil { + fmt.Fprintf(cmd.OutOrStdout(), "FAIL: %s: %v\n", remoteRef, err) + return err + } + } else { + img, err := rmt.Image() + if err != nil { + return fmt.Errorf("reading image: %w", err) + } + if err := validate.Image(img, opt...); err != nil { + fmt.Fprintf(cmd.OutOrStdout(), "FAIL: %s: %v\n", remoteRef, err) + return err + } + } + fmt.Fprintf(cmd.OutOrStdout(), "PASS: %s\n", remoteRef) } + return nil }, } @@ -67,7 +94,3 @@ return validateCmd } - -func makeTarball(path string, _ ...crane.Option) (v1.Image, error) { - return tarball.ImageFromPath(path, nil) -} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-containerregistry-0.16.1/cmd/crane/doc/crane_registry.md new/go-containerregistry-0.17.0/cmd/crane/doc/crane_registry.md --- old/go-containerregistry-0.16.1/cmd/crane/doc/crane_registry.md 2023-08-02 22:59:06.000000000 +0200 +++ new/go-containerregistry-0.17.0/cmd/crane/doc/crane_registry.md 2023-11-29 22:32:21.000000000 +0100 @@ -20,5 +20,5 @@ ### SEE ALSO * [crane](crane.md) - Crane is a tool for managing container images -* [crane registry serve](crane_registry_serve.md) - Serve an in-memory registry implementation +* [crane registry serve](crane_registry_serve.md) - Serve a registry implementation diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-containerregistry-0.16.1/cmd/crane/doc/crane_registry_serve.md new/go-containerregistry-0.17.0/cmd/crane/doc/crane_registry_serve.md --- old/go-containerregistry-0.16.1/cmd/crane/doc/crane_registry_serve.md 2023-08-02 22:59:06.000000000 +0200 +++ new/go-containerregistry-0.17.0/cmd/crane/doc/crane_registry_serve.md 2023-11-29 22:32:21.000000000 +0100 @@ -1,14 +1,14 @@ ## crane registry serve -Serve an in-memory registry implementation +Serve a registry implementation ### Synopsis -This sub-command serves an in-memory registry implementation on an automatically chosen port (or $PORT) +This sub-command serves a registry implementation on an automatically chosen port (:0), $PORT or --address The command blocks while the server accepts pushes and pulls. -Contents are only stored in memory, and when the process exits, pushed data is lost. +Contents are can be stored in memory (when the process exits, pushed data is lost.), and disk (--disk). ``` crane registry serve [flags] @@ -17,7 +17,9 @@ ### Options ``` - -h, --help help for serve + --address string Address to listen on + --disk string Path to a directory where blobs will be stored + -h, --help help for serve ``` ### Options inherited from parent commands diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-containerregistry-0.16.1/cmd/crane/doc/crane_tag.md new/go-containerregistry-0.17.0/cmd/crane/doc/crane_tag.md --- old/go-containerregistry-0.16.1/cmd/crane/doc/crane_tag.md 2023-08-02 22:59:06.000000000 +0200 +++ new/go-containerregistry-0.17.0/cmd/crane/doc/crane_tag.md 2023-11-29 22:32:21.000000000 +0100 @@ -4,6 +4,8 @@ ### Synopsis +Tag remote image without downloading it. + This differs slightly from the "copy" command in a couple subtle ways: 1. You don't have to specify the entire repository for the tag you're adding. For example, these two commands are functionally equivalent: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-containerregistry-0.16.1/pkg/authn/keychain.go new/go-containerregistry-0.17.0/pkg/authn/keychain.go --- old/go-containerregistry-0.16.1/pkg/authn/keychain.go 2023-08-02 22:59:06.000000000 +0200 +++ new/go-containerregistry-0.17.0/pkg/authn/keychain.go 2023-11-29 22:32:21.000000000 +0100 @@ -53,7 +53,7 @@ var ( // DefaultKeychain implements Keychain by interpreting the docker config file. - DefaultKeychain = RefreshingKeychain(&defaultKeychain{}, 5*time.Minute) + DefaultKeychain = &defaultKeychain{} ) const ( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-containerregistry-0.16.1/pkg/registry/blobs_disk.go new/go-containerregistry-0.17.0/pkg/registry/blobs_disk.go --- old/go-containerregistry-0.16.1/pkg/registry/blobs_disk.go 2023-08-02 22:59:06.000000000 +0200 +++ new/go-containerregistry-0.17.0/pkg/registry/blobs_disk.go 2023-11-29 22:32:21.000000000 +0100 @@ -30,8 +30,12 @@ func NewDiskBlobHandler(dir string) BlobHandler { return &diskHandler{dir: dir} } +func (m *diskHandler) blobHashPath(h v1.Hash) string { + return filepath.Join(m.dir, h.Algorithm, h.Hex) +} + func (m *diskHandler) Stat(_ context.Context, _ string, h v1.Hash) (int64, error) { - fi, err := os.Stat(filepath.Join(m.dir, h.String())) + fi, err := os.Stat(m.blobHashPath(h)) if errors.Is(err, os.ErrNotExist) { return 0, errNotFound } else if err != nil { @@ -40,7 +44,7 @@ return fi.Size(), nil } func (m *diskHandler) Get(_ context.Context, _ string, h v1.Hash) (io.ReadCloser, error) { - return os.Open(filepath.Join(m.dir, h.String())) + return os.Open(m.blobHashPath(h)) } func (m *diskHandler) Put(_ context.Context, _ string, h v1.Hash, rc io.ReadCloser) error { // Put the temp file in the same directory to avoid cross-device problems @@ -57,9 +61,11 @@ }(); err != nil { return err } - - return os.Rename(f.Name(), filepath.Join(m.dir, h.String())) + if err := os.MkdirAll(filepath.Join(m.dir, h.Algorithm), os.ModePerm); err != nil { + return err + } + return os.Rename(f.Name(), m.blobHashPath(h)) } func (m *diskHandler) Delete(_ context.Context, _ string, h v1.Hash) error { - return os.Remove(filepath.Join(m.dir, h.String())) + return os.Remove(m.blobHashPath(h)) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-containerregistry-0.16.1/pkg/registry/blobs_disk_test.go new/go-containerregistry-0.17.0/pkg/registry/blobs_disk_test.go --- old/go-containerregistry-0.16.1/pkg/registry/blobs_disk_test.go 2023-08-02 22:59:06.000000000 +0200 +++ new/go-containerregistry-0.17.0/pkg/registry/blobs_disk_test.go 2023-11-29 22:32:21.000000000 +0100 @@ -15,6 +15,7 @@ package registry_test import ( + "fmt" "net/http/httptest" "os" "path/filepath" @@ -59,7 +60,7 @@ if h, err := img.ConfigName(); err != nil { t.Fatal(err) } else { - want[h.String()] = true + want[fmt.Sprintf("%s/%s", h.Algorithm, h.Hex)] = true } ls, err := img.Layers() if err != nil { @@ -69,7 +70,7 @@ if h, err := l.Digest(); err != nil { t.Fatal(err) } else { - want[h.String()] = true + want[fmt.Sprintf("%s/%s", h.Algorithm, h.Hex)] = true } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-containerregistry-0.16.1/pkg/v1/layout/gc.go new/go-containerregistry-0.17.0/pkg/v1/layout/gc.go --- old/go-containerregistry-0.16.1/pkg/v1/layout/gc.go 1970-01-01 01:00:00.000000000 +0100 +++ new/go-containerregistry-0.17.0/pkg/v1/layout/gc.go 2023-11-29 22:32:21.000000000 +0100 @@ -0,0 +1,137 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This is an EXPERIMENTAL package, and may change in arbitrary ways without notice. +package layout + +import ( + "fmt" + "io/fs" + "path/filepath" + "strings" + + v1 "github.com/google/go-containerregistry/pkg/v1" +) + +// GarbageCollect removes unreferenced blobs from the oci-layout +// +// This is an experimental api, and not subject to any stability guarantees +// We may abandon it at any time, without prior notice. +// Deprecated: Use it at your own risk! +func (l Path) GarbageCollect() ([]v1.Hash, error) { + idx, err := l.ImageIndex() + if err != nil { + return nil, err + } + blobsToKeep := map[string]bool{} + if err := l.garbageCollectImageIndex(idx, blobsToKeep); err != nil { + return nil, err + } + blobsDir := l.path("blobs") + removedBlobs := []v1.Hash{} + + err = filepath.WalkDir(blobsDir, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + + if d.IsDir() { + return nil + } + + rel, err := filepath.Rel(blobsDir, path) + if err != nil { + return err + } + hashString := strings.Replace(rel, "/", ":", 1) + if present := blobsToKeep[hashString]; !present { + h, err := v1.NewHash(hashString) + if err != nil { + return err + } + removedBlobs = append(removedBlobs, h) + } + return nil + }) + + if err != nil { + return nil, err + } + + return removedBlobs, nil +} + +func (l Path) garbageCollectImageIndex(index v1.ImageIndex, blobsToKeep map[string]bool) error { + idxm, err := index.IndexManifest() + if err != nil { + return err + } + + h, err := index.Digest() + if err != nil { + return err + } + + blobsToKeep[h.String()] = true + + for _, descriptor := range idxm.Manifests { + if descriptor.MediaType.IsImage() { + img, err := index.Image(descriptor.Digest) + if err != nil { + return err + } + if err := l.garbageCollectImage(img, blobsToKeep); err != nil { + return err + } + } else if descriptor.MediaType.IsIndex() { + idx, err := index.ImageIndex(descriptor.Digest) + if err != nil { + return err + } + if err := l.garbageCollectImageIndex(idx, blobsToKeep); err != nil { + return err + } + } else { + return fmt.Errorf("gc: unknown media type: %s", descriptor.MediaType) + } + } + return nil +} + +func (l Path) garbageCollectImage(image v1.Image, blobsToKeep map[string]bool) error { + h, err := image.Digest() + if err != nil { + return err + } + blobsToKeep[h.String()] = true + + h, err = image.ConfigName() + if err != nil { + return err + } + blobsToKeep[h.String()] = true + + ls, err := image.Layers() + if err != nil { + return err + } + for _, l := range ls { + h, err := l.Digest() + if err != nil { + return err + } + blobsToKeep[h.String()] = true + } + return nil +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-containerregistry-0.16.1/pkg/v1/layout/gc_test.go new/go-containerregistry-0.17.0/pkg/v1/layout/gc_test.go --- old/go-containerregistry-0.16.1/pkg/v1/layout/gc_test.go 1970-01-01 01:00:00.000000000 +0100 +++ new/go-containerregistry-0.17.0/pkg/v1/layout/gc_test.go 2023-11-29 22:32:21.000000000 +0100 @@ -0,0 +1,96 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package layout + +import ( + "path/filepath" + "testing" +) + +var ( + gcIndexPath = filepath.Join("testdata", "test_gc_index") + gcIndexBlobHash = "sha256:492b89b9dd3cda4596f94916d17f6901455fb8bd7f4c5a2a90df8d39c90f48a0" + gcUnknownMediaTypePath = filepath.Join("testdata", "test_gc_image_unknown_mediatype") + gcUnknownMediaTypeErr = "gc: unknown media type: application/vnd.oci.descriptor.v1+json" + gcTestOneImagePath = filepath.Join("testdata", "test_index_one_image") + gcTestIndexMediaTypePath = filepath.Join("testdata", "test_index_media_type") +) + +func TestGcIndex(t *testing.T) { + lp, err := FromPath(gcIndexPath) + if err != nil { + t.Fatalf("FromPath() = %v", err) + } + + removed, err := lp.GarbageCollect() + if err != nil { + t.Fatalf("GarbageCollect() = %v", err) + } + + if len(removed) != 1 { + t.Fatalf("expected to have only one gc-able blob") + } + if removed[0].String() != gcIndexBlobHash { + t.Fatalf("wrong blob is gc-ed: expected '%s', got '%s'", gcIndexBlobHash, removed[0].String()) + } +} + +func TestGcOneImage(t *testing.T) { + lp, err := FromPath(gcTestOneImagePath) + if err != nil { + t.Fatalf("FromPath() = %v", err) + } + + removed, err := lp.GarbageCollect() + if err != nil { + t.Fatalf("GarbageCollect() = %v", err) + } + + if len(removed) != 0 { + t.Fatalf("expected to have to gc-able blobs") + } +} + +func TestGcIndexMediaType(t *testing.T) { + lp, err := FromPath(gcTestIndexMediaTypePath) + if err != nil { + t.Fatalf("FromPath() = %v", err) + } + + removed, err := lp.GarbageCollect() + if err != nil { + t.Fatalf("GarbageCollect() = %v", err) + } + + if len(removed) != 0 { + t.Fatalf("expected to have to gc-able blobs") + } +} + +func TestGcUnknownMediaType(t *testing.T) { + lp, err := FromPath(gcUnknownMediaTypePath) + if err != nil { + t.Fatalf("FromPath() = %v", err) + } + + _, err = lp.GarbageCollect() + if err == nil { + t.Fatalf("expected GarbageCollect to return err but did not") + } + + if err.Error() != gcUnknownMediaTypeErr { + t.Fatalf("expected error '%s', got '%s'", gcUnknownMediaTypeErr, err.Error()) + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-containerregistry-0.16.1/pkg/v1/layout/testdata/test_gc_image_unknown_mediatype/index.json new/go-containerregistry-0.17.0/pkg/v1/layout/testdata/test_gc_image_unknown_mediatype/index.json --- old/go-containerregistry-0.16.1/pkg/v1/layout/testdata/test_gc_image_unknown_mediatype/index.json 1970-01-01 01:00:00.000000000 +0100 +++ new/go-containerregistry-0.17.0/pkg/v1/layout/testdata/test_gc_image_unknown_mediatype/index.json 2023-11-29 22:32:21.000000000 +0100 @@ -0,0 +1,10 @@ +{ + "schemaVersion": 2, + "manifests": [ + { + "mediaType": "application/vnd.oci.descriptor.v1+json", + "size": 423, + "digest": "sha256:32589985702551b6c56033bb3334432a0a513bf9d6aceda0f67c42b003850720" + } + ] +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-containerregistry-0.16.1/pkg/v1/layout/testdata/test_gc_image_unknown_mediatype/oci-layout new/go-containerregistry-0.17.0/pkg/v1/layout/testdata/test_gc_image_unknown_mediatype/oci-layout --- old/go-containerregistry-0.16.1/pkg/v1/layout/testdata/test_gc_image_unknown_mediatype/oci-layout 1970-01-01 01:00:00.000000000 +0100 +++ new/go-containerregistry-0.17.0/pkg/v1/layout/testdata/test_gc_image_unknown_mediatype/oci-layout 2023-11-29 22:32:21.000000000 +0100 @@ -0,0 +1,3 @@ +{ + "imageLayoutVersion": "1.0.0" +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-containerregistry-0.16.1/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/05f95b26ed10668b7183c1e2da98610e91372fa9f510046d4ce5812addad86b5 new/go-containerregistry-0.17.0/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/05f95b26ed10668b7183c1e2da98610e91372fa9f510046d4ce5812addad86b5 --- old/go-containerregistry-0.16.1/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/05f95b26ed10668b7183c1e2da98610e91372fa9f510046d4ce5812addad86b5 1970-01-01 01:00:00.000000000 +0100 +++ new/go-containerregistry-0.17.0/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/05f95b26ed10668b7183c1e2da98610e91372fa9f510046d4ce5812addad86b5 2023-11-29 22:32:21.000000000 +0100 @@ -0,0 +1,13 @@ +{ + "schemaVersion": 2, + "manifests": [ + { + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "size": 423, + "digest": "sha256:eebff607b1628d67459b0596643fc07de70d702eccf030f0bc7bb6fc2b278650", + "annotations": { + "org.opencontainers.image.ref.name": "1" + } + } + ] +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-containerregistry-0.16.1/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/2b29a2b8dea3af91ea7d0154be1da0c92d55ddd098540930fc8d3db7de377fdb new/go-containerregistry-0.17.0/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/2b29a2b8dea3af91ea7d0154be1da0c92d55ddd098540930fc8d3db7de377fdb --- old/go-containerregistry-0.16.1/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/2b29a2b8dea3af91ea7d0154be1da0c92d55ddd098540930fc8d3db7de377fdb 1970-01-01 01:00:00.000000000 +0100 +++ new/go-containerregistry-0.17.0/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/2b29a2b8dea3af91ea7d0154be1da0c92d55ddd098540930fc8d3db7de377fdb 2023-11-29 22:32:21.000000000 +0100 @@ -0,0 +1,13 @@ +{ + "schemaVersion": 2, + "manifests": [ + { + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "size": 423, + "digest": "sha256:eebff607b1628d67459b0596643fc07de70d702eccf030f0bc7bb6fc2b278650", + "annotations": { + "org.opencontainers.image.ref.name": "4" + } + } + ] +} Binary files old/go-containerregistry-0.16.1/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/492b89b9dd3cda4596f94916d17f6901455fb8bd7f4c5a2a90df8d39c90f48a0 and new/go-containerregistry-0.17.0/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/492b89b9dd3cda4596f94916d17f6901455fb8bd7f4c5a2a90df8d39c90f48a0 differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-containerregistry-0.16.1/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/6e0b05049ed9c17d02e1a55e80d6599dbfcce7f4f4b022e3c673e685789c470e new/go-containerregistry-0.17.0/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/6e0b05049ed9c17d02e1a55e80d6599dbfcce7f4f4b022e3c673e685789c470e --- old/go-containerregistry-0.16.1/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/6e0b05049ed9c17d02e1a55e80d6599dbfcce7f4f4b022e3c673e685789c470e 1970-01-01 01:00:00.000000000 +0100 +++ new/go-containerregistry-0.17.0/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/6e0b05049ed9c17d02e1a55e80d6599dbfcce7f4f4b022e3c673e685789c470e 2023-11-29 22:32:21.000000000 +0100 @@ -0,0 +1 @@ +{"architecture": "amd64", "author": "Bazel", "config": {}, "created": "1970-01-01T00:00:00Z", "history": [{"author": "Bazel", "created": "1970-01-01T00:00:00Z", "created_by": "bazel build ..."}], "os": "linux", "rootfs": {"diff_ids": ["sha256:8897395fd26dc44ad0e2a834335b33198cb41ac4d98dfddf58eced3853fa7b17"], "type": "layers"}} Binary files old/go-containerregistry-0.16.1/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/dc52c6e48a1d51a96047b059f16889bc889c4b4c28f3b36b3f93187f62fc0b2b and new/go-containerregistry-0.17.0/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/dc52c6e48a1d51a96047b059f16889bc889c4b4c28f3b36b3f93187f62fc0b2b differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-containerregistry-0.16.1/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/eebff607b1628d67459b0596643fc07de70d702eccf030f0bc7bb6fc2b278650 new/go-containerregistry-0.17.0/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/eebff607b1628d67459b0596643fc07de70d702eccf030f0bc7bb6fc2b278650 --- old/go-containerregistry-0.16.1/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/eebff607b1628d67459b0596643fc07de70d702eccf030f0bc7bb6fc2b278650 1970-01-01 01:00:00.000000000 +0100 +++ new/go-containerregistry-0.17.0/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/eebff607b1628d67459b0596643fc07de70d702eccf030f0bc7bb6fc2b278650 2023-11-29 22:32:21.000000000 +0100 @@ -0,0 +1 @@ +{"schemaVersion":2,"mediaType":"application/vnd.docker.distribution.manifest.v2+json","config":{"mediaType":"application/vnd.docker.container.image.v1+json","size":330,"digest":"sha256:6e0b05049ed9c17d02e1a55e80d6599dbfcce7f4f4b022e3c673e685789c470e"},"layers":[{"mediaType":"application/vnd.docker.image.rootfs.diff.tar.gzip","size":167,"digest":"sha256:dc52c6e48a1d51a96047b059f16889bc889c4b4c28f3b36b3f93187f62fc0b2b"}]} \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-containerregistry-0.16.1/pkg/v1/layout/testdata/test_gc_index/index.json new/go-containerregistry-0.17.0/pkg/v1/layout/testdata/test_gc_index/index.json --- old/go-containerregistry-0.16.1/pkg/v1/layout/testdata/test_gc_index/index.json 1970-01-01 01:00:00.000000000 +0100 +++ new/go-containerregistry-0.17.0/pkg/v1/layout/testdata/test_gc_index/index.json 2023-11-29 22:32:21.000000000 +0100 @@ -0,0 +1,29 @@ +{ + "schemaVersion": 2, + "manifests": [ + { + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "size": 423, + "digest": "sha256:eebff607b1628d67459b0596643fc07de70d702eccf030f0bc7bb6fc2b278650", + "annotations": { + "org.opencontainers.image.ref.name": "1" + } + }, + { + "mediaType": "application/vnd.oci.image.index.v1+json", + "size": 314, + "digest": "sha256:05f95b26ed10668b7183c1e2da98610e91372fa9f510046d4ce5812addad86b5", + "annotations": { + "org.opencontainers.image.ref.name": "3" + } + }, + { + "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json", + "size": 314, + "digest": "sha256:2b29a2b8dea3af91ea7d0154be1da0c92d55ddd098540930fc8d3db7de377fdb", + "annotations": { + "org.opencontainers.image.ref.name": "4" + } + } + ] +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-containerregistry-0.16.1/pkg/v1/layout/testdata/test_gc_index/oci-layout new/go-containerregistry-0.17.0/pkg/v1/layout/testdata/test_gc_index/oci-layout --- old/go-containerregistry-0.16.1/pkg/v1/layout/testdata/test_gc_index/oci-layout 1970-01-01 01:00:00.000000000 +0100 +++ new/go-containerregistry-0.17.0/pkg/v1/layout/testdata/test_gc_index/oci-layout 2023-11-29 22:32:21.000000000 +0100 @@ -0,0 +1,3 @@ +{ + "imageLayoutVersion": "1.0.0" +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-containerregistry-0.16.1/pkg/v1/remote/write.go new/go-containerregistry-0.17.0/pkg/v1/remote/write.go --- old/go-containerregistry-0.16.1/pkg/v1/remote/write.go 2023-08-02 22:59:06.000000000 +0200 +++ new/go-containerregistry-0.17.0/pkg/v1/remote/write.go 2023-11-29 22:32:21.000000000 +0100 @@ -280,6 +280,11 @@ if _, ok := layer.(*stream.Layer); !ok { // We can't retry streaming layers. req.GetBody = getBody + + // If we know the size, set it. + if size, err := layer.Size(); err == nil { + req.ContentLength = size + } } req.Header.Set("Content-Type", "application/octet-stream") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-containerregistry-0.16.1/pkg/v1/validate/index.go new/go-containerregistry-0.17.0/pkg/v1/validate/index.go --- old/go-containerregistry-0.16.1/pkg/v1/validate/index.go 2023-08-02 22:59:06.000000000 +0200 +++ new/go-containerregistry-0.17.0/pkg/v1/validate/index.go 2023-11-29 22:32:21.000000000 +0100 @@ -79,6 +79,9 @@ if err := validateMediaType(img, desc.MediaType); err != nil { errs = append(errs, fmt.Sprintf("failed to validate image MediaType[%d](%s): %v", i, desc.Digest, err)) } + if err := validatePlatform(img, desc.Platform); err != nil { + errs = append(errs, fmt.Sprintf("failed to validate image platform[%d](%s): %v", i, desc.Digest, err)) + } default: // Workaround for #819. if wl, ok := idx.(withLayer); ok { @@ -173,3 +176,54 @@ return nil } + +func validatePlatform(img v1.Image, want *v1.Platform) error { + if want == nil { + return nil + } + + cf, err := img.ConfigFile() + if err != nil { + return err + } + + got := cf.Platform() + + if got == nil { + return fmt.Errorf("config file missing platform fields") + } + + if got.Equals(*want) { + return nil + } + + errs := []string{} + + if got.OS != want.OS { + errs = append(errs, fmt.Sprintf("mismatched OS: %s != %s", got.OS, want.OS)) + } + + if got.Architecture != want.Architecture { + errs = append(errs, fmt.Sprintf("mismatched Architecture: %s != %s", got.Architecture, want.Architecture)) + } + + if got.OSVersion != want.OSVersion { + errs = append(errs, fmt.Sprintf("mismatched OSVersion: %s != %s", got.OSVersion, want.OSVersion)) + } + + if got.OSVersion != want.OSVersion { + errs = append(errs, fmt.Sprintf("mismatched OSVersion: %s != %s", got.OSVersion, want.OSVersion)) + } + + if len(errs) == 0 { + // If we got here, some features might be mismatched. Just add those... + if len(got.Features) != 0 || len(want.Features) != 0 { + errs = append(errs, fmt.Sprintf("mismatched Features: %v, %v", got.Features, want.Features)) + } + if len(got.OSFeatures) != 0 || len(want.OSFeatures) != 0 { + errs = append(errs, fmt.Sprintf("mismatched OSFeatures: %v, %v", got.OSFeatures, want.OSFeatures)) + } + } + + return errors.New(strings.Join(errs, "\n")) +} ++++++ vendor.tar.gz ++++++