// addImageStreamsToGraph adds all the streams to the graph. The most recent n // image revisions for a tag will be preserved, where n is specified by the // algorithm's keepTagRevisions. Image revisions older than n are candidates // for pruning. if the image stream's age is at least as old as the minimum // threshold in algorithm. Otherwise, if the image stream is younger than the // threshold, all image revisions for that stream are ineligible for pruning. // // addImageStreamsToGraph also adds references from each stream to all the // layers it references (via each image a stream references). func addImageStreamsToGraph(g graph.Graph, streams *imageapi.ImageStreamList, algorithm pruneAlgorithm) { for i := range streams.Items { stream := &streams.Items[i] glog.V(4).Infof("Examining ImageStream %s/%s", stream.Namespace, stream.Name) // use a weak reference for old image revisions by default oldImageRevisionReferenceKind := WeakReferencedImageEdgeKind age := util.Now().Sub(stream.CreationTimestamp.Time) if age < algorithm.keepYoungerThan { // stream's age is below threshold - use a strong reference for old image revisions instead glog.V(4).Infof("Stream %s/%s is below age threshold - none of its images are eligible for pruning", stream.Namespace, stream.Name) oldImageRevisionReferenceKind = ReferencedImageEdgeKind } glog.V(4).Infof("Adding ImageStream %s/%s to graph", stream.Namespace, stream.Name) isNode := imagegraph.EnsureImageStreamNode(g, stream) imageStreamNode := isNode.(*imagegraph.ImageStreamNode) for tag, history := range stream.Status.Tags { for i := range history.Items { n := imagegraph.FindImage(g, history.Items[i].Image) if n == nil { glog.V(2).Infof("Unable to find image %q in graph (from tag=%q, revision=%d, dockerImageReference=%s)", history.Items[i].Image, tag, i, history.Items[i].DockerImageReference) continue } imageNode := n.(*imagegraph.ImageNode) var kind string switch { case i < algorithm.keepTagRevisions: kind = ReferencedImageEdgeKind default: kind = oldImageRevisionReferenceKind } glog.V(4).Infof("Checking for existing strong reference from stream %s/%s to image %s", stream.Namespace, stream.Name, imageNode.Image.Name) if edge := g.Edge(imageStreamNode, imageNode); edge != nil && g.EdgeKinds(edge).Has(ReferencedImageEdgeKind) { glog.V(4).Infof("Strong reference found") continue } glog.V(4).Infof("Adding edge (kind=%d) from %q to %q", kind, imageStreamNode.UniqueName.UniqueName(), imageNode.UniqueName.UniqueName()) g.AddEdge(imageStreamNode, imageNode, kind) glog.V(4).Infof("Adding stream->layer references") // add stream -> layer references so we can prune them later for _, s := range g.From(imageNode) { if g.Kind(s) != imagegraph.ImageLayerNodeKind { continue } glog.V(4).Infof("Adding reference from stream %q to layer %q", stream.Name, s.(*imagegraph.ImageLayerNode).Layer) g.AddEdge(imageStreamNode, s, ReferencedImageLayerEdgeKind) } } } } }
// layerIsPrunable returns true if the layer is not referenced by any images. func layerIsPrunable(g graph.Graph, layerNode *imagegraph.ImageLayerNode) bool { for _, predecessor := range g.To(layerNode) { glog.V(4).Infof("Examining layer predecessor %#v", predecessor) if g.Kind(predecessor) == imagegraph.ImageNodeKind { glog.V(4).Infof("Layer has an image predecessor") return false } } return true }
// streamReferencingImageComponent returns a list of ImageStreamNodes that reference a // given ImageComponentNode. func streamsReferencingImageComponent(g graph.Graph, cn *imagegraph.ImageComponentNode) []*imagegraph.ImageStreamNode { ret := []*imagegraph.ImageStreamNode{} for _, predecessor := range g.To(cn) { if g.Kind(predecessor) != imagegraph.ImageStreamNodeKind { continue } ret = append(ret, predecessor.(*imagegraph.ImageStreamNode)) } return ret }
// imageComponentIsPrunable returns true if the image component is not referenced by any images. func imageComponentIsPrunable(g graph.Graph, cn *imagegraph.ImageComponentNode) bool { for _, predecessor := range g.To(cn) { glog.V(4).Infof("Examining predecessor %#v of image config %v", predecessor, cn) if g.Kind(predecessor) == imagegraph.ImageNodeKind { glog.V(4).Infof("Config %v has an image predecessor", cn) return false } } return true }
// streamLayerReferences returns a list of ImageStreamNodes that reference a // given ImageLayerNode. func streamLayerReferences(g graph.Graph, layerNode *imagegraph.ImageLayerNode) []*imagegraph.ImageStreamNode { ret := []*imagegraph.ImageStreamNode{} for _, predecessor := range g.To(layerNode) { if g.Kind(predecessor) != imagegraph.ImageStreamNodeKind { continue } ret = append(ret, predecessor.(*imagegraph.ImageStreamNode)) } return ret }
// DescendentNodesByNodeKind starts at the root navigates down the root. Every edge is checked against the edgeChecker // to determine whether or not to follow it. The nodes at the tail end of every chased edge are then checked against the // the targetNodeKind. Matches are added to the return and every checked node then has its edges checked: lather, rinse, repeat func DescendentNodesByNodeKind(g osgraph.Graph, visitedNodes graphview.IntSet, node graph.Node, targetNodeKind string, edgeChecker osgraph.EdgeFunc) []graph.Node { if visitedNodes.Has(node.ID()) { return []graph.Node{} } visitedNodes.Insert(node.ID()) ret := []graph.Node{} for _, successor := range g.From(node) { edge := g.Edge(node, successor) if edgeChecker(osgraph.New(), node, successor, g.EdgeKinds(edge)) { if g.Kind(successor) == targetNodeKind { ret = append(ret, successor) } ret = append(ret, DescendentNodesByNodeKind(g, visitedNodes, successor, targetNodeKind, edgeChecker)...) } } return ret }