func findBuildInputs(g osgraph.Graph, n graph.Node, covered osgraph.NodeSet) (base ImageTagLocation, source SourceLocation, err error) { // find inputs to the build for _, input := range g.Neighbors(n) { switch g.EdgeKind(g.EdgeBetween(n, input)) { case buildedges.BuildInputEdgeKind: if source != nil { // report this as an error (unexpected duplicate source) } covered.Add(input.ID()) source = input.(SourceLocation) case buildedges.BuildInputImageEdgeKind: if base != nil { // report this as an error (unexpected duplicate input build) } covered.Add(input.ID()) base = input.(ImageTagLocation) } } return }
// ImagePipelineFromNode attempts to locate a build flow from the provided node. If no such // build flow can be located, false is returned. func ImagePipelineFromNode(g osgraph.Graph, n graph.Node, covered osgraph.NodeSet) (ImagePipeline, bool) { flow := ImagePipeline{} switch node := n.(type) { case *buildgraph.BuildConfigNode: covered.Add(n.ID()) base, src, _ := findBuildInputs(g, n, covered) flow.Build = node flow.BaseImage = base flow.Source = src return flow, true case ImageTagLocation: covered.Add(n.ID()) flow.Image = node for _, input := range g.Neighbors(n) { switch g.EdgeKind(g.EdgeBetween(n, input)) { case buildedges.BuildOutputEdgeKind: covered.Add(input.ID()) build := input.(*buildgraph.BuildConfigNode) if flow.Build != nil { // report this as an error (unexpected duplicate input build) } if build.BuildConfig == nil { // report this as as a missing build / broken link break } base, src, _ := findBuildInputs(g, input, covered) flow.Build = build flow.BaseImage = base flow.Source = src } } return flow, true default: return flow, false } }
// 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 }