func TestTarjan(t *testing.T) { for i, test := range tarjanTests { g := concrete.NewDirectedGraph() for u, e := range test.g { g.AddNode(concrete.Node(u)) for v := range e { if !g.NodeExists(concrete.Node(v)) { g.AddNode(concrete.Node(v)) } g.AddDirectedEdge(concrete.Edge{H: concrete.Node(u), T: concrete.Node(v)}, 0) } } gotSCCs := search.Tarjan(g) // tarjan.strongconnect does range iteration over maps, // so sort SCC members to ensure consistent ordering. gotIDs := make([][]int, len(gotSCCs)) for i, scc := range gotSCCs { gotIDs[i] = make([]int, len(scc)) for j, id := range scc { gotIDs[i][j] = id.ID() } sort.Ints(gotIDs[i]) } for _, iv := range test.ambiguousOrder { sort.Sort(byComponentLengthOrStart(test.want[iv.start:iv.end])) sort.Sort(byComponentLengthOrStart(gotIDs[iv.start:iv.end])) } if !reflect.DeepEqual(gotIDs, test.want) { t.Errorf("unexpected Tarjan scc result for %d:\n\tgot:%v\n\twant:%v", i, gotIDs, test.want) } } }
// ServiceAndDeploymentGroups breaks the provided graph of API relationships into ServiceGroup objects, // ordered consistently. Groups are organized so that overlapping Services and DeploymentConfigs are // part of the same group, Deployment Configs are each in their own group, and then BuildConfigs are // part of the last service group. func ServiceAndDeploymentGroups(g Graph) []ServiceGroup { deploys, covered := DeploymentPipelines(g) other := g.Subgraph(UncoveredDeploymentFlowNodes(covered), UncoveredDeploymentFlowEdges(covered)) components := search.Tarjan(other) serviceGroups := []ServiceGroup{} for _, c := range components { group := ServiceGroup{} matches := NodesByKind(other, c, ServiceGraphKind, DeploymentConfigGraphKind) svcs, dcs, _ := matches[0], matches[1], matches[2] for _, n := range svcs { covers := []*DeploymentConfigNode{} for _, neighbor := range other.Neighbors(n) { switch other.EdgeKind(g.EdgeBetween(neighbor, n)) { case ExposedThroughServiceGraphEdgeKind: covers = append(covers, neighbor.(*DeploymentConfigNode)) } } group.Services = append(group.Services, ServiceReference{ Service: n.(*ServiceNode), Covers: covers, }) } sort.Sort(SortedServiceReferences(group.Services)) for _, n := range dcs { d := n.(*DeploymentConfigNode) group.Deployments = append(group.Deployments, DeploymentFlow{ Deployment: d, Images: deploys[d], }) } sort.Sort(SortedDeploymentPipelines(group.Deployments)) if len(dcs) == 0 || len(svcs) == 0 { unknown := g.SubgraphWithNodes(c, ExistingDirectEdge) for _, n := range unknown.NodeList() { g.PredecessorEdges(n, AddGraphEdgesTo(unknown), BuildOutputGraphEdgeKind) } unknown = unknown.EdgeSubgraph(ReverseGraphEdge) for _, n := range unknown.RootNodes() { if flow, ok := ImagePipelineFromNode(unknown, n, make(NodeSet)); ok { group.Builds = append(group.Builds, flow) } } } sort.Sort(SortedImagePipelines(group.Builds)) serviceGroups = append(serviceGroups, group) } sort.Sort(SortedServiceGroups(serviceGroups)) return serviceGroups }
// ServiceAndDeploymentGroups breaks the provided graph of API relationships into ServiceGroup objects, // ordered consistently. Groups are organized so that overlapping Services and DeploymentConfigs are // part of the same group, Deployment Configs are each in their own group, and then BuildConfigs are // part of the last service group. func ServiceAndDeploymentGroups(g osgraph.Graph) []ServiceGroup { deploys, covered := DeploymentPipelines(g) other := g.Subgraph(UncoveredDeploymentFlowNodes(covered), UncoveredDeploymentFlowEdges(covered)) components := search.Tarjan(other) serviceGroups := []ServiceGroup{} for _, c := range components { group := ServiceGroup{} matches := osgraph.NodesByKind(other, c, kubegraph.ServiceNodeKind, deploygraph.DeploymentConfigNodeKind) svcs, dcs, _ := matches[0], matches[1], matches[2] for _, n := range svcs { coveredDCs := []*deploygraph.DeploymentConfigNode{} coveredRCs := []*kubegraph.ReplicationControllerNode{} coveredPods := []*kubegraph.PodNode{} for _, neighbor := range other.Neighbors(n) { switch other.EdgeKind(g.EdgeBetween(neighbor, n)) { case kubeedges.ExposedThroughServiceEdgeKind: containerNode := osgraph.GetTopLevelContainerNode(g, neighbor) switch castCoveredNode := containerNode.(type) { case *deploygraph.DeploymentConfigNode: coveredDCs = append(coveredDCs, castCoveredNode) case *kubegraph.ReplicationControllerNode: coveredRCs = append(coveredRCs, castCoveredNode) case *kubegraph.PodNode: coveredPods = append(coveredPods, castCoveredNode) } } } group.Services = append(group.Services, ServiceReference{ Service: n.(*kubegraph.ServiceNode), CoveredDCs: coveredDCs, CoveredRCs: coveredRCs, CoveredPods: coveredPods, }) } sort.Sort(SortedServiceReferences(group.Services)) for _, n := range dcs { d := n.(*deploygraph.DeploymentConfigNode) group.Deployments = append(group.Deployments, DeploymentFlow{ Deployment: d, Images: deploys[d], }) } sort.Sort(SortedDeploymentPipelines(group.Deployments)) if len(dcs) == 0 || len(svcs) == 0 { unknown := g.SubgraphWithNodes(c, osgraph.ExistingDirectEdge) for _, n := range unknown.NodeList() { g.PredecessorEdges(n, osgraph.AddGraphEdgesTo(unknown), buildedges.BuildOutputEdgeKind) } unknown = unknown.EdgeSubgraph(osgraph.ReverseGraphEdge) for _, n := range unknown.RootNodes() { if flow, ok := ImagePipelineFromNode(unknown, n, make(osgraph.NodeSet)); ok { group.Builds = append(group.Builds, flow) } } } sort.Sort(SortedImagePipelines(group.Builds)) serviceGroups = append(serviceGroups, group) } sort.Sort(SortedServiceGroups(serviceGroups)) return serviceGroups }