diff --git a/registry/storage/garbagecollect.go b/registry/storage/garbagecollect.go index 317c792da..887f35238 100644 --- a/registry/storage/garbagecollect.go +++ b/registry/storage/garbagecollect.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/docker/distribution" + "github.com/docker/distribution/manifest/schema2" "github.com/docker/distribution/reference" "github.com/docker/distribution/registry/storage/driver" "github.com/opencontainers/go-digest" @@ -25,6 +26,7 @@ type ManifestDel struct { Name string Digest digest.Digest Tags []string + Layers []digest.Digest } // MarkAndSweep performs a mark and sweep of registry data @@ -61,6 +63,11 @@ func MarkAndSweep(ctx context.Context, storageDriver driver.StorageDriver, regis } err = manifestEnumerator.Enumerate(ctx, func(dgst digest.Digest) error { + manifest, err := manifestService.Get(ctx, dgst) + if err != nil { + return fmt.Errorf("failed to retrieve manifest for digest %v: %v", dgst, err) + } + if opts.RemoveUntagged { // fetch all tags where this manifest is the latest one tags, err := repository.Tags(ctx).Lookup(ctx, distribution.Descriptor{Digest: dgst}) @@ -76,7 +83,23 @@ func MarkAndSweep(ctx context.Context, storageDriver driver.StorageDriver, regis if err != nil { return fmt.Errorf("failed to retrieve tags %v", err) } - manifestArr = append(manifestArr, ManifestDel{Name: repoName, Digest: dgst, Tags: allTags}) + + manifestDel := ManifestDel{ + Name: repoName, + Digest: dgst, + Tags: allTags, + Layers: []digest.Digest{}, + } + + for _, ref := range manifest.References() { + if ref.MediaType == schema2.MediaTypeLayer || + ref.MediaType == schema2.MediaTypeImageConfig { + manifestDel.Layers = append(manifestDel.Layers, ref.Digest) + } + } + + manifestArr = append(manifestArr, manifestDel) + return nil } } @@ -84,11 +107,6 @@ func MarkAndSweep(ctx context.Context, storageDriver driver.StorageDriver, regis emit("%s: marking manifest %s ", repoName, dgst) markSet[dgst] = struct{}{} - manifest, err := manifestService.Get(ctx, dgst) - if err != nil { - return fmt.Errorf("failed to retrieve manifest for digest %v: %v", dgst, err) - } - descriptors := manifest.References() for _, descriptor := range descriptors { markSet[descriptor.Digest] = struct{}{} @@ -122,6 +140,14 @@ func MarkAndSweep(ctx context.Context, storageDriver driver.StorageDriver, regis if err != nil { return fmt.Errorf("failed to delete manifest %s: %v", obj.Digest, err) } + for _, layerDgst := range obj.Layers { + if _, ok := markSet[layerDgst]; !ok { + err := vacuum.RemoveLayerLink(obj.Name, layerDgst) + if err != nil { + return fmt.Errorf("failed to delete layer link %s for manifest %s: %v", layerDgst, obj.Name, err) + } + } + } } } blobService := registry.Blobs() diff --git a/registry/storage/vacuum.go b/registry/storage/vacuum.go index a43db17a4..74e581a2d 100644 --- a/registry/storage/vacuum.go +++ b/registry/storage/vacuum.go @@ -84,6 +84,28 @@ func (v Vacuum) RemoveManifest(name string, dgst digest.Digest, tags []string) e return v.driver.Delete(v.ctx, manifestPath) } +// RemoveLayerLink removes a layer link from the filesystem +func (v Vacuum) RemoveLayerLink(manifestName string, dgst digest.Digest) error { + layerLinkPath, err := pathFor(layerLinkPathSpec{name: manifestName, digest: dgst}) + if err != nil { + return err + } + + dcontext.GetLogger(v.ctx).Infof("Deleting layer link path : %s", layerLinkPath) + + _, err = v.driver.Stat(v.ctx, layerLinkPath) + if err != nil { + switch err := err.(type) { + case driver.PathNotFoundError: + return nil + default: + return err + } + } + + return v.driver.Delete(v.ctx, layerLinkPath) +} + // RemoveRepository removes a repository directory from the // filesystem func (v Vacuum) RemoveRepository(repoName string) error {