// layerIsPrunable returns true if the layer is not referenced by any images. func layerIsPrunable(g graph.Graph, layerNode *graph.ImageLayerNode) bool { for _, predecessor := range g.Predecessors(layerNode) { glog.V(4).Infof("Examining layer predecessor %#v", predecessor) if g.Kind(predecessor) == graph.ImageGraphKind { glog.V(4).Infof("Layer has an image predecessor") return false } } return true }
// streamLayerReferences returns a list of ImageStreamNodes that reference a // given ImageLayeNode. func streamLayerReferences(g graph.Graph, layerNode *graph.ImageLayerNode) []*graph.ImageStreamNode { ret := []*graph.ImageStreamNode{} for _, predecessor := range g.Predecessors(layerNode) { if g.Kind(predecessor) != graph.ImageStreamGraphKind { continue } ret = append(ret, predecessor.(*graph.ImageStreamNode)) } return ret }
// imageIsPrunable returns true iff the image node only has weak references // from its predecessors to it. A weak reference to an image is a reference // from an image stream to an image where the image is not the current image // for a tag and the image stream is at least as old as the minimum pruning // age. func imageIsPrunable(g graph.Graph, imageNode *graph.ImageNode) bool { onlyWeakReferences := true for _, n := range g.Predecessors(imageNode) { glog.V(4).Infof("Examining predecessor %#v", n) if !edgeKind(g, n, imageNode, graph.WeakReferencedImageGraphEdgeKind) { glog.V(4).Infof("Strong reference detected") onlyWeakReferences = false break } } return onlyWeakReferences }
// pruneStreams removes references from all image streams' status.tags entries // to prunable images, invoking streamPruner.PruneImageStream for each updated // stream. func pruneStreams(g graph.Graph, imageNodes []*imagegraph.ImageNode, streamPruner ImageStreamPruner) []error { errs := []error{} glog.V(4).Infof("Removing pruned image references from streams") for _, imageNode := range imageNodes { for _, n := range g.Predecessors(imageNode) { streamNode, ok := n.(*imagegraph.ImageStreamNode) if !ok { continue } stream := streamNode.ImageStream updatedTags := util.NewStringSet() glog.V(4).Infof("Checking if ImageStream %s/%s has references to image %s in status.tags", stream.Namespace, stream.Name, imageNode.Image.Name) for tag, history := range stream.Status.Tags { glog.V(4).Infof("Checking tag %q", tag) newHistory := imageapi.TagEventList{} for i, tagEvent := range history.Items { glog.V(4).Infof("Checking tag event %d with image %q", i, tagEvent.Image) if tagEvent.Image != imageNode.Image.Name { glog.V(4).Infof("Tag event doesn't match deleted image - keeping") newHistory.Items = append(newHistory.Items, tagEvent) } else { glog.V(4).Infof("Tag event matches deleted image - removing reference") updatedTags.Insert(tag) } } stream.Status.Tags[tag] = newHistory } updatedStream, err := streamPruner.PruneImageStream(stream, imageNode.Image, updatedTags.List()) if err != nil { errs = append(errs, fmt.Errorf("error pruning image from stream: %v", err)) continue } streamNode.ImageStream = updatedStream } } glog.V(4).Infof("Done removing pruned image references from streams") return errs }
// pruneImages invokes imagePruneFunc with each image that is prunable, along // with the image streams that reference the image. After imagePruneFunc is // invoked, the image node is removed from the graph, so that layers eligible // for pruning may later be identified. func pruneImages(g graph.Graph, imageNodes []*graph.ImageNode, pruneImage ImagePruneFunc, pruneStream ImageStreamPruneFunc, pruneManifest ManifestPruneFunc) { for _, imageNode := range imageNodes { glog.V(4).Infof("Examining image %q", imageNode.Image.Name) if !imageIsPrunable(g, imageNode) { glog.V(4).Infof("Image has strong references - not pruning") continue } glog.V(4).Infof("Image has only weak references - pruning") if err := pruneImage(imageNode.Image); err != nil { util.HandleError(fmt.Errorf("error pruning image %q: %v", imageNode.Image.Name, err)) } for _, n := range g.Predecessors(imageNode) { if streamNode, ok := n.(*graph.ImageStreamNode); ok { stream := streamNode.ImageStream repoName := fmt.Sprintf("%s/%s", stream.Namespace, stream.Name) glog.V(4).Infof("Pruning image from stream %s", repoName) updatedStream, err := pruneStream(stream, imageNode.Image) if err != nil { util.HandleError(fmt.Errorf("error pruning image from stream: %v", err)) continue } streamNode.ImageStream = updatedStream ref, err := imageapi.DockerImageReferenceForStream(stream) if err != nil { util.HandleError(fmt.Errorf("error constructing DockerImageReference for %q: %v", repoName, err)) continue } glog.V(4).Infof("Invoking pruneManifest for registry %q, repo %q, image %q", ref.Registry, repoName, imageNode.Image.Name) if err := pruneManifest(ref.Registry, repoName, imageNode.Image.Name); err != nil { util.HandleError(fmt.Errorf("error pruning manifest for registry %q, repo %q, image %q: %v", ref.Registry, repoName, imageNode.Image.Name, err)) } } } // remove pruned image node from graph, for layer pruning later g.RemoveNode(imageNode) } }
// pruneManifests invokes manifestPruner.PruneManifest for each repository // manifest to be deleted from the registry. func pruneManifests(g graph.Graph, registryClient *http.Client, registryURL string, imageNodes []*imagegraph.ImageNode, manifestPruner ManifestPruner) []error { errs := []error{} for _, imageNode := range imageNodes { for _, n := range g.Predecessors(imageNode) { streamNode, ok := n.(*imagegraph.ImageStreamNode) if !ok { continue } stream := streamNode.ImageStream repoName := fmt.Sprintf("%s/%s", stream.Namespace, stream.Name) glog.V(4).Infof("Pruning manifest for registry %q, repo %q, image %q", registryURL, repoName, imageNode.Image.Name) if err := manifestPruner.PruneManifest(registryClient, registryURL, repoName, imageNode.Image.Name); err != nil { errs = append(errs, fmt.Errorf("error pruning manifest for registry %q, repo %q, image %q: %v", registryURL, repoName, imageNode.Image.Name, err)) } } } return errs }