Exemple #1
0
// partitionReverse the graph down to a subgraph starting from the given root
func partitionReverse(g osgraph.Graph, root graph.Node, buildInputEdgeKinds []string) osgraph.Graph {
	// Filter out all but BuildConfig and ImageStreamTag nodes
	nodeFn := osgraph.NodesOfKind(buildgraph.BuildConfigNodeKind, imagegraph.ImageStreamTagNodeKind)
	// Filter out all but BuildInputImage and BuildOutput edges
	edgeKinds := []string{}
	edgeKinds = append(edgeKinds, buildInputEdgeKinds...)
	edgeKinds = append(edgeKinds, buildedges.BuildOutputEdgeKind)
	edgeFn := osgraph.EdgesOfKind(edgeKinds...)
	sub := g.Subgraph(nodeFn, edgeFn)

	// Filter out inbound edges to the IST of interest
	edgeFn = osgraph.RemoveOutboundEdges([]graph.Node{root})
	sub = sub.Subgraph(nodeFn, edgeFn)

	// Check all paths leading from the root node, collect any
	// node found in them, and create the desired subgraph
	desired := []graph.Node{root}
	paths := path.DijkstraAllPaths(sub)
	for _, node := range sub.Nodes() {
		if node == root {
			continue
		}
		path, _, _ := paths.Between(node, root)
		if len(path) != 0 {
			desired = append(desired, node)
		}
	}
	return sub.SubgraphWithNodes(desired, osgraph.ExistingDirectEdge)
}
Exemple #2
0
// FindCircularBuilds checks all build configs for cycles
func FindCircularBuilds(g osgraph.Graph) []osgraph.Marker {
	// Filter out all but ImageStreamTag and BuildConfig nodes
	nodeFn := osgraph.NodesOfKind(imagegraph.ImageStreamTagNodeKind, buildgraph.BuildConfigNodeKind)
	// Filter out all but BuildInputImage and BuildOutput edges
	edgeFn := osgraph.EdgesOfKind(buildedges.BuildInputImageEdgeKind, buildedges.BuildOutputEdgeKind)

	// Create desired subgraph
	sub := g.Subgraph(nodeFn, edgeFn)

	markers := []osgraph.Marker{}

	// Check for cycles
	for _, cycle := range topo.CyclesIn(sub) {
		nodeNames := []string{}
		for _, node := range cycle {
			if resourceStringer, ok := node.(osgraph.ResourceNode); ok {
				nodeNames = append(nodeNames, resourceStringer.ResourceString())
			}
		}

		markers = append(markers, osgraph.Marker{
			Node:         cycle[0],
			RelatedNodes: cycle,

			Severity: osgraph.WarningSeverity,
			Key:      CyclicBuildConfigWarning,
			Message:  fmt.Sprintf("Cycle detected in build configurations: %s", strings.Join(nodeNames, " -> ")),
		})

	}

	return markers
}
Exemple #3
0
// FindOverlappingHPAs scans the graph in search of HorizontalPodAutoscalers that are attempting to scale the same set of pods.
// This can occur in two ways:
//   - 1. label selectors for two ReplicationControllers/DeploymentConfigs/etc overlap
//   - 2. multiple HorizontalPodAutoscalers are attempting to scale the same ReplicationController/DeploymentConfig/etc
// Case 1 is handled by deconflicting the area of influence of ReplicationControllers/DeploymentConfigs/etc, and therefore we
// can assume that it will be handled before this step. Therefore, we are only concerned with finding HPAs that are trying to
// scale the same resources.
//
// The algorithm that is used to implement this check is described as follows:
//  - create a sub-graph containing only HPA nodes and other nodes that can be scaled, as well as any scaling edges or other
//    edges used to connect between objects that can be scaled
//  - for every resulting edge in the new sub-graph, create an edge in the reverse direction
//  - find the shortest paths between all HPA nodes in the graph
//  - shortest paths connecting two horizontal pod autoscalers are used to create markers for the graph
func FindOverlappingHPAs(graph osgraph.Graph, namer osgraph.Namer) []osgraph.Marker {
	markers := []osgraph.Marker{}

	nodeFilter := osgraph.NodesOfKind(
		kubenodes.HorizontalPodAutoscalerNodeKind,
		kubenodes.ReplicationControllerNodeKind,
		deploynodes.DeploymentConfigNodeKind,
	)
	edgeFilter := osgraph.EdgesOfKind(
		kubegraph.ScalingEdgeKind,
		deploygraph.DeploymentEdgeKind,
		kubeedges.ManagedByControllerEdgeKind,
	)

	hpaSubGraph := graph.Subgraph(nodeFilter, edgeFilter)
	for _, edge := range hpaSubGraph.Edges() {
		osgraph.AddReversedEdge(hpaSubGraph, edge.From(), edge.To(), sets.NewString())
	}

	hpaNodes := hpaSubGraph.NodesByKind(kubenodes.HorizontalPodAutoscalerNodeKind)

	for _, firstHPA := range hpaNodes {
		// we can use Dijkstra's algorithm as we know we do not have any negative edge weights
		shortestPaths := path.DijkstraFrom(firstHPA, hpaSubGraph)

		for _, secondHPA := range hpaNodes {
			if firstHPA == secondHPA {
				continue
			}

			shortestPath, _ := shortestPaths.To(secondHPA)

			if shortestPath == nil {
				// if two HPAs have no path between them, no error exists
				continue
			}

			markers = append(markers, osgraph.Marker{
				Node:         firstHPA,
				Severity:     osgraph.WarningSeverity,
				RelatedNodes: shortestPath[1:],
				Key:          HPAOverlappingScaleRefWarning,
				Message: fmt.Sprintf("%s and %s overlap because they both attempt to scale %s",
					namer.ResourceName(firstHPA), namer.ResourceName(secondHPA), nameList(shortestPath[1:len(shortestPath)-1], namer)),
			})
		}
	}

	return markers
}
Exemple #4
0
// subgraphWithoutPrunableImages creates a subgraph from g with prunable image
// nodes excluded.
func subgraphWithoutPrunableImages(g graph.Graph, prunableImageIDs graph.NodeSet) graph.Graph {
	return g.Subgraph(
		func(g graph.Interface, node gonum.Node) bool {
			return !prunableImageIDs.Has(node.ID())
		},
		func(g graph.Interface, head, tail gonum.Node, edgeKinds util.StringSet) bool {
			if prunableImageIDs.Has(head.ID()) {
				return false
			}
			if prunableImageIDs.Has(tail.ID()) {
				return false
			}
			return true
		},
	)
}
Exemple #5
0
// subgraphWithoutPrunableImages creates a subgraph from g with prunable image
// nodes excluded.
func subgraphWithoutPrunableImages(g graph.Graph, prunableImageIDs graph.NodeSet) graph.Graph {
	return g.Subgraph(
		func(g graph.Interface, node gonum.Node) bool {
			return !prunableImageIDs.Has(node.ID())
		},
		func(g graph.Interface, from, to gonum.Node, edgeKinds sets.String) bool {
			if prunableImageIDs.Has(from.ID()) {
				return false
			}
			if prunableImageIDs.Has(to.ID()) {
				return false
			}
			return true
		},
	)
}
Exemple #6
0
// 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
}