From 169808e09f59591fab9a620252cf185cde31314e Mon Sep 17 00:00:00 2001 From: thesayyn Date: Wed, 11 Oct 2023 17:41:29 -0400 Subject: [PATCH 1/7] feat: implement prune flag --- cmd/crane/cmd/pull.go | 14 +++++-- pkg/v1/layout/write.go | 95 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 3 deletions(-) diff --git a/cmd/crane/cmd/pull.go b/cmd/crane/cmd/pull.go index 41c6e95cd..1879b8d70 100644 --- a/cmd/crane/cmd/pull.go +++ b/cmd/crane/cmd/pull.go @@ -30,8 +30,8 @@ import ( // NewCmdPull creates a new cobra.Command for the pull subcommand. func NewCmdPull(options *[]crane.Option) *cobra.Command { var ( - cachePath, format string - annotateRef bool + cachePath, format string + annotateRef, prune bool ) cmd := &cobra.Command{ @@ -124,6 +124,13 @@ func NewCmdPull(options *[]crane.Option) *cobra.Command { return err } } + + if prune { + if err := p.GarbageCollect(); err != nil { + return err + } + } + default: return fmt.Errorf("unexpected --format: %q (valid values are: tarball, legacy, and oci)", format) } @@ -133,6 +140,7 @@ func NewCmdPull(options *[]crane.Option) *cobra.Command { cmd.Flags().StringVarP(&cachePath, "cache_path", "c", "", "Path to cache image layers") cmd.Flags().StringVar(&format, "format", "tarball", fmt.Sprintf("Format in which to save images (%q, %q, or %q)", "tarball", "legacy", "oci")) cmd.Flags().BoolVar(&annotateRef, "annotate-ref", false, "Preserves image reference used to pull as an annotation when used with --format=oci") - + cmd.Flags().BoolVar(&prune, "prune", false, "Removes orphan blobs from the oci-layout after pull") + cmd.Flags().MarkHidden("prune") return cmd } diff --git a/pkg/v1/layout/write.go b/pkg/v1/layout/write.go index d6e35c391..f3f7f2b91 100644 --- a/pkg/v1/layout/write.go +++ b/pkg/v1/layout/write.go @@ -20,6 +20,7 @@ import ( "errors" "fmt" "io" + "io/fs" "os" "path/filepath" @@ -37,6 +38,100 @@ var layoutFile = `{ "imageLayoutVersion": "1.0.0" }` +// GarbageCollect removes unreferenced blobs from the oci-layout +func (l Path) GarbageCollect() error { + idx, err := l.ImageIndex() + if err != nil { + return err + } + blobsToKeep := map[string]bool{} + if err := l.garbageCollectImageIndex(idx, blobsToKeep); err != nil { + return err + } + blobsDir := l.path("blobs") + + if err := filepath.WalkDir(blobsDir, func(path string, d fs.DirEntry, err error) error { + if d.IsDir() { + return nil + } + + rel, err := filepath.Rel(blobsDir, path) + if err != nil { + return err + } + if ok := blobsToKeep[rel]; !ok { + if err := os.Remove(path); err != nil { + return err + } + } + return nil + }); err != nil { + return err + } + + return nil +} + +func (l Path) garbageCollectImageIndex(index v1.ImageIndex, blobsToKeep map[string]bool) error { + idxm, err := index.IndexManifest() + if err != nil { + return err + } + if h, err := index.Digest(); err != nil { + return err + } else { + blobsToKeep[fmt.Sprintf("%s/%s", h.Algorithm, h.Hex)] = 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 + } + } + } + return nil +} + +func (l Path) garbageCollectImage(image v1.Image, blobsToKeep map[string]bool) error { + + if h, err := image.Digest(); err != nil { + return err + } else { + blobsToKeep[fmt.Sprintf("%s/%s", h.Algorithm, h.Hex)] = true + } + + if h, err := image.ConfigName(); err != nil { + return err + } else { + blobsToKeep[fmt.Sprintf("%s/%s", h.Algorithm, h.Hex)] = true + } + + ls, err := image.Layers() + if err != nil { + return err + } + for _, l := range ls { + if h, err := l.Digest(); err != nil { + return err + } else { + blobsToKeep[fmt.Sprintf("%s/%s", h.Algorithm, h.Hex)] = true + } + } + return nil +} + // AppendImage writes a v1.Image to the Path and updates // the index.json to reference it. func (l Path) AppendImage(img v1.Image, options ...Option) error { From 72a54709685b6c59d1ebf25b316e1b2c2bddf553 Mon Sep 17 00:00:00 2001 From: thesayyn Date: Tue, 14 Nov 2023 12:31:39 -0800 Subject: [PATCH 2/7] address changes --- cmd/crane/cmd/gc.go | 58 +++++++++ cmd/crane/cmd/pull.go | 14 +- cmd/crane/cmd/root.go | 3 +- cmd/crane/cmd/serve.go | 2 +- pkg/v1/layout/gc.go | 123 ++++++++++++++++++ pkg/v1/layout/gc_test.go | 82 ++++++++++++ .../index.json | 10 ++ .../oci-layout | 3 + ...e2da98610e91372fa9f510046d4ce5812addad86b5 | 13 ++ ...54be1da0c92d55ddd098540930fc8d3db7de377fdb | 13 ++ ...16d17f6901455fb8bd7f4c5a2a90df8d39c90f48a0 | Bin 0 -> 114 bytes ...e80d6599dbfcce7f4f4b022e3c673e685789c470e} | 2 +- ...59f16889bc889c4b4c28f3b36b3f93187f62fc0b2b | Bin 0 -> 167 bytes ...96643fc07de70d702eccf030f0bc7bb6fc2b278650 | 1 + .../layout/testdata/test_gc_index/index.json | 29 +++++ .../layout/testdata/test_gc_index/oci-layout | 3 + ...2d04b7aff249f4ed960d43404a9f699886906cc9d3 | Bin 165 -> 0 bytes ...bb3334432a0a513bf9d6aceda0f67c42b003850720 | 1 - pkg/v1/layout/write.go | 95 -------------- 19 files changed, 342 insertions(+), 110 deletions(-) create mode 100644 cmd/crane/cmd/gc.go create mode 100644 pkg/v1/layout/gc.go create mode 100644 pkg/v1/layout/gc_test.go create mode 100644 pkg/v1/layout/testdata/test_gc_image_unknown_mediatype/index.json create mode 100644 pkg/v1/layout/testdata/test_gc_image_unknown_mediatype/oci-layout create mode 100644 pkg/v1/layout/testdata/test_gc_index/blobs/sha256/05f95b26ed10668b7183c1e2da98610e91372fa9f510046d4ce5812addad86b5 create mode 100644 pkg/v1/layout/testdata/test_gc_index/blobs/sha256/2b29a2b8dea3af91ea7d0154be1da0c92d55ddd098540930fc8d3db7de377fdb create mode 100644 pkg/v1/layout/testdata/test_gc_index/blobs/sha256/492b89b9dd3cda4596f94916d17f6901455fb8bd7f4c5a2a90df8d39c90f48a0 rename pkg/v1/layout/testdata/{test_index/blobs/sha256/930705ce23e3b6ed4c08746b6fe880089c864fbaf62482702ae3fdd66b8c7fe9 => test_gc_index/blobs/sha256/6e0b05049ed9c17d02e1a55e80d6599dbfcce7f4f4b022e3c673e685789c470e} (61%) create mode 100644 pkg/v1/layout/testdata/test_gc_index/blobs/sha256/dc52c6e48a1d51a96047b059f16889bc889c4b4c28f3b36b3f93187f62fc0b2b create mode 100644 pkg/v1/layout/testdata/test_gc_index/blobs/sha256/eebff607b1628d67459b0596643fc07de70d702eccf030f0bc7bb6fc2b278650 create mode 100644 pkg/v1/layout/testdata/test_gc_index/index.json create mode 100644 pkg/v1/layout/testdata/test_gc_index/oci-layout delete mode 100644 pkg/v1/layout/testdata/test_index/blobs/sha256/321460fa87fd42433950b42d04b7aff249f4ed960d43404a9f699886906cc9d3 delete mode 100644 pkg/v1/layout/testdata/test_index/blobs/sha256/32589985702551b6c56033bb3334432a0a513bf9d6aceda0f67c42b003850720 diff --git a/cmd/crane/cmd/gc.go b/cmd/crane/cmd/gc.go new file mode 100644 index 000000000..63b563916 --- /dev/null +++ b/cmd/crane/cmd/gc.go @@ -0,0 +1,58 @@ +// 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" +) + +// 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 --git a/cmd/crane/cmd/pull.go b/cmd/crane/cmd/pull.go index 1879b8d70..41c6e95cd 100644 --- a/cmd/crane/cmd/pull.go +++ b/cmd/crane/cmd/pull.go @@ -30,8 +30,8 @@ import ( // NewCmdPull creates a new cobra.Command for the pull subcommand. func NewCmdPull(options *[]crane.Option) *cobra.Command { var ( - cachePath, format string - annotateRef, prune bool + cachePath, format string + annotateRef bool ) cmd := &cobra.Command{ @@ -124,13 +124,6 @@ func NewCmdPull(options *[]crane.Option) *cobra.Command { return err } } - - if prune { - if err := p.GarbageCollect(); err != nil { - return err - } - } - default: return fmt.Errorf("unexpected --format: %q (valid values are: tarball, legacy, and oci)", format) } @@ -140,7 +133,6 @@ func NewCmdPull(options *[]crane.Option) *cobra.Command { cmd.Flags().StringVarP(&cachePath, "cache_path", "c", "", "Path to cache image layers") cmd.Flags().StringVar(&format, "format", "tarball", fmt.Sprintf("Format in which to save images (%q, %q, or %q)", "tarball", "legacy", "oci")) cmd.Flags().BoolVar(&annotateRef, "annotate-ref", false, "Preserves image reference used to pull as an annotation when used with --format=oci") - cmd.Flags().BoolVar(&prune, "prune", false, "Removes orphan blobs from the oci-layout after pull") - cmd.Flags().MarkHidden("prune") + return cmd } diff --git a/cmd/crane/cmd/root.go b/cmd/crane/cmd/root.go index 3faa85f5f..490439600 100644 --- a/cmd/crane/cmd/root.go +++ b/cmd/crane/cmd/root.go @@ -129,7 +129,8 @@ func New(use, short string, options []crane.Option) *cobra.Command { NewCmdTag(&options), NewCmdValidate(&options), NewCmdVersion(), - newCmdRegistry(), + NewCmdRegistry(), + NewCmdGc(), ) root.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Enable debug logs") diff --git a/cmd/crane/cmd/serve.go b/cmd/crane/cmd/serve.go index 61d031b39..5256c3714 100644 --- a/cmd/crane/cmd/serve.go +++ b/cmd/crane/cmd/serve.go @@ -28,7 +28,7 @@ import ( "github.com/google/go-containerregistry/pkg/registry" ) -func newCmdRegistry() *cobra.Command { +func NewCmdRegistry() *cobra.Command { cmd := &cobra.Command{ Use: "registry", } diff --git a/pkg/v1/layout/gc.go b/pkg/v1/layout/gc.go new file mode 100644 index 000000000..51cfef75a --- /dev/null +++ b/pkg/v1/layout/gc.go @@ -0,0 +1,123 @@ +// 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 --git a/pkg/v1/layout/gc_test.go b/pkg/v1/layout/gc_test.go new file mode 100644 index 000000000..94c1ea147 --- /dev/null +++ b/pkg/v1/layout/gc_test.go @@ -0,0 +1,82 @@ +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 --git a/pkg/v1/layout/testdata/test_gc_image_unknown_mediatype/index.json b/pkg/v1/layout/testdata/test_gc_image_unknown_mediatype/index.json new file mode 100644 index 000000000..7a8c41040 --- /dev/null +++ b/pkg/v1/layout/testdata/test_gc_image_unknown_mediatype/index.json @@ -0,0 +1,10 @@ +{ + "schemaVersion": 2, + "manifests": [ + { + "mediaType": "application/vnd.oci.descriptor.v1+json", + "size": 423, + "digest": "sha256:32589985702551b6c56033bb3334432a0a513bf9d6aceda0f67c42b003850720" + } + ] +} diff --git a/pkg/v1/layout/testdata/test_gc_image_unknown_mediatype/oci-layout b/pkg/v1/layout/testdata/test_gc_image_unknown_mediatype/oci-layout new file mode 100644 index 000000000..10ff2f3ce --- /dev/null +++ b/pkg/v1/layout/testdata/test_gc_image_unknown_mediatype/oci-layout @@ -0,0 +1,3 @@ +{ + "imageLayoutVersion": "1.0.0" +} diff --git a/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/05f95b26ed10668b7183c1e2da98610e91372fa9f510046d4ce5812addad86b5 b/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/05f95b26ed10668b7183c1e2da98610e91372fa9f510046d4ce5812addad86b5 new file mode 100644 index 000000000..1597d0721 --- /dev/null +++ b/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/05f95b26ed10668b7183c1e2da98610e91372fa9f510046d4ce5812addad86b5 @@ -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 --git a/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/2b29a2b8dea3af91ea7d0154be1da0c92d55ddd098540930fc8d3db7de377fdb b/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/2b29a2b8dea3af91ea7d0154be1da0c92d55ddd098540930fc8d3db7de377fdb new file mode 100644 index 000000000..e6587e23e --- /dev/null +++ b/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/2b29a2b8dea3af91ea7d0154be1da0c92d55ddd098540930fc8d3db7de377fdb @@ -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" + } + } + ] +} diff --git a/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/492b89b9dd3cda4596f94916d17f6901455fb8bd7f4c5a2a90df8d39c90f48a0 b/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/492b89b9dd3cda4596f94916d17f6901455fb8bd7f4c5a2a90df8d39c90f48a0 new file mode 100644 index 0000000000000000000000000000000000000000..1e4eb2219a345965f598b778a9a8ac6ba4acabd1 GIT binary patch literal 114 zcmV-&0FD12iwFP!32ul0|K!m@3WY!j24L6k6z>SXjoNuybb-e1A47|V*~UQde;W}+ z5p%C8lR<$n6WqoKz(q@#`MPL{*01jJM?Ykiv*vaPUhf)?>PuhN{{MSYa^%R7Bgg3i U009600RRC1|H~9E0ssgA0Hg;rZ~y=R literal 0 HcmV?d00001 diff --git a/pkg/v1/layout/testdata/test_index/blobs/sha256/930705ce23e3b6ed4c08746b6fe880089c864fbaf62482702ae3fdd66b8c7fe9 b/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/6e0b05049ed9c17d02e1a55e80d6599dbfcce7f4f4b022e3c673e685789c470e similarity index 61% rename from pkg/v1/layout/testdata/test_index/blobs/sha256/930705ce23e3b6ed4c08746b6fe880089c864fbaf62482702ae3fdd66b8c7fe9 rename to pkg/v1/layout/testdata/test_gc_index/blobs/sha256/6e0b05049ed9c17d02e1a55e80d6599dbfcce7f4f4b022e3c673e685789c470e index 425c2d0b2..4228c8902 100644 --- a/pkg/v1/layout/testdata/test_index/blobs/sha256/930705ce23e3b6ed4c08746b6fe880089c864fbaf62482702ae3fdd66b8c7fe9 +++ b/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/6e0b05049ed9c17d02e1a55e80d6599dbfcce7f4f4b022e3c673e685789c470e @@ -1 +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:3610aa5267a210147ba6ca02cdd87610dfc08522de9c5f5015edd8ee14853fd8"], "type": "layers"}} +{"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"}} diff --git a/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/dc52c6e48a1d51a96047b059f16889bc889c4b4c28f3b36b3f93187f62fc0b2b b/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/dc52c6e48a1d51a96047b059f16889bc889c4b4c28f3b36b3f93187f62fc0b2b new file mode 100644 index 0000000000000000000000000000000000000000..05c63217be53006ff9116a7f1756afe20e3b2390 GIT binary patch literal 167 zcmb2|=3oE;mj7=qJ8~T|5NLfES=}{fLG7($bJSx?q&uqWXD(3CW0}aY^#68G&B#4x zKD1rjHF=3hvbE~o`R{(7pE*TsuFjEnC(pbo+xbkYss7)Q$@}f{l-mkVZY>U*@BL@T z|DAU$Km1+(ImdBsv8na_|C3%XJj=M-q$gW*Zv4Bw|5p8X_Z0s7zkRF7FMhB~z{G)L TcbOR&{{PQ;A{NV_!N33j2r5x< literal 0 HcmV?d00001 diff --git a/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/eebff607b1628d67459b0596643fc07de70d702eccf030f0bc7bb6fc2b278650 b/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/eebff607b1628d67459b0596643fc07de70d702eccf030f0bc7bb6fc2b278650 new file mode 100644 index 000000000..21dc412c3 --- /dev/null +++ b/pkg/v1/layout/testdata/test_gc_index/blobs/sha256/eebff607b1628d67459b0596643fc07de70d702eccf030f0bc7bb6fc2b278650 @@ -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 --git a/pkg/v1/layout/testdata/test_gc_index/index.json b/pkg/v1/layout/testdata/test_gc_index/index.json new file mode 100644 index 000000000..9b6576c02 --- /dev/null +++ b/pkg/v1/layout/testdata/test_gc_index/index.json @@ -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 --git a/pkg/v1/layout/testdata/test_gc_index/oci-layout b/pkg/v1/layout/testdata/test_gc_index/oci-layout new file mode 100644 index 000000000..10ff2f3ce --- /dev/null +++ b/pkg/v1/layout/testdata/test_gc_index/oci-layout @@ -0,0 +1,3 @@ +{ + "imageLayoutVersion": "1.0.0" +} diff --git a/pkg/v1/layout/testdata/test_index/blobs/sha256/321460fa87fd42433950b42d04b7aff249f4ed960d43404a9f699886906cc9d3 b/pkg/v1/layout/testdata/test_index/blobs/sha256/321460fa87fd42433950b42d04b7aff249f4ed960d43404a9f699886906cc9d3 deleted file mode 100644 index 096f21fb72d922ab8acc6f7b162a71bb90a1dc8f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 165 zcmb2|=3oE;mj7=qJDxgZAaL|!Wc~6JoVSjrU!J3G*>ud;Utd_{NUMU8)64(cJvAfu zocXZq<{{6Zla+HO#rc2#@!UsC{crTChbPaxse1W!&ZPRfqm%9J^VFB^yec>6>hnqW z-_=W&zpMG58mp^malPQf-|e^lACoEYxc6W8$J2YS{eQ=c{rb Date: Tue, 14 Nov 2023 13:15:38 -0800 Subject: [PATCH 3/7] revert --- ...50b42d04b7aff249f4ed960d43404a9f699886906cc9d3 | Bin 0 -> 165 bytes ...6033bb3334432a0a513bf9d6aceda0f67c42b003850720 | 1 + ...08746b6fe880089c864fbaf62482702ae3fdd66b8c7fe9 | 1 + 3 files changed, 2 insertions(+) create mode 100644 pkg/v1/layout/testdata/test_index/blobs/sha256/321460fa87fd42433950b42d04b7aff249f4ed960d43404a9f699886906cc9d3 create mode 100644 pkg/v1/layout/testdata/test_index/blobs/sha256/32589985702551b6c56033bb3334432a0a513bf9d6aceda0f67c42b003850720 create mode 100644 pkg/v1/layout/testdata/test_index/blobs/sha256/930705ce23e3b6ed4c08746b6fe880089c864fbaf62482702ae3fdd66b8c7fe9 diff --git a/pkg/v1/layout/testdata/test_index/blobs/sha256/321460fa87fd42433950b42d04b7aff249f4ed960d43404a9f699886906cc9d3 b/pkg/v1/layout/testdata/test_index/blobs/sha256/321460fa87fd42433950b42d04b7aff249f4ed960d43404a9f699886906cc9d3 new file mode 100644 index 0000000000000000000000000000000000000000..096f21fb72d922ab8acc6f7b162a71bb90a1dc8f GIT binary patch literal 165 zcmb2|=3oE;mj7=qJDxgZAaL|!Wc~6JoVSjrU!J3G*>ud;Utd_{NUMU8)64(cJvAfu zocXZq<{{6Zla+HO#rc2#@!UsC{crTChbPaxse1W!&ZPRfqm%9J^VFB^yec>6>hnqW z-_=W&zpMG58mp^malPQf-|e^lACoEYxc6W8$J2YS{eQ=c{rb Date: Tue, 14 Nov 2023 13:18:05 -0800 Subject: [PATCH 4/7] boilerplate --- pkg/v1/layout/gc.go | 14 ++++++++++++++ pkg/v1/layout/gc_test.go | 13 +++++++++++++ 2 files changed, 27 insertions(+) diff --git a/pkg/v1/layout/gc.go b/pkg/v1/layout/gc.go index 51cfef75a..5fdb2c05d 100644 --- a/pkg/v1/layout/gc.go +++ b/pkg/v1/layout/gc.go @@ -1,3 +1,17 @@ +// 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 diff --git a/pkg/v1/layout/gc_test.go b/pkg/v1/layout/gc_test.go index 94c1ea147..a7a1fabb5 100644 --- a/pkg/v1/layout/gc_test.go +++ b/pkg/v1/layout/gc_test.go @@ -1,3 +1,16 @@ +// 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 ( From 26194ac28413fa2df665d360546b3c0663af709e Mon Sep 17 00:00:00 2001 From: thesayyn Date: Tue, 21 Nov 2023 16:11:07 -0800 Subject: [PATCH 5/7] rename --- cmd/crane/cmd/gc.go | 10 +++++++++- cmd/crane/cmd/root.go | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/cmd/crane/cmd/gc.go b/cmd/crane/cmd/gc.go index 63b563916..a80f14843 100644 --- a/cmd/crane/cmd/gc.go +++ b/cmd/crane/cmd/gc.go @@ -22,8 +22,16 @@ import ( "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 { +func newCmdGc() *cobra.Command { cmd := &cobra.Command{ Use: "gc OCI-LAYOUT", Short: "Garbage collect unreferenced blobs in a local oci-layout", diff --git a/cmd/crane/cmd/root.go b/cmd/crane/cmd/root.go index 490439600..8fc8ccefa 100644 --- a/cmd/crane/cmd/root.go +++ b/cmd/crane/cmd/root.go @@ -130,7 +130,7 @@ func New(use, short string, options []crane.Option) *cobra.Command { NewCmdValidate(&options), NewCmdVersion(), NewCmdRegistry(), - NewCmdGc(), + NewCmdLayout(), ) root.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Enable debug logs") From fb8638f80d7f0bc68f8ab9b7fcda084eb4de7d2a Mon Sep 17 00:00:00 2001 From: thesayyn Date: Wed, 29 Nov 2023 11:49:54 -0800 Subject: [PATCH 6/7] boilerplate --- pkg/v1/layout/gc_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/v1/layout/gc_test.go b/pkg/v1/layout/gc_test.go index a7a1fabb5..c80a2dc42 100644 --- a/pkg/v1/layout/gc_test.go +++ b/pkg/v1/layout/gc_test.go @@ -11,6 +11,7 @@ // 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 ( From 6746d7f7b2647912e5ba1f7ebe559fe6c7558e74 Mon Sep 17 00:00:00 2001 From: Jason Hall Date: Wed, 29 Nov 2023 15:08:04 -0500 Subject: [PATCH 7/7] Update .gitattributes --- .gitattributes | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitattributes b/.gitattributes index e4fb95146..babd135fd 100644 --- a/.gitattributes +++ b/.gitattributes @@ -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