package top
import (
"github.com/golang/glog"
gonum "github.com/gonum/graph"
"github.com/docker/distribution/digest"
kapi "k8s.io/kubernetes/pkg/api"
"github.com/openshift/origin/pkg/api/graph"
kubegraph "github.com/openshift/origin/pkg/api/kubegraph/nodes"
imageapi "github.com/openshift/origin/pkg/image/api"
imagegraph "github.com/openshift/origin/pkg/image/graph/nodes"
)
const (
ImageLayerEdgeKind = "ImageLayer"
ImageTopLayerEdgeKind = "ImageTopLayer"
ImageStreamImageEdgeKind = "ImageStreamImage"
HistoricImageStreamImageEdgeKind = "HistoricImageStreamImage"
PodImageEdgeKind = "PodImage"
ParentImageEdgeKind = "ParentImage"
// digest.DigestSha256EmptyTar is empty layer digest, whereas this is gzipped digest of empty layer
digestSHA256GzippedEmptyTar = "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
)
func getImageNodes(nodes []gonum.Node) []*imagegraph.ImageNode {
ret := []*imagegraph.ImageNode{}
for i := range nodes {
if node, ok := nodes[i].(*imagegraph.ImageNode); ok {
ret = append(ret, node)
}
}
return ret
}
func addImagesToGraph(g graph.Graph, images *imageapi.ImageList) {
for i := range images.Items {
image := &images.Items[i]
glog.V(4).Infof("Adding image %q to graph", image.Name)
imageNode := imagegraph.EnsureImageNode(g, image)
topLayerAdded := false
// We're looking through layers in reversed order since we need to
// find first layer (from top) which is not an empty layer, we're omitting
// empty layers because every image has those and they're giving us
// false positives about parents. This applies only to schema v1 images
// schema v2 does not have that problem.
for i := len(image.DockerImageLayers) - 1; i >= 0; i-- {
layer := image.DockerImageLayers[i]
layerNode := imagegraph.EnsureImageComponentLayerNode(g, layer.Name)
edgeKind := ImageLayerEdgeKind
if !topLayerAdded && layer.Name != digest.DigestSha256EmptyTar && layer.Name != digestSHA256GzippedEmptyTar {
edgeKind = ImageTopLayerEdgeKind
topLayerAdded = true
}
g.AddEdge(imageNode, layerNode, edgeKind)
glog.V(4).Infof("Adding image layer %q to graph (%q)", layer.Name, edgeKind)
}
}
}
func addImageStreamsToGraph(g graph.Graph, streams *imageapi.ImageStreamList) {
for i := range streams.Items {
stream := &streams.Items[i]
glog.V(4).Infof("Adding ImageStream %s/%s to graph", stream.Namespace, stream.Name)
isNode := imagegraph.EnsureImageStreamNode(g, stream)
imageStreamNode := isNode.(*imagegraph.ImageStreamNode)
// connect IS with underlying images
for tag, history := range stream.Status.Tags {
for i := range history.Items {
image := history.Items[i]
n := imagegraph.FindImage(g, image.Image)
if n == nil {
glog.V(2).Infof("Unable to find image %q in graph (from tag=%q, dockerImageReference=%s)",
history.Items[i].Image, tag, image.DockerImageReference)
continue
}
imageNode := n.(*imagegraph.ImageNode)
glog.V(4).Infof("Adding edge from %q to %q", imageStreamNode.UniqueName(), imageNode.UniqueName())
edgeKind := ImageStreamImageEdgeKind
if i > 1 {
edgeKind = HistoricImageStreamImageEdgeKind
}
g.AddEdge(imageStreamNode, imageNode, edgeKind)
}
}
}
}
func addPodsToGraph(g graph.Graph, pods *kapi.PodList) {
for i := range pods.Items {
pod := &pods.Items[i]
if pod.Status.Phase != kapi.PodRunning && pod.Status.Phase != kapi.PodPending {
glog.V(4).Infof("Pod %s/%s is not running nor pending - skipping", pod.Namespace, pod.Name)
continue
}
glog.V(4).Infof("Adding pod %s/%s to graph", pod.Namespace, pod.Name)
podNode := kubegraph.EnsurePodNode(g, pod)
addPodSpecToGraph(g, &pod.Spec, podNode)
}
}
func addPodSpecToGraph(g graph.Graph, spec *kapi.PodSpec, predecessor gonum.Node) {
for j := range spec.Containers {
container := spec.Containers[j]
glog.V(4).Infof("Examining container image %q", container.Image)
ref, err := imageapi.ParseDockerImageReference(container.Image)
if err != nil {
glog.V(2).Infof("Unable to parse DockerImageReference %q: %v - skipping", container.Image, err)
continue
}
if len(ref.ID) == 0 {
// ignore not managed images
continue
}
imageNode := imagegraph.FindImage(g, ref.ID)
if imageNode == nil {
glog.V(1).Infof("Unable to find image %q in the graph", ref.ID)
continue
}
glog.V(4).Infof("Adding edge from %v to %v", predecessor, imageNode)
g.AddEdge(predecessor, imageNode, PodImageEdgeKind)
}
}
func markParentsInGraph(g graph.Graph) {
imageNodes := getImageNodes(g.Nodes())
for _, in := range imageNodes {
// find image's top layer, should be just one
for _, e := range g.OutboundEdges(in, ImageTopLayerEdgeKind) {
layerNode, _ := e.To().(*imagegraph.ImageComponentNode)
// find image's containing this layer but not being their top layer
for _, ed := range g.InboundEdges(layerNode, ImageLayerEdgeKind) {
childNode, _ := ed.From().(*imagegraph.ImageNode)
if in.ID() == childNode.ID() {
// don't add self edge, otherwise gonum/graph will panic
continue
}
g.AddEdge(in, childNode, ParentImageEdgeKind)
}
// TODO: Find image's containing THIS layer being their top layer,
// this happens when image contents is not being changed.
// TODO: If two layers have exactly the same contents the current
// mechanism might trip over that as well. We should check for
// a series of layers when checking for parents.
}
}
}