registry/storage: add option to quiet GC output.

Consumers might not want GC output to be displayed (e.g, if you have
your own logging system).

Signed-off-by: Rafael Fonseca <r4f4rfs@gmail.com>
This commit is contained in:
Rafael Fonseca 2025-01-31 17:04:09 +01:00
parent 7271d882c0
commit a032989bf9
2 changed files with 31 additions and 10 deletions

View File

@ -18,6 +18,7 @@ func init() {
RootCmd.AddCommand(GCCmd) RootCmd.AddCommand(GCCmd)
GCCmd.Flags().BoolVarP(&dryRun, "dry-run", "d", false, "do everything except remove the blobs") GCCmd.Flags().BoolVarP(&dryRun, "dry-run", "d", false, "do everything except remove the blobs")
GCCmd.Flags().BoolVarP(&removeUntagged, "delete-untagged", "m", false, "delete manifests that are not currently referenced via tag") GCCmd.Flags().BoolVarP(&removeUntagged, "delete-untagged", "m", false, "delete manifests that are not currently referenced via tag")
GCCmd.Flags().BoolVarP(&quiet, "quiet", "q", false, "silence output")
RootCmd.Flags().BoolVarP(&showVersion, "version", "v", false, "show the version and exit") RootCmd.Flags().BoolVarP(&showVersion, "version", "v", false, "show the version and exit")
} }
@ -39,6 +40,7 @@ var RootCmd = &cobra.Command{
var ( var (
dryRun bool dryRun bool
removeUntagged bool removeUntagged bool
quiet bool
) )
// GCCmd is the cobra command that corresponds to the garbage-collect subcommand // GCCmd is the cobra command that corresponds to the garbage-collect subcommand
@ -77,6 +79,7 @@ var GCCmd = &cobra.Command{
err = storage.MarkAndSweep(ctx, driver, registry, storage.GCOpts{ err = storage.MarkAndSweep(ctx, driver, registry, storage.GCOpts{
DryRun: dryRun, DryRun: dryRun,
RemoveUntagged: removeUntagged, RemoveUntagged: removeUntagged,
Quiet: quiet,
}) })
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "failed to garbage collect: %v", err) fmt.Fprintf(os.Stderr, "failed to garbage collect: %v", err)

View File

@ -20,6 +20,7 @@ func emit(format string, a ...interface{}) {
type GCOpts struct { type GCOpts struct {
DryRun bool DryRun bool
RemoveUntagged bool RemoveUntagged bool
Quiet bool
} }
// ManifestDel contains manifest structure which will be deleted // ManifestDel contains manifest structure which will be deleted
@ -41,7 +42,9 @@ func MarkAndSweep(ctx context.Context, storageDriver driver.StorageDriver, regis
deleteLayerSet := make(map[string][]digest.Digest) deleteLayerSet := make(map[string][]digest.Digest)
manifestArr := make([]ManifestDel, 0) manifestArr := make([]ManifestDel, 0)
err := repositoryEnumerator.Enumerate(ctx, func(repoName string) error { err := repositoryEnumerator.Enumerate(ctx, func(repoName string) error {
emit(repoName) if !opts.Quiet {
emit(repoName)
}
var err error var err error
named, err := reference.WithName(repoName) named, err := reference.WithName(repoName)
@ -77,7 +80,9 @@ func MarkAndSweep(ctx context.Context, storageDriver driver.StorageDriver, regis
allTags, err := repository.Tags(ctx).All(ctx) allTags, err := repository.Tags(ctx).All(ctx)
if err != nil { if err != nil {
if _, ok := err.(distribution.ErrRepositoryUnknown); ok { if _, ok := err.(distribution.ErrRepositoryUnknown); ok {
emit("manifest tags path of repository %s does not exist", repoName) if !opts.Quiet {
emit("manifest tags path of repository %s does not exist", repoName)
}
return nil return nil
} }
return fmt.Errorf("failed to retrieve tags %v", err) return fmt.Errorf("failed to retrieve tags %v", err)
@ -87,14 +92,18 @@ func MarkAndSweep(ctx context.Context, storageDriver driver.StorageDriver, regis
} }
} }
// Mark the manifest's blob // Mark the manifest's blob
emit("%s: marking manifest %s ", repoName, dgst) if !opts.Quiet {
emit("%s: marking manifest %s ", repoName, dgst)
}
markSet[dgst] = struct{}{} markSet[dgst] = struct{}{}
return markManifestReferences(dgst, manifestService, ctx, func(d digest.Digest) bool { return markManifestReferences(dgst, manifestService, ctx, func(d digest.Digest) bool {
_, marked := markSet[d] _, marked := markSet[d]
if !marked { if !marked {
markSet[d] = struct{}{} markSet[d] = struct{}{}
emit("%s: marking blob %s", repoName, d) if !opts.Quiet {
emit("%s: marking blob %s", repoName, d)
}
} }
return marked return marked
}) })
@ -132,7 +141,7 @@ func MarkAndSweep(ctx context.Context, storageDriver driver.StorageDriver, regis
return fmt.Errorf("failed to mark: %v", err) return fmt.Errorf("failed to mark: %v", err)
} }
manifestArr = unmarkReferencedManifest(manifestArr, markSet) manifestArr = unmarkReferencedManifest(manifestArr, markSet, opts.Quiet)
// sweep // sweep
vacuum := NewVacuum(ctx, storageDriver) vacuum := NewVacuum(ctx, storageDriver)
@ -156,9 +165,13 @@ func MarkAndSweep(ctx context.Context, storageDriver driver.StorageDriver, regis
if err != nil { if err != nil {
return fmt.Errorf("error enumerating blobs: %v", err) return fmt.Errorf("error enumerating blobs: %v", err)
} }
emit("\n%d blobs marked, %d blobs and %d manifests eligible for deletion", len(markSet), len(deleteSet), len(manifestArr)) if !opts.Quiet {
emit("\n%d blobs marked, %d blobs and %d manifests eligible for deletion", len(markSet), len(deleteSet), len(manifestArr))
}
for dgst := range deleteSet { for dgst := range deleteSet {
emit("blob eligible for deletion: %s", dgst) if !opts.Quiet {
emit("blob eligible for deletion: %s", dgst)
}
if opts.DryRun { if opts.DryRun {
continue continue
} }
@ -170,7 +183,9 @@ func MarkAndSweep(ctx context.Context, storageDriver driver.StorageDriver, regis
for repo, dgsts := range deleteLayerSet { for repo, dgsts := range deleteLayerSet {
for _, dgst := range dgsts { for _, dgst := range dgsts {
emit("%s: layer link eligible for deletion: %s", repo, dgst) if !opts.Quiet {
emit("%s: layer link eligible for deletion: %s", repo, dgst)
}
if opts.DryRun { if opts.DryRun {
continue continue
} }
@ -185,11 +200,14 @@ func MarkAndSweep(ctx context.Context, storageDriver driver.StorageDriver, regis
} }
// unmarkReferencedManifest filters out manifest present in markSet // unmarkReferencedManifest filters out manifest present in markSet
func unmarkReferencedManifest(manifestArr []ManifestDel, markSet map[digest.Digest]struct{}) []ManifestDel { func unmarkReferencedManifest(manifestArr []ManifestDel, markSet map[digest.Digest]struct{}, quietOutput bool) []ManifestDel {
filtered := make([]ManifestDel, 0) filtered := make([]ManifestDel, 0)
for _, obj := range manifestArr { for _, obj := range manifestArr {
if _, ok := markSet[obj.Digest]; !ok { if _, ok := markSet[obj.Digest]; !ok {
emit("manifest eligible for deletion: %s", obj) if !quietOutput {
emit("manifest eligible for deletion: %s", obj)
}
filtered = append(filtered, obj) filtered = append(filtered, obj)
} }
} }