Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement gc command #1811

Merged
merged 7 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions cmd/crane/cmd/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
thesayyn marked this conversation as resolved.
Show resolved Hide resolved
)

cmd := &cobra.Command{
Expand Down Expand Up @@ -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)
}
Expand All @@ -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
}
95 changes: 95 additions & 0 deletions pkg/v1/layout/write.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"errors"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"

Expand All @@ -37,6 +38,100 @@
"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 {

Check failure on line 53 in pkg/v1/layout/write.go

View workflow job for this annotation

GitHub Actions / Presubmit

argument err is overwritten before first use (SA4009)

Check failure on line 53 in pkg/v1/layout/write.go

View workflow job for this annotation

GitHub Actions / Lint

SA4009: argument err is overwritten before first use (staticcheck)
if d.IsDir() {
return nil
}

rel, err := filepath.Rel(blobsDir, path)

Check failure on line 58 in pkg/v1/layout/write.go

View workflow job for this annotation

GitHub Actions / Presubmit

assignment to err

Check failure on line 58 in pkg/v1/layout/write.go

View workflow job for this annotation

GitHub Actions / Lint

SA4009(related information): assignment to err (staticcheck)
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 {

Check warning on line 82 in pkg/v1/layout/write.go

View workflow job for this annotation

GitHub Actions / Lint

indent-error-flow: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) (revive)
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 {

Check failure on line 107 in pkg/v1/layout/write.go

View workflow job for this annotation

GitHub Actions / Lint

unnecessary leading newline (whitespace)

if h, err := image.Digest(); err != nil {
return err
} else {

Check warning on line 111 in pkg/v1/layout/write.go

View workflow job for this annotation

GitHub Actions / Lint

indent-error-flow: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) (revive)
blobsToKeep[fmt.Sprintf("%s/%s", h.Algorithm, h.Hex)] = true
}

if h, err := image.ConfigName(); err != nil {
return err
} else {

Check warning on line 117 in pkg/v1/layout/write.go

View workflow job for this annotation

GitHub Actions / Lint

indent-error-flow: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) (revive)
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 {
Expand Down
Loading