// humanReadableOutput traverses the provided graph using DFS and outputs it // in a human-readable format. It starts from the provided root, assuming it // is an imageStreamTag node and continues to the rest of the graph handling // only imageStreamTag and buildConfig nodes. func (d *ChainDescriber) humanReadableOutput(g osgraph.Graph, f osgraph.Namer, root graph.Node, reverse bool) string { if reverse { g = g.EdgeSubgraph(osgraph.ReverseExistingDirectEdge) } var singleNamespace bool if len(d.namespaces) == 1 && !d.namespaces.Has(kapi.NamespaceAll) { singleNamespace = true } depth := map[graph.Node]int{ root: 0, } out := "" dfs := &DepthFirst{ Visit: func(u, v graph.Node) { depth[v] = depth[u] + 1 }, } until := func(node graph.Node) bool { var info string switch t := node.(type) { case *imagegraph.ImageStreamTagNode: info = outputHelper(f.ResourceName(t), t.Namespace, singleNamespace) case *buildgraph.BuildConfigNode: info = outputHelper(f.ResourceName(t), t.BuildConfig.Namespace, singleNamespace) default: panic("this graph contains node kinds other than imageStreamTags and buildConfigs") } if depth[node] != 0 { out += "\n" } out += fmt.Sprintf("%s", strings.Repeat("\t", depth[node])) out += fmt.Sprintf("%s", info) return false } dfs.Walk(g, root, until) return out }
// DeploymentPipelines returns a map of DeploymentConfigs to the deployment flows that create them, // extracted from the provided Graph. func DeploymentPipelines(g osgraph.Graph) (DeploymentPipelineMap, osgraph.NodeSet) { covered := make(osgraph.NodeSet) g = g.EdgeSubgraph(osgraph.ReverseGraphEdge) flows := make(DeploymentPipelineMap) for _, node := range g.NodeList() { switch t := node.(type) { case *deploygraph.DeploymentConfigNode: covered.Add(t.ID()) images := []ImagePipeline{} for _, n := range g.Neighbors(node) { // find incoming image edges only switch g.EdgeKind(g.EdgeBetween(node, n)) { case deployedges.TriggersDeploymentEdgeKind, deployedges.UsedInDeploymentEdgeKind: if flow, ok := ImagePipelineFromNode(g, n, covered); ok { images = append(images, flow) } } } output := []ImagePipeline{} // ensure the list of images is ordered the same as what is in the template if template := t.DeploymentConfig.Template.ControllerTemplate.Template; template != nil { deployedges.EachTemplateImage( &template.Spec, deployedges.DeploymentConfigHasTrigger(t.DeploymentConfig), func(image deployedges.TemplateImage, err error) { if err != nil { return } for i := range images { switch t := images[i].Image.(type) { case *imagegraph.ImageStreamTagNode: if image.Ref != nil { continue } from := image.From if t.ImageStream.Name != from.Name || t.ImageStream.Namespace != from.Namespace { continue } output = append(output, images[i]) return case *imagegraph.DockerImageRepositoryNode: if image.From != nil { continue } ref1, ref2 := t.Ref.Minimal(), image.Ref.DockerClientDefaults().Minimal() if ref1 != ref2 { continue } output = append(output, images[i]) return } } }, ) } flows[t] = output } } return flows, covered }