Skip to content

Commit

Permalink
feat: implement prune flag
Browse files Browse the repository at this point in the history
  • Loading branch information
thesayyn committed Oct 11, 2023
1 parent dbcd01c commit 169808e
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 3 deletions.
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
)

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 @@ import (
"errors"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"

Expand All @@ -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 {

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

0 comments on commit 169808e

Please sign in to comment.