Skip to content

Commit

Permalink
Renamed LayerDeleter to LayerLinkDeleter
Browse files Browse the repository at this point in the history
LayerDeleter suggests it is usable to delete just image layers. It is
suitable to delete image configs as well. In fact, it doesn't really
remove the layers, it just removes layer links from desired repository.
This LayerLinkDeleter is a more appropriate.

Signed-off-by: Michal Minář <[email protected]>
  • Loading branch information
Michal Minář committed Sep 7, 2016
1 parent 7f59726 commit 1edcbbb
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 86 deletions.
27 changes: 13 additions & 14 deletions pkg/cmd/admin/prune/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,21 +233,21 @@ func (o PruneImagesOptions) Run() error {

imageDeleter := &describingImageDeleter{w: w}
imageStreamDeleter := &describingImageStreamDeleter{w: w}
layerDeleter := &describingLayerDeleter{w: w}
layerLinkDeleter := &describingLayerLinkDeleter{w: w}
blobDeleter := &describingBlobDeleter{w: w}
manifestDeleter := &describingManifestDeleter{w: w}

if o.Confirm {
imageDeleter.delegate = prune.NewImageDeleter(o.Client.Images())
imageStreamDeleter.delegate = prune.NewImageStreamDeleter(o.Client)
layerDeleter.delegate = prune.NewLayerDeleter()
layerLinkDeleter.delegate = prune.NewLayerLinkDeleter()
blobDeleter.delegate = prune.NewBlobDeleter()
manifestDeleter.delegate = prune.NewManifestDeleter()
} else {
fmt.Fprintln(os.Stderr, "Dry run enabled - no modifications will be made. Add --confirm to remove images")
}

return o.Pruner.Prune(imageDeleter, imageStreamDeleter, layerDeleter, blobDeleter, manifestDeleter)
return o.Pruner.Prune(imageDeleter, imageStreamDeleter, layerLinkDeleter, blobDeleter, manifestDeleter)
}

// describingImageStreamDeleter prints information about each image stream update.
Expand Down Expand Up @@ -312,33 +312,32 @@ func (p *describingImageDeleter) DeleteImage(image *imageapi.Image) error {
return err
}

// describingLayerDeleter prints information about each repo layer link being
// deleted. If a delegate exists, its DeleteLayer function is invoked prior to
// returning.
type describingLayerDeleter struct {
// describingLayerLinkDeleter prints information about each repo layer link being deleted. If a delegate
// exists, its DeleteLayerLink function is invoked prior to returning.
type describingLayerLinkDeleter struct {
w io.Writer
delegate prune.LayerDeleter
delegate prune.LayerLinkDeleter
headerPrinted bool
}

var _ prune.LayerDeleter = &describingLayerDeleter{}
var _ prune.LayerLinkDeleter = &describingLayerLinkDeleter{}

func (p *describingLayerDeleter) DeleteLayer(registryClient *http.Client, registryURL, repo, layer string) error {
func (p *describingLayerLinkDeleter) DeleteLayerLink(registryClient *http.Client, registryURL, repo, name string) error {
if !p.headerPrinted {
p.headerPrinted = true
fmt.Fprintln(p.w, "\nDeleting registry repository layer links ...")
fmt.Fprintln(p.w, "REPO\tLAYER")
fmt.Fprintln(p.w, "REPO\tLAYER LINK")
}

fmt.Fprintf(p.w, "%s\t%s\n", repo, layer)
fmt.Fprintf(p.w, "%s\t%s\n", repo, name)

if p.delegate == nil {
return nil
}

err := p.delegate.DeleteLayer(registryClient, registryURL, repo, layer)
err := p.delegate.DeleteLayerLink(registryClient, registryURL, repo, name)
if err != nil {
fmt.Fprintf(os.Stderr, "error deleting repository %s layer link %s from the registry: %v\n", repo, layer, err)
fmt.Fprintf(os.Stderr, "error deleting repository %s layer link %s from the registry: %v\n", repo, name, err)
}

return err
Expand Down
59 changes: 35 additions & 24 deletions pkg/image/prune/prune.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@ type BlobDeleter interface {
DeleteBlob(registryClient *http.Client, registryURL, blob string) error
}

// LayerDeleter knows how to delete a repository layer link from the Docker registry.
type LayerDeleter interface {
// DeleteLayer uses registryClient to ask the registry at registryURL to
// LayerLinkDeleter knows how to delete a repository layer link from the Docker registry.
type LayerLinkDeleter interface {
// DeleteLayerLink uses registryClient to ask the registry at registryURL to
// delete the repository layer link.
DeleteLayer(registryClient *http.Client, registryURL, repo, layer string) error
DeleteLayerLink(registryClient *http.Client, registryURL, repo, linkName string) error
}

// ManifestDeleter knows how to delete image manifest data for a repository from
Expand Down Expand Up @@ -130,12 +130,11 @@ type PrunerOptions struct {

// Pruner knows how to prune images and layers.
type Pruner interface {
// Prune uses imagePruner, streamPruner, layerPruner, blobPruner, and
// Prune uses imagePruner, streamPruner, layerLinkPruner, blobPruner, and
// manifestPruner to remove images that have been identified as candidates
// for pruning based on the Pruner's internal pruning algorithm.
// Please see NewPruner for details on the algorithm.
Prune(imagePruner ImageDeleter, streamPruner ImageStreamDeleter, layerPruner LayerDeleter,
blobPruner BlobDeleter, manifestPruner ManifestDeleter) error
Prune(imagePruner ImageDeleter, streamPruner ImageStreamDeleter, layerLinkPruner LayerLinkDeleter, blobPruner BlobDeleter, manifestPruner ManifestDeleter) error
}

// pruner is an object that knows how to prune a data set
Expand Down Expand Up @@ -776,10 +775,16 @@ func (p *pruner) determineRegistry(imageNodes []*imagegraph.ImageNode) (string,
return ref.Registry, nil
}

// Run identifies images eligible for pruning, invoking imagePruneFunc for each
// image, and then it identifies layers eligible for pruning, invoking
// layerPruneFunc for each registry URL that has layers that can be pruned.
func (p *pruner) Prune(imagePruner ImageDeleter, streamPruner ImageStreamDeleter, layerPruner LayerDeleter, blobPruner BlobDeleter, manifestPruner ManifestDeleter) error {
// Run identifies images eligible for pruning, invoking imagePruner for each image, and then it identifies
// image layers eligible for pruning, invoking layerLinkPruner for each registry URL that has
// layers that can be pruned.
func (p *pruner) Prune(
imagePruner ImageDeleter,
streamPruner ImageStreamDeleter,
layerLinkPruner LayerLinkDeleter,
blobPruner BlobDeleter,
manifestPruner ManifestDeleter,
) error {
allNodes := p.g.Nodes()

imageNodes := getImageNodes(allNodes)
Expand All @@ -804,7 +809,7 @@ func (p *pruner) Prune(imagePruner ImageDeleter, streamPruner ImageStreamDeleter
errs := []error{}

errs = append(errs, pruneStreams(p.g, prunableImageNodes, streamPruner)...)
errs = append(errs, pruneLayers(p.g, p.registryClient, registryURL, prunableLayers, layerPruner)...)
errs = append(errs, pruneLayers(p.g, p.registryClient, registryURL, prunableLayers, layerLinkPruner)...)
errs = append(errs, pruneBlobs(p.g, p.registryClient, registryURL, prunableLayers, blobPruner)...)
errs = append(errs, pruneManifests(p.g, p.registryClient, registryURL, prunableImageNodes, manifestPruner)...)

Expand Down Expand Up @@ -846,9 +851,15 @@ func streamLayerReferences(g graph.Graph, layerNode *imagegraph.ImageLayerNode)
return ret
}

// pruneLayers invokes layerPruner.DeleteLayer for each repository layer link to
// pruneLayers invokes layerLinkDeleter.DeleteLayerLink for each repository layer link to
// be deleted from the registry.
func pruneLayers(g graph.Graph, registryClient *http.Client, registryURL string, layerNodes []*imagegraph.ImageLayerNode, layerPruner LayerDeleter) []error {
func pruneLayers(
g graph.Graph,
registryClient *http.Client,
registryURL string,
layerNodes []*imagegraph.ImageLayerNode,
layerLinkDeleter LayerLinkDeleter,
) []error {
errs := []error{}

for _, layerNode := range layerNodes {
Expand All @@ -860,7 +871,7 @@ func pruneLayers(g graph.Graph, registryClient *http.Client, registryURL string,
streamName := fmt.Sprintf("%s/%s", stream.Namespace, stream.Name)

glog.V(4).Infof("Pruning registry=%q, repo=%q, layer=%q", registryURL, streamName, layerNode.Layer)
if err := layerPruner.DeleteLayer(registryClient, registryURL, streamName, layerNode.Layer); err != nil {
if err := layerLinkDeleter.DeleteLayerLink(registryClient, registryURL, streamName, layerNode.Layer); err != nil {
errs = append(errs, fmt.Errorf("error pruning repo %q layer link %q: %v", streamName, layerNode.Layer, err))
}
}
Expand Down Expand Up @@ -1010,19 +1021,19 @@ func deleteFromRegistry(registryClient *http.Client, url string) error {
return err
}

// layerDeleter removes a repository layer link from the registry.
type layerDeleter struct{}
// layerLinkDeleter removes a repository layer link from the registry.
type layerLinkDeleter struct{}

var _ LayerDeleter = &layerDeleter{}
var _ LayerLinkDeleter = &layerLinkDeleter{}

// NewLayerDeleter creates a new layerDeleter.
func NewLayerDeleter() LayerDeleter {
return &layerDeleter{}
// NewLayerLinkDeleter creates a new layerLinkDeleter.
func NewLayerLinkDeleter() LayerLinkDeleter {
return &layerLinkDeleter{}
}

func (p *layerDeleter) DeleteLayer(registryClient *http.Client, registryURL, repoName, layer string) error {
glog.V(4).Infof("Pruning registry %q, repo %q, layer %q", registryURL, repoName, layer)
return deleteFromRegistry(registryClient, fmt.Sprintf("%s/v2/%s/blobs/%s", registryURL, repoName, layer))
func (p *layerLinkDeleter) DeleteLayerLink(registryClient *http.Client, registryURL, repoName, linkName string) error {
glog.V(4).Infof("Pruning registry %q, repo %q, layer link %q", registryURL, repoName, linkName)
return deleteFromRegistry(registryClient, fmt.Sprintf("%s/v2/%s/blobs/%s", registryURL, repoName, linkName))
}

// blobDeleter removes a blob from the registry.
Expand Down
96 changes: 48 additions & 48 deletions pkg/image/prune/prune_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -372,14 +372,14 @@ func (p *fakeBlobDeleter) DeleteBlob(registryClient *http.Client, registryURL, b
return p.err
}

type fakeLayerDeleter struct {
type fakeLayerLinkDeleter struct {
invocations sets.String
err error
}

var _ LayerDeleter = &fakeLayerDeleter{}
var _ LayerLinkDeleter = &fakeLayerLinkDeleter{}

func (p *fakeLayerDeleter) DeleteLayer(registryClient *http.Client, registryURL, repo, layer string) error {
func (p *fakeLayerLinkDeleter) DeleteLayerLink(registryClient *http.Client, registryURL, repo, layer string) error {
p.invocations.Insert(fmt.Sprintf("%s|%s|%s", registryURL, repo, layer))
return p.err
}
Expand All @@ -404,20 +404,20 @@ func TestImagePruning(t *testing.T) {
registryURL := "registry"

tests := map[string]struct {
pruneOverSizeLimit *bool
registryURLs []string
images imageapi.ImageList
pods kapi.PodList
streams imageapi.ImageStreamList
rcs kapi.ReplicationControllerList
bcs buildapi.BuildConfigList
builds buildapi.BuildList
dcs deployapi.DeploymentConfigList
limits map[string][]*kapi.LimitRange
expectedImageDeletions []string
expectedStreamUpdates []string
expectedLayerDeletions []string
expectedBlobDeletions []string
pruneOverSizeLimit *bool
registryURLs []string
images imageapi.ImageList
pods kapi.PodList
streams imageapi.ImageStreamList
rcs kapi.ReplicationControllerList
bcs buildapi.BuildConfigList
builds buildapi.BuildList
dcs deployapi.DeploymentConfigList
limits map[string][]*kapi.LimitRange
expectedImageDeletions []string
expectedStreamUpdates []string
expectedLayerLinkDeletions []string
expectedBlobDeletions []string
}{
"1 pod - phase pending - don't prune": {
images: imageList(image("id", registryURL+"/foo/bar@id")),
Expand Down Expand Up @@ -735,7 +735,7 @@ func TestImagePruning(t *testing.T) {
),
expectedImageDeletions: []string{"id4"},
expectedStreamUpdates: []string{"foo/bar|id4"},
expectedLayerDeletions: []string{
expectedLayerLinkDeletions: []string{
registryURL + "|foo/bar|layer5",
registryURL + "|foo/bar|layer6",
registryURL + "|foo/bar|layer7",
Expand Down Expand Up @@ -852,11 +852,11 @@ func TestImagePruning(t *testing.T) {

imageDeleter := &fakeImageDeleter{invocations: sets.NewString()}
streamDeleter := &fakeImageStreamDeleter{invocations: sets.NewString()}
layerDeleter := &fakeLayerDeleter{invocations: sets.NewString()}
layerLinkDeleter := &fakeLayerLinkDeleter{invocations: sets.NewString()}
blobDeleter := &fakeBlobDeleter{invocations: sets.NewString()}
manifestDeleter := &fakeManifestDeleter{invocations: sets.NewString()}

p.Prune(imageDeleter, streamDeleter, layerDeleter, blobDeleter, manifestDeleter)
p.Prune(imageDeleter, streamDeleter, layerLinkDeleter, blobDeleter, manifestDeleter)

expectedImageDeletions := sets.NewString(test.expectedImageDeletions...)
if !reflect.DeepEqual(expectedImageDeletions, imageDeleter.invocations) {
Expand All @@ -868,9 +868,9 @@ func TestImagePruning(t *testing.T) {
t.Errorf("%s: expected stream updates %q, got %q", name, expectedStreamUpdates.List(), streamDeleter.invocations.List())
}

expectedLayerDeletions := sets.NewString(test.expectedLayerDeletions...)
if !reflect.DeepEqual(expectedLayerDeletions, layerDeleter.invocations) {
t.Errorf("%s: expected layer deletions %q, got %q", name, expectedLayerDeletions.List(), layerDeleter.invocations.List())
expectedLayerLinkDeletions := sets.NewString(test.expectedLayerLinkDeletions...)
if !reflect.DeepEqual(expectedLayerLinkDeletions, layerLinkDeleter.invocations) {
t.Errorf("%s: expected layer link deletions %q, got %q", name, expectedLayerLinkDeletions.List(), layerLinkDeleter.invocations.List())
}

expectedBlobDeletions := sets.NewString(test.expectedBlobDeletions...)
Expand Down Expand Up @@ -925,8 +925,8 @@ func TestLayerDeleter(t *testing.T) {
actions = append(actions, req.Method+":"+req.URL.String())
return &http.Response{StatusCode: http.StatusServiceUnavailable, Body: ioutil.NopCloser(bytes.NewReader([]byte{}))}, nil
})
layerDeleter := NewLayerDeleter()
layerDeleter.DeleteLayer(client, "registry1", "repo", "layer1")
layerLinkDeleter := NewLayerLinkDeleter()
layerLinkDeleter.DeleteLayerLink(client, "registry1", "repo", "layer1")

if !reflect.DeepEqual(actions, []string{"DELETE:https://registry1/v2/repo/blobs/layer1",
"DELETE:http://registry1/v2/repo/blobs/layer1"}) {
Expand All @@ -942,8 +942,8 @@ func TestNotFoundLayerDeleter(t *testing.T) {
actions = append(actions, req.Method+":"+req.URL.String())
return &http.Response{StatusCode: http.StatusNotFound, Body: ioutil.NopCloser(bytes.NewReader([]byte{}))}, nil
})
layerDeleter := NewLayerDeleter()
layerDeleter.DeleteLayer(client, "registry1", "repo", "layer1")
layerLinkDeleter := NewLayerLinkDeleter()
layerLinkDeleter.DeleteLayerLink(client, "registry1", "repo", "layer1")

if !reflect.DeepEqual(actions, []string{"DELETE:https://registry1/v2/repo/blobs/layer1"}) {
t.Errorf("Unexpected actions %v", actions)
Expand All @@ -954,12 +954,12 @@ func TestRegistryPruning(t *testing.T) {
flag.Lookup("v").Value.Set(fmt.Sprint(*logLevel))

tests := map[string]struct {
images imageapi.ImageList
streams imageapi.ImageStreamList
expectedLayerDeletions sets.String
expectedBlobDeletions sets.String
expectedManifestDeletions sets.String
pingErr error
images imageapi.ImageList
streams imageapi.ImageStreamList
expectedLayerLinkDeletions sets.String
expectedBlobDeletions sets.String
expectedManifestDeletions sets.String
pingErr error
}{
"layers unique to id1 pruned": {
images: imageList(
Expand All @@ -979,7 +979,7 @@ func TestRegistryPruning(t *testing.T) {
),
)),
),
expectedLayerDeletions: sets.NewString(
expectedLayerLinkDeletions: sets.NewString(
"registry1|foo/bar|layer1",
"registry1|foo/bar|layer2",
),
Expand All @@ -1002,16 +1002,16 @@ func TestRegistryPruning(t *testing.T) {
),
)),
),
expectedLayerDeletions: sets.NewString(),
expectedBlobDeletions: sets.NewString(),
expectedManifestDeletions: sets.NewString(),
expectedLayerLinkDeletions: sets.NewString(),
expectedBlobDeletions: sets.NewString(),
expectedManifestDeletions: sets.NewString(),
},
"blobs pruned when streams have already been deleted": {
images: imageList(
imageWithLayers("id1", "registry1/foo/bar@id1", "layer1", "layer2", "layer3", "layer4"),
imageWithLayers("id2", "registry1/foo/bar@id2", "layer3", "layer4", "layer5", "layer6"),
),
expectedLayerDeletions: sets.NewString(),
expectedLayerLinkDeletions: sets.NewString(),
expectedBlobDeletions: sets.NewString(
"registry1|layer1",
"registry1|layer2",
Expand Down Expand Up @@ -1040,10 +1040,10 @@ func TestRegistryPruning(t *testing.T) {
),
)),
),
expectedLayerDeletions: sets.NewString(),
expectedBlobDeletions: sets.NewString(),
expectedManifestDeletions: sets.NewString(),
pingErr: errors.New("foo"),
expectedLayerLinkDeletions: sets.NewString(),
expectedBlobDeletions: sets.NewString(),
expectedManifestDeletions: sets.NewString(),
pingErr: errors.New("foo"),
},
}

Expand Down Expand Up @@ -1073,20 +1073,20 @@ func TestRegistryPruning(t *testing.T) {

imageDeleter := &fakeImageDeleter{invocations: sets.NewString()}
streamDeleter := &fakeImageStreamDeleter{invocations: sets.NewString()}
layerDeleter := &fakeLayerDeleter{invocations: sets.NewString()}
layerLinkDeleter := &fakeLayerLinkDeleter{invocations: sets.NewString()}
blobDeleter := &fakeBlobDeleter{invocations: sets.NewString()}
manifestDeleter := &fakeManifestDeleter{invocations: sets.NewString()}

p.Prune(imageDeleter, streamDeleter, layerDeleter, blobDeleter, manifestDeleter)
p.Prune(imageDeleter, streamDeleter, layerLinkDeleter, blobDeleter, manifestDeleter)

if !reflect.DeepEqual(test.expectedLayerDeletions, layerDeleter.invocations) {
t.Errorf("%s: expected layer deletions %#v, got %#v", name, test.expectedLayerDeletions, layerDeleter.invocations)
if !reflect.DeepEqual(test.expectedLayerLinkDeletions, layerLinkDeleter.invocations) {
t.Errorf("%s: expected layer link deletions %#v, got %#v", name, test.expectedLayerLinkDeletions.List(), layerLinkDeleter.invocations.List())
}
if !reflect.DeepEqual(test.expectedBlobDeletions, blobDeleter.invocations) {
t.Errorf("%s: expected blob deletions %#v, got %#v", name, test.expectedBlobDeletions, blobDeleter.invocations)
t.Errorf("%s: expected blob deletions %#v, got %#v", name, test.expectedBlobDeletions.List(), blobDeleter.invocations.List())
}
if !reflect.DeepEqual(test.expectedManifestDeletions, manifestDeleter.invocations) {
t.Errorf("%s: expected manifest deletions %#v, got %#v", name, test.expectedManifestDeletions, manifestDeleter.invocations)
t.Errorf("%s: expected manifest deletions %#v, got %#v", name, test.expectedManifestDeletions.List(), manifestDeleter.invocations.List())
}
}
}
Expand Down

0 comments on commit 1edcbbb

Please sign in to comment.