package graph import ( "fmt" "reflect" "sort" "strings" "github.com/gonum/graph" "github.com/gonum/graph/concrete" "github.com/gonum/graph/encoding/dot" "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/util/sets" ) type Node struct { concrete.Node UniqueName } // DOTAttributes implements an attribute getter for the DOT encoding func (n Node) DOTAttributes() []dot.Attribute { return []dot.Attribute{{Key: "label", Value: fmt.Sprintf("%q", n.UniqueName)}} } // ExistenceChecker is an interface for those nodes that can be created without a backing object. // This can happen when a node wants an edge to a non-existent node. We know the node should exist, // The graph needs something in that location to track the information we have about the node, but the // backing object doesn't exist. type ExistenceChecker interface { // Found returns false if the node represents an object that we don't have the backing object for Found() bool } type UniqueName string type UniqueNameFunc func(obj interface{}) UniqueName func (n UniqueName) UniqueName() string { return string(n) } func (n UniqueName) String() string { return string(n) } type uniqueNamer interface { UniqueName() UniqueName } type NodeFinder interface { Find(name UniqueName) graph.Node } // UniqueNodeInitializer is a graph that allows nodes with a unique name to be added without duplication. // If the node is newly added, true will be returned. type UniqueNodeInitializer interface { FindOrCreate(name UniqueName, fn NodeInitializerFunc) (graph.Node, bool) } type NodeInitializerFunc func(Node) graph.Node func EnsureUnique(g UniqueNodeInitializer, name UniqueName, fn NodeInitializerFunc) graph.Node { node, _ := g.FindOrCreate(name, fn) return node } type MutableDirectedEdge interface { AddEdge(from, to graph.Node, edgeKind string) } type MutableUniqueGraph interface { graph.Mutable MutableDirectedEdge UniqueNodeInitializer NodeFinder } type Edge struct { concrete.Edge kinds sets.String } func NewEdge(from, to graph.Node, kinds ...string) Edge { return Edge{concrete.Edge{F: from, T: to}, sets.NewString(kinds...)} } func (e Edge) Kinds() sets.String { return e.kinds } func (e Edge) IsKind(kind string) bool { return e.kinds.Has(kind) } // DOTAttributes implements an attribute getter for the DOT encoding func (e Edge) DOTAttributes() []dot.Attribute { return []dot.Attribute{{Key: "label", Value: fmt.Sprintf("%q", strings.Join(e.Kinds().List(), ","))}} } type GraphDescriber interface { Name(node graph.Node) string Kind(node graph.Node) string Object(node graph.Node) interface{} EdgeKinds(edge graph.Edge) sets.String } type Interface interface { graph.Directed GraphDescriber MutableUniqueGraph Edges() []graph.Edge } type Namer interface { ResourceName(obj interface{}) string } type namer struct{} var DefaultNamer Namer = namer{} func (namer) ResourceName(obj interface{}) string { switch t := obj.(type) { case uniqueNamer: return t.UniqueName().String() default: return reflect.TypeOf(obj).String() } } type Graph struct { // the standard graph graph.Directed // helper methods for switching on the kind and types of the node GraphDescriber // exposes the public interface for adding nodes uniqueNamedGraph // the internal graph object, which allows edges and nodes to be directly added internal *concrete.DirectedGraph } // Graph must implement MutableUniqueGraph var _ MutableUniqueGraph = Graph{} // New initializes a graph from input to output. func New() Graph { g := concrete.NewDirectedGraph() return Graph{ Directed: g, GraphDescriber: typedGraph{}, uniqueNamedGraph: newUniqueNamedGraph(g), internal: g, } } // Edges returns all the edges of the graph. Note that the returned set // will have no specific ordering. func (g Graph) Edges() []graph.Edge { return g.internal.Edges() } func (g Graph) String() string { ret := "" nodes := g.Nodes() sort.Sort(ByID(nodes)) for _, node := range nodes { ret += fmt.Sprintf("%d: %v\n", node.ID(), g.GraphDescriber.Name(node)) // can't use SuccessorEdges, because I want stable ordering successors := g.From(node) sort.Sort(ByID(successors)) for _, successor := range successors { edge := g.Edge(node, successor) kinds := g.EdgeKinds(edge) for _, kind := range kinds.List() { ret += fmt.Sprintf("\t%v to %d: %v\n", kind, successor.ID(), g.GraphDescriber.Name(successor)) } } } return ret } // ByID is a sorted group of nodes by ID type ByID []graph.Node func (m ByID) Len() int { return len(m) } func (m ByID) Swap(i, j int) { m[i], m[j] = m[j], m[i] } func (m ByID) Less(i, j int) bool { return m[i].ID() < m[j].ID() } // SyntheticNodes returns back the set of nodes that were created in response to edge requests, but did not exist func (g Graph) SyntheticNodes() []graph.Node { ret := []graph.Node{} nodes := g.Nodes() sort.Sort(ByID(nodes)) for _, node := range nodes { if potentiallySyntheticNode, ok := node.(ExistenceChecker); ok { if !potentiallySyntheticNode.Found() { ret = append(ret, node) } } } return ret } // NodesByKind returns all the nodes of the graph with the provided kinds func (g Graph) NodesByKind(nodeKinds ...string) []graph.Node { ret := []graph.Node{} kinds := sets.NewString(nodeKinds...) for _, node := range g.internal.Nodes() { if kinds.Has(g.Kind(node)) { ret = append(ret, node) } } return ret } // RootNodes returns all the roots of this graph. func (g Graph) RootNodes() []graph.Node { roots := []graph.Node{} for _, n := range g.Nodes() { if len(g.To(n)) != 0 { continue } roots = append(roots, n) } return roots } // PredecessorEdges invokes fn with all of the predecessor edges of node that have the specified // edge kind. func (g Graph) PredecessorEdges(node graph.Node, fn EdgeFunc, edgeKinds ...string) { for _, n := range g.To(node) { edge := g.Edge(n, node) kinds := g.EdgeKinds(edge) if kinds.HasAny(edgeKinds...) { fn(g, n, node, kinds) } } } // SuccessorEdges invokes fn with all of the successor edges of node that have the specified // edge kind. func (g Graph) SuccessorEdges(node graph.Node, fn EdgeFunc, edgeKinds ...string) { for _, n := range g.From(node) { edge := g.Edge(node, n) kinds := g.EdgeKinds(edge) if kinds.HasAny(edgeKinds...) { fn(g, n, node, kinds) } } } // OutboundEdges returns all the outbound edges from node that are in the list of edgeKinds // if edgeKinds is empty, then all edges are returned func (g Graph) OutboundEdges(node graph.Node, edgeKinds ...string) []graph.Edge { ret := []graph.Edge{} for _, n := range g.From(node) { edge := g.Edge(node, n) if edge == nil { continue } if len(edgeKinds) == 0 || g.EdgeKinds(edge).HasAny(edgeKinds...) { ret = append(ret, edge) } } return ret } // InboundEdges returns all the inbound edges to node that are in the list of edgeKinds // if edgeKinds is empty, then all edges are returned func (g Graph) InboundEdges(node graph.Node, edgeKinds ...string) []graph.Edge { ret := []graph.Edge{} for _, n := range g.To(node) { edge := g.Edge(n, node) if edge == nil { continue } if len(edgeKinds) == 0 || g.EdgeKinds(edge).HasAny(edgeKinds...) { ret = append(ret, edge) } } return ret } // PredecessorNodesByEdgeKind returns all the predecessor nodes of the given node // that can be reached via edges of the provided kinds func (g Graph) PredecessorNodesByEdgeKind(node graph.Node, edgeKinds ...string) []graph.Node { ret := []graph.Node{} for _, inboundEdges := range g.InboundEdges(node, edgeKinds...) { ret = append(ret, inboundEdges.From()) } return ret } // SuccessorNodesByEdgeKind returns all the successor nodes of the given node // that can be reached via edges of the provided kinds func (g Graph) SuccessorNodesByEdgeKind(node graph.Node, edgeKinds ...string) []graph.Node { ret := []graph.Node{} for _, outboundEdge := range g.OutboundEdges(node, edgeKinds...) { ret = append(ret, outboundEdge.To()) } return ret } func (g Graph) SuccessorNodesByNodeAndEdgeKind(node graph.Node, nodeKind, edgeKind string) []graph.Node { ret := []graph.Node{} for _, successor := range g.SuccessorNodesByEdgeKind(node, edgeKind) { if g.Kind(successor) != nodeKind { continue } ret = append(ret, successor) } return ret } func (g Graph) AddNode(n graph.Node) { g.internal.AddNode(n) } // AddEdge implements MutableUniqueGraph func (g Graph) AddEdge(from, to graph.Node, edgeKind string) { // a Contains edge has semantic meaning for osgraph.Graph objects. It never makes sense // to allow a single object to be "contained" by multiple nodes. if edgeKind == ContainsEdgeKind { // check incoming edges on the 'to' node to be certain that we aren't already contained containsEdges := g.InboundEdges(to, ContainsEdgeKind) if len(containsEdges) != 0 { // TODO consider changing the AddEdge API to make this cleaner. This is a pretty severe programming error panic(fmt.Sprintf("%v is already contained by %v", to, containsEdges)) } } kinds := sets.NewString(edgeKind) if existingEdge := g.Edge(from, to); existingEdge != nil { kinds.Insert(g.EdgeKinds(existingEdge).List()...) } g.internal.SetEdge(NewEdge(from, to, kinds.List()...), 1.0) } // addEdges adds the specified edges, filtered by the provided edge connection // function. func (g Graph) addEdges(edges []graph.Edge, fn EdgeFunc) { for _, e := range edges { switch t := e.(type) { case concrete.WeightedEdge: if fn(g, t.From(), t.To(), t.Edge.(Edge).Kinds()) { g.internal.SetEdge(t.Edge.(Edge), t.Cost) } case Edge: if fn(g, t.From(), t.To(), t.Kinds()) { g.internal.SetEdge(t, 1.0) } default: panic("bad edge") } } } // NodeFunc is passed a new graph, a node in the graph, and should return true if the // node should be included. type NodeFunc func(g Interface, n graph.Node) bool // NodesOfKind returns a new NodeFunc accepting the provided kinds of nodes // If no kinds are specified, the returned NodeFunc will accept all nodes func NodesOfKind(kinds ...string) NodeFunc { if len(kinds) == 0 { return func(g Interface, n graph.Node) bool { return true } } allowedKinds := sets.NewString(kinds...) return func(g Interface, n graph.Node) bool { return allowedKinds.Has(g.Kind(n)) } } // EdgeFunc is passed a new graph, an edge in the current graph, and should mutate // the new graph as needed. If true is returned, the existing edge will be added to the graph. type EdgeFunc func(g Interface, from, to graph.Node, edgeKinds sets.String) bool // EdgesOfKind returns a new EdgeFunc accepting the provided kinds of edges // If no kinds are specified, the returned EdgeFunc will accept all edges func EdgesOfKind(kinds ...string) EdgeFunc { if len(kinds) == 0 { return func(g Interface, from, to graph.Node, edgeKinds sets.String) bool { return true } } allowedKinds := sets.NewString(kinds...) return func(g Interface, from, to graph.Node, edgeKinds sets.String) bool { return allowedKinds.HasAny(edgeKinds.List()...) } } // RemoveInboundEdges returns a new EdgeFunc dismissing any inbound edges to // the provided set of nodes func RemoveInboundEdges(nodes []graph.Node) EdgeFunc { return func(g Interface, from, to graph.Node, edgeKinds sets.String) bool { for _, node := range nodes { if node == to { return false } } return true } } func RemoveOutboundEdges(nodes []graph.Node) EdgeFunc { return func(g Interface, from, to graph.Node, edgeKinds sets.String) bool { for _, node := range nodes { if node == from { return false } } return true } } // EdgeSubgraph returns the directed subgraph with only the edges that match the // provided function. func (g Graph) EdgeSubgraph(edgeFn EdgeFunc) Graph { out := New() for _, node := range g.Nodes() { out.internal.AddNode(node) } out.addEdges(g.internal.Edges(), edgeFn) return out } // Subgraph returns the directed subgraph with only the nodes and edges that match the // provided functions. func (g Graph) Subgraph(nodeFn NodeFunc, edgeFn EdgeFunc) Graph { out := New() for _, node := range g.Nodes() { if nodeFn(out, node) { out.internal.AddNode(node) } } out.addEdges(g.internal.Edges(), edgeFn) return out } // SubgraphWithNodes returns the directed subgraph with only the listed nodes and edges that // match the provided function. func (g Graph) SubgraphWithNodes(nodes []graph.Node, fn EdgeFunc) Graph { out := New() for _, node := range nodes { out.internal.AddNode(node) } out.addEdges(g.internal.Edges(), fn) return out } // ConnectedEdgeSubgraph creates a new graph that iterates through all edges in the graph // and includes all edges the provided function returns true for. Nodes not referenced by // an edge will be dropped unless the function adds them explicitly. func (g Graph) ConnectedEdgeSubgraph(fn EdgeFunc) Graph { out := New() out.addEdges(g.internal.Edges(), fn) return out } // AllNodes includes all nodes in the graph func AllNodes(g Interface, node graph.Node) bool { return true } // ExistingDirectEdge returns true if both from and to already exist in the graph and the edge kind is // not ReferencedByEdgeKind (the generic reverse edge kind). This will purge the graph of any // edges created by AddReversedEdge. func ExistingDirectEdge(g Interface, from, to graph.Node, edgeKinds sets.String) bool { return !edgeKinds.Has(ReferencedByEdgeKind) && g.Has(from) && g.Has(to) } // ReverseExistingDirectEdge reverses the order of the edge and drops the existing edge only if // both from and to already exist in the graph and the edge kind is not ReferencedByEdgeKind // (the generic reverse edge kind). func ReverseExistingDirectEdge(g Interface, from, to graph.Node, edgeKinds sets.String) bool { return ExistingDirectEdge(g, from, to, edgeKinds) && ReverseGraphEdge(g, from, to, edgeKinds) } // ReverseGraphEdge reverses the order of the edge and drops the existing edge. func ReverseGraphEdge(g Interface, from, to graph.Node, edgeKinds sets.String) bool { for edgeKind := range edgeKinds { g.AddEdge(to, from, edgeKind) } return false } // AddReversedEdge adds a reversed edge for every passed edge and preserves the existing // edge. Used to convert a one directional edge into a bidirectional edge, but will // create duplicate edges if a bidirectional edge between two nodes already exists. func AddReversedEdge(g Interface, from, to graph.Node, edgeKinds sets.String) bool { g.AddEdge(to, from, ReferencedByEdgeKind) return true } // AddGraphEdgesTo returns an EdgeFunc that will add the selected edges to the passed // graph. func AddGraphEdgesTo(g Interface) EdgeFunc { return func(_ Interface, from, to graph.Node, edgeKinds sets.String) bool { for edgeKind := range edgeKinds { g.AddEdge(from, to, edgeKind) } return false } } type uniqueNamedGraph struct { graph.Mutable names map[UniqueName]graph.Node } func newUniqueNamedGraph(g graph.Mutable) uniqueNamedGraph { return uniqueNamedGraph{ Mutable: g, names: make(map[UniqueName]graph.Node), } } func (g uniqueNamedGraph) FindOrCreate(name UniqueName, fn NodeInitializerFunc) (graph.Node, bool) { if node, ok := g.names[name]; ok { return node, true } id := g.NewNodeID() node := fn(Node{concrete.Node(id), name}) g.names[name] = node g.AddNode(node) return node, false } func (g uniqueNamedGraph) Find(name UniqueName) graph.Node { if node, ok := g.names[name]; ok { return node } return nil } type typedGraph struct{} func (g typedGraph) Name(node graph.Node) string { switch t := node.(type) { case fmt.Stringer: return t.String() case uniqueNamer: return t.UniqueName().String() default: return fmt.Sprintf("", node.ID()) } } type objectifier interface { Object() interface{} } func (g typedGraph) Object(node graph.Node) interface{} { switch t := node.(type) { case objectifier: return t.Object() default: return nil } } type kind interface { Kind() string } func (g typedGraph) Kind(node graph.Node) string { if k, ok := node.(kind); ok { return k.Kind() } return UnknownNodeKind } func (g typedGraph) EdgeKinds(edge graph.Edge) sets.String { var e Edge switch t := edge.(type) { case concrete.WeightedEdge: e = t.Edge.(Edge) case Edge: e = t default: return sets.NewString(UnknownEdgeKind) } return e.Kinds() } type NodeSet map[int]struct{} func (n NodeSet) Has(id int) bool { _, ok := n[id] return ok } func (n NodeSet) Add(id int) { n[id] = struct{}{} } func NodesByKind(g Interface, nodes []graph.Node, kinds ...string) [][]graph.Node { buckets := make(map[string]int) for i, kind := range kinds { buckets[kind] = i } if nodes == nil { nodes = g.Nodes() } last := len(kinds) result := make([][]graph.Node, last+1) for _, node := range nodes { if bucket, ok := buckets[g.Kind(node)]; ok { result[bucket] = append(result[bucket], node) } else { result[last] = append(result[last], node) } } return result } // IsFromDifferentNamespace returns if a node is in a different namespace // than the one provided. func IsFromDifferentNamespace(namespace string, node graph.Node) bool { potentiallySyntheticNode, ok := node.(ExistenceChecker) if !ok || potentiallySyntheticNode.Found() { return false } objectified, ok := node.(objectifier) if !ok { return false } object, err := meta.Accessor(objectified) if err != nil { return false } return object.GetNamespace() != namespace } func pathCovered(path []graph.Node, paths map[int][]graph.Node) bool { l := len(path) for _, existing := range paths { if l >= len(existing) { continue } if pathEqual(path, existing) { return true } } return false } func pathEqual(a, b []graph.Node) bool { for i := range a { if a[i] != b[i] { return false } } return true }