| ... | ... |
@@ -3,9 +3,12 @@ package graph |
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 | 5 |
"io" |
| 6 |
+ "sort" |
|
| 6 | 7 |
|
| 7 | 8 |
"github.com/gonum/graph" |
| 8 | 9 |
"github.com/gonum/graph/concrete" |
| 10 |
+ |
|
| 11 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/util" |
|
| 9 | 12 |
) |
| 10 | 13 |
|
| 11 | 14 |
type Node struct {
|
| ... | ... |
@@ -109,6 +112,35 @@ func New() Graph {
|
| 109 | 109 |
} |
| 110 | 110 |
} |
| 111 | 111 |
|
| 112 |
+func (g Graph) String() string {
|
|
| 113 |
+ ret := "" |
|
| 114 |
+ |
|
| 115 |
+ nodeList := g.NodeList() |
|
| 116 |
+ sort.Sort(SortedNodeList(nodeList)) |
|
| 117 |
+ for _, node := range nodeList {
|
|
| 118 |
+ ret += fmt.Sprintf("%d: %v\n", node.ID(), g.GraphDescriber.Name(node))
|
|
| 119 |
+ |
|
| 120 |
+ // can't use SuccessorEdges, because I want stable ordering |
|
| 121 |
+ successors := g.Successors(node) |
|
| 122 |
+ sort.Sort(SortedNodeList(successors)) |
|
| 123 |
+ for _, successor := range successors {
|
|
| 124 |
+ edge := g.EdgeBetween(node, successor) |
|
| 125 |
+ kind := g.EdgeKind(edge) |
|
| 126 |
+ ret += fmt.Sprintf("\t%v to %d: %v\n", kind, successor.ID(), g.GraphDescriber.Name(successor))
|
|
| 127 |
+ } |
|
| 128 |
+ } |
|
| 129 |
+ |
|
| 130 |
+ return ret |
|
| 131 |
+} |
|
| 132 |
+ |
|
| 133 |
+type SortedNodeList []graph.Node |
|
| 134 |
+ |
|
| 135 |
+func (m SortedNodeList) Len() int { return len(m) }
|
|
| 136 |
+func (m SortedNodeList) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
|
|
| 137 |
+func (m SortedNodeList) Less(i, j int) bool {
|
|
| 138 |
+ return m[i].ID() < m[j].ID() |
|
| 139 |
+} |
|
| 140 |
+ |
|
| 112 | 141 |
// RootNodes returns all the roots of this graph. |
| 113 | 142 |
func (g Graph) RootNodes() []graph.Node {
|
| 114 | 143 |
roots := []graph.Node{}
|
| ... | ... |
@@ -153,6 +185,38 @@ func (g Graph) SuccessorEdges(node graph.Node, fn EdgeFunc, edgeKind ...string) |
| 153 | 153 |
} |
| 154 | 154 |
} |
| 155 | 155 |
|
| 156 |
+// OutboundEdges returns all the outbound edges from node that are in the list of edgeKinds |
|
| 157 |
+// if edgeKinds is empty, then all edges are returned |
|
| 158 |
+func (g Graph) OutboundEdges(node graph.Node, edgeKinds ...string) []graph.Edge {
|
|
| 159 |
+ ret := []graph.Edge{}
|
|
| 160 |
+ |
|
| 161 |
+ allowedKinds := util.NewStringSet(edgeKinds...) |
|
| 162 |
+ for _, n := range g.Successors(node) {
|
|
| 163 |
+ edge := g.EdgeBetween(n, node) |
|
| 164 |
+ if len(allowedKinds) == 0 || allowedKinds.Has(g.EdgeKind(edge)) {
|
|
| 165 |
+ ret = append(ret, edge) |
|
| 166 |
+ } |
|
| 167 |
+ } |
|
| 168 |
+ |
|
| 169 |
+ return ret |
|
| 170 |
+} |
|
| 171 |
+ |
|
| 172 |
+// InboundEdges returns all the inbound edges to node that are in the list of edgeKinds |
|
| 173 |
+// if edgeKinds is empty, then all edges are returned |
|
| 174 |
+func (g Graph) InboundEdges(node graph.Node, edgeKinds ...string) []graph.Edge {
|
|
| 175 |
+ ret := []graph.Edge{}
|
|
| 176 |
+ |
|
| 177 |
+ allowedKinds := util.NewStringSet(edgeKinds...) |
|
| 178 |
+ for _, n := range g.Predecessors(node) {
|
|
| 179 |
+ edge := g.EdgeBetween(n, node) |
|
| 180 |
+ if len(allowedKinds) == 0 || allowedKinds.Has(g.EdgeKind(edge)) {
|
|
| 181 |
+ ret = append(ret, edge) |
|
| 182 |
+ } |
|
| 183 |
+ } |
|
| 184 |
+ |
|
| 185 |
+ return ret |
|
| 186 |
+} |
|
| 187 |
+ |
|
| 156 | 188 |
func (g Graph) EdgeList() []graph.Edge {
|
| 157 | 189 |
return g.internal.EdgeList() |
| 158 | 190 |
} |
| ... | ... |
@@ -163,6 +227,16 @@ func (g Graph) AddNode(n graph.Node) {
|
| 163 | 163 |
|
| 164 | 164 |
// AddEdge implements MutableUniqueGraph |
| 165 | 165 |
func (g Graph) AddEdge(head, tail graph.Node, edgeKind string) {
|
| 166 |
+ // a Contains edge has semantic meaning for osgraph.Graph objects. It never makes sense |
|
| 167 |
+ // to allow a single object to be "contained" by multiple nodes. |
|
| 168 |
+ if edgeKind == ContainsEdgeKind {
|
|
| 169 |
+ // check incoming edges on the tail to be certain that we aren't already contained |
|
| 170 |
+ containsEdges := g.InboundEdges(tail, ContainsEdgeKind) |
|
| 171 |
+ if len(containsEdges) != 0 {
|
|
| 172 |
+ // TODO consider changing the AddEdge API to make this cleaner. This is a pretty severe programming error |
|
| 173 |
+ panic(fmt.Sprintf("%v is already contained by %v", tail, containsEdges))
|
|
| 174 |
+ } |
|
| 175 |
+ } |
|
| 166 | 176 |
g.internal.AddDirectedEdge(NewEdge(head, tail, edgeKind), 1) |
| 167 | 177 |
} |
| 168 | 178 |
|
| ... | ... |
@@ -3,6 +3,8 @@ package graph |
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 | 5 |
|
| 6 |
+ "github.com/gonum/graph" |
|
| 7 |
+ |
|
| 6 | 8 |
kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
| 7 | 9 |
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" |
| 8 | 10 |
) |
| ... | ... |
@@ -12,8 +14,13 @@ var ( |
| 12 | 12 |
) |
| 13 | 13 |
|
| 14 | 14 |
var ( |
| 15 |
- UnknownEdgeKind = "UnknownEdge" |
|
| 15 |
+ UnknownEdgeKind = "UnknownEdge" |
|
| 16 |
+ // ReferencedByEdgeKind is the kind to use if you're building reverse links that don't have a specific edge in the other direction |
|
| 17 |
+ // other uses are discouraged. You should create a kind for your edge |
|
| 16 | 18 |
ReferencedByEdgeKind = "ReferencedBy" |
| 19 |
+ // ContainsEdgeKind is the kind to use if one node's contents logically contain another node's contents. A given node can only have |
|
| 20 |
+ // a single inbound Contais edge. The code does not prevent contains cycles, but that's insane, don't do that. |
|
| 21 |
+ ContainsEdgeKind = "Contains" |
|
| 17 | 22 |
) |
| 18 | 23 |
|
| 19 | 24 |
func GetUniqueRuntimeObjectNodeName(nodeKind string, obj runtime.Object) UniqueName {
|
| ... | ... |
@@ -24,3 +31,43 @@ func GetUniqueRuntimeObjectNodeName(nodeKind string, obj runtime.Object) UniqueN |
| 24 | 24 |
|
| 25 | 25 |
return UniqueName(fmt.Sprintf("%s|%s/%s", nodeKind, meta.Namespace, meta.Name))
|
| 26 | 26 |
} |
| 27 |
+ |
|
| 28 |
+// GetTopLevelContainerNode traverses the reverse ContainsEdgeKind edges until it finds a node |
|
| 29 |
+// that does not have an inbound ContainsEdgeKind edge. This could be the node itself |
|
| 30 |
+func GetTopLevelContainerNode(g Graph, containedNode graph.Node) graph.Node {
|
|
| 31 |
+ // my kingdom for a LinkedHashSet |
|
| 32 |
+ visited := map[int]bool{}
|
|
| 33 |
+ prevContainingNode := containedNode |
|
| 34 |
+ |
|
| 35 |
+ for {
|
|
| 36 |
+ visited[prevContainingNode.ID()] = true |
|
| 37 |
+ currContainingNode := GetContainingNode(g, prevContainingNode) |
|
| 38 |
+ |
|
| 39 |
+ if currContainingNode == nil {
|
|
| 40 |
+ return prevContainingNode |
|
| 41 |
+ } |
|
| 42 |
+ if _, alreadyVisited := visited[currContainingNode.ID()]; alreadyVisited {
|
|
| 43 |
+ panic(fmt.Sprintf("contains cycle in %v", visited))
|
|
| 44 |
+ } |
|
| 45 |
+ |
|
| 46 |
+ prevContainingNode = currContainingNode |
|
| 47 |
+ } |
|
| 48 |
+ |
|
| 49 |
+ // can't happen |
|
| 50 |
+ panic(fmt.Sprintf("math failed %v", visited))
|
|
| 51 |
+ return nil |
|
| 52 |
+} |
|
| 53 |
+ |
|
| 54 |
+// GetContainingNode returns the direct predecessor that is linked to the node by a ContainsEdgeKind. It returns |
|
| 55 |
+// nil if no container is found. |
|
| 56 |
+func GetContainingNode(g Graph, containedNode graph.Node) graph.Node {
|
|
| 57 |
+ for _, node := range g.Predecessors(containedNode) {
|
|
| 58 |
+ edge := g.EdgeBetween(node, containedNode) |
|
| 59 |
+ |
|
| 60 |
+ if g.EdgeKind(edge) == ContainsEdgeKind {
|
|
| 61 |
+ return node |
|
| 62 |
+ } |
|
| 63 |
+ } |
|
| 64 |
+ |
|
| 65 |
+ return nil |
|
| 66 |
+} |
| 27 | 67 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,69 @@ |
| 0 |
+package graph |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "testing" |
|
| 4 |
+ |
|
| 5 |
+ "github.com/gonum/graph" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+func makeTestNode(g MutableUniqueGraph, name string) graph.Node {
|
|
| 9 |
+ return EnsureUnique(g, |
|
| 10 |
+ UniqueName(name), |
|
| 11 |
+ func(node Node) graph.Node {
|
|
| 12 |
+ return node |
|
| 13 |
+ }, |
|
| 14 |
+ ) |
|
| 15 |
+} |
|
| 16 |
+ |
|
| 17 |
+func TestContainsNavigation(t *testing.T) {
|
|
| 18 |
+ g := New() |
|
| 19 |
+ |
|
| 20 |
+ aNode := makeTestNode(g, "a") |
|
| 21 |
+ bNode := makeTestNode(g, "b") |
|
| 22 |
+ cNode := makeTestNode(g, "c") |
|
| 23 |
+ |
|
| 24 |
+ g.AddEdge(aNode, bNode, ContainsEdgeKind) |
|
| 25 |
+ g.AddEdge(bNode, cNode, ContainsEdgeKind) |
|
| 26 |
+ |
|
| 27 |
+ topA := GetTopLevelContainerNode(g, aNode) |
|
| 28 |
+ if e, a := aNode.ID(), topA.ID(); e != a {
|
|
| 29 |
+ t.Errorf("expected %v, got %v", e, a)
|
|
| 30 |
+ } |
|
| 31 |
+ |
|
| 32 |
+ topB := GetTopLevelContainerNode(g, bNode) |
|
| 33 |
+ if e, a := aNode.ID(), topB.ID(); e != a {
|
|
| 34 |
+ t.Errorf("expected %v, got %v", e, a)
|
|
| 35 |
+ } |
|
| 36 |
+ |
|
| 37 |
+ topC := GetTopLevelContainerNode(g, cNode) |
|
| 38 |
+ if e, a := aNode.ID(), topC.ID(); e != a {
|
|
| 39 |
+ t.Errorf("expected %v, got %v", e, a)
|
|
| 40 |
+ } |
|
| 41 |
+ |
|
| 42 |
+ containsB := GetContainingNode(g, bNode) |
|
| 43 |
+ if e, a := aNode.ID(), containsB.ID(); e != a {
|
|
| 44 |
+ t.Errorf("expected %v, got %v", e, a)
|
|
| 45 |
+ } |
|
| 46 |
+ |
|
| 47 |
+ containsC := GetContainingNode(g, cNode) |
|
| 48 |
+ if e, a := bNode.ID(), containsC.ID(); e != a {
|
|
| 49 |
+ t.Errorf("expected %v, got %v", e, a)
|
|
| 50 |
+ } |
|
| 51 |
+} |
|
| 52 |
+ |
|
| 53 |
+func TestOnlyOneContainseEdge(t *testing.T) {
|
|
| 54 |
+ g := New() |
|
| 55 |
+ |
|
| 56 |
+ aNode := makeTestNode(g, "a") |
|
| 57 |
+ bNode := makeTestNode(g, "b") |
|
| 58 |
+ cNode := makeTestNode(g, "c") |
|
| 59 |
+ |
|
| 60 |
+ g.AddEdge(aNode, bNode, ContainsEdgeKind) |
|
| 61 |
+ |
|
| 62 |
+ defer func() {
|
|
| 63 |
+ if r := recover(); r == nil {
|
|
| 64 |
+ t.Errorf("expected to recover panic!")
|
|
| 65 |
+ } |
|
| 66 |
+ }() |
|
| 67 |
+ g.AddEdge(cNode, bNode, ContainsEdgeKind) |
|
| 68 |
+} |
| ... | ... |
@@ -9,6 +9,7 @@ import ( |
| 9 | 9 |
kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
| 10 | 10 |
|
| 11 | 11 |
osgraph "github.com/openshift/origin/pkg/api/graph" |
| 12 |
+ kubeedges "github.com/openshift/origin/pkg/api/kubegraph" |
|
| 12 | 13 |
kubegraph "github.com/openshift/origin/pkg/api/kubegraph/nodes" |
| 13 | 14 |
buildedges "github.com/openshift/origin/pkg/build/graph" |
| 14 | 15 |
buildgraph "github.com/openshift/origin/pkg/build/graph/nodes" |
| ... | ... |
@@ -37,6 +38,22 @@ type DeploymentFlow struct {
|
| 37 | 37 |
Images []ImagePipeline |
| 38 | 38 |
} |
| 39 | 39 |
|
| 40 |
+// ServiceReference is a service and the DeploymentConfigs it covers |
|
| 41 |
+type ServiceReference struct {
|
|
| 42 |
+ Service *kubegraph.ServiceNode |
|
| 43 |
+ CoveredDCs []*deploygraph.DeploymentConfigNode |
|
| 44 |
+ CoveredRCs []*kubegraph.ReplicationControllerNode |
|
| 45 |
+ CoveredPods []*kubegraph.PodNode |
|
| 46 |
+} |
|
| 47 |
+ |
|
| 48 |
+// ServiceGroup is a related set of resources that should be displayed together |
|
| 49 |
+// logically. They are usually sorted internally. |
|
| 50 |
+type ServiceGroup struct {
|
|
| 51 |
+ Services []ServiceReference |
|
| 52 |
+ Deployments []DeploymentFlow |
|
| 53 |
+ Builds []ImagePipeline |
|
| 54 |
+} |
|
| 55 |
+ |
|
| 40 | 56 |
// ImageTagLocation identifies the source or destination of an image. Represents |
| 41 | 57 |
// both a tag in a Docker image repository, as well as a tag in an OpenShift image stream. |
| 42 | 58 |
type ImageTagLocation interface {
|
| ... | ... |
@@ -195,16 +212,28 @@ func ServiceAndDeploymentGroups(g osgraph.Graph) []ServiceGroup {
|
| 195 | 195 |
svcs, dcs, _ := matches[0], matches[1], matches[2] |
| 196 | 196 |
|
| 197 | 197 |
for _, n := range svcs {
|
| 198 |
- covers := []*deploygraph.DeploymentConfigNode{}
|
|
| 198 |
+ coveredDCs := []*deploygraph.DeploymentConfigNode{}
|
|
| 199 |
+ coveredRCs := []*kubegraph.ReplicationControllerNode{}
|
|
| 200 |
+ coveredPods := []*kubegraph.PodNode{}
|
|
| 199 | 201 |
for _, neighbor := range other.Neighbors(n) {
|
| 200 | 202 |
switch other.EdgeKind(g.EdgeBetween(neighbor, n)) {
|
| 201 |
- case deployedges.ExposedThroughServiceEdgeKind: |
|
| 202 |
- covers = append(covers, neighbor.(*deploygraph.DeploymentConfigNode)) |
|
| 203 |
+ case kubeedges.ExposedThroughServiceEdgeKind: |
|
| 204 |
+ containerNode := osgraph.GetTopLevelContainerNode(g, neighbor) |
|
| 205 |
+ switch castCoveredNode := containerNode.(type) {
|
|
| 206 |
+ case *deploygraph.DeploymentConfigNode: |
|
| 207 |
+ coveredDCs = append(coveredDCs, castCoveredNode) |
|
| 208 |
+ case *kubegraph.ReplicationControllerNode: |
|
| 209 |
+ coveredRCs = append(coveredRCs, castCoveredNode) |
|
| 210 |
+ case *kubegraph.PodNode: |
|
| 211 |
+ coveredPods = append(coveredPods, castCoveredNode) |
|
| 212 |
+ } |
|
| 203 | 213 |
} |
| 204 | 214 |
} |
| 205 | 215 |
group.Services = append(group.Services, ServiceReference{
|
| 206 |
- Service: n.(*kubegraph.ServiceNode), |
|
| 207 |
- Covers: covers, |
|
| 216 |
+ Service: n.(*kubegraph.ServiceNode), |
|
| 217 |
+ CoveredDCs: coveredDCs, |
|
| 218 |
+ CoveredRCs: coveredRCs, |
|
| 219 |
+ CoveredPods: coveredPods, |
|
| 208 | 220 |
}) |
| 209 | 221 |
} |
| 210 | 222 |
sort.Sort(SortedServiceReferences(group.Services)) |
| ... | ... |
@@ -243,7 +272,7 @@ func ServiceAndDeploymentGroups(g osgraph.Graph) []ServiceGroup {
|
| 243 | 243 |
// Services and DeploymentConfigs. |
| 244 | 244 |
func UncoveredDeploymentFlowEdges(covered osgraph.NodeSet) osgraph.EdgeFunc {
|
| 245 | 245 |
return func(g osgraph.Interface, head, tail graph.Node, edgeKind string) bool {
|
| 246 |
- if edgeKind == deployedges.ExposedThroughServiceEdgeKind {
|
|
| 246 |
+ if edgeKind == kubeedges.ExposedThroughServiceEdgeKind {
|
|
| 247 | 247 |
return osgraph.AddReversedEdge(g, head, tail, osgraph.ReferencedByEdgeKind) |
| 248 | 248 |
} |
| 249 | 249 |
if covered.Has(head.ID()) && covered.Has(tail.ID()) {
|
| ... | ... |
@@ -265,20 +294,6 @@ func UncoveredDeploymentFlowNodes(covered osgraph.NodeSet) osgraph.NodeFunc {
|
| 265 | 265 |
} |
| 266 | 266 |
} |
| 267 | 267 |
|
| 268 |
-// ServiceReference is a service and the DeploymentConfigs it covers |
|
| 269 |
-type ServiceReference struct {
|
|
| 270 |
- Service *kubegraph.ServiceNode |
|
| 271 |
- Covers []*deploygraph.DeploymentConfigNode |
|
| 272 |
-} |
|
| 273 |
- |
|
| 274 |
-// ServiceGroup is a related set of resources that should be displayed together |
|
| 275 |
-// logically. They are usually sorted internally. |
|
| 276 |
-type ServiceGroup struct {
|
|
| 277 |
- Services []ServiceReference |
|
| 278 |
- Deployments []DeploymentFlow |
|
| 279 |
- Builds []ImagePipeline |
|
| 280 |
-} |
|
| 281 |
- |
|
| 282 | 268 |
// Sorts on the provided objects. |
| 283 | 269 |
|
| 284 | 270 |
type SortedServiceReferences []ServiceReference |
| ... | ... |
@@ -11,6 +11,7 @@ import ( |
| 11 | 11 |
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" |
| 12 | 12 |
|
| 13 | 13 |
osgraph "github.com/openshift/origin/pkg/api/graph" |
| 14 |
+ kubeedges "github.com/openshift/origin/pkg/api/kubegraph" |
|
| 14 | 15 |
kubegraph "github.com/openshift/origin/pkg/api/kubegraph/nodes" |
| 15 | 16 |
buildapi "github.com/openshift/origin/pkg/build/api" |
| 16 | 17 |
buildedges "github.com/openshift/origin/pkg/build/graph" |
| ... | ... |
@@ -52,7 +53,7 @@ func TestGraph(t *testing.T) {
|
| 52 | 52 |
}, |
| 53 | 53 |
} |
| 54 | 54 |
|
| 55 |
- bcNode := buildgraph.EnsureBuildConfigNode(g, &buildapi.BuildConfig{
|
|
| 55 |
+ bc1Node := buildgraph.EnsureBuildConfigNode(g, &buildapi.BuildConfig{
|
|
| 56 | 56 |
ObjectMeta: kapi.ObjectMeta{Namespace: "default", Name: "build1"},
|
| 57 | 57 |
Triggers: []buildapi.BuildTriggerPolicy{
|
| 58 | 58 |
{
|
| ... | ... |
@@ -72,7 +73,7 @@ func TestGraph(t *testing.T) {
|
| 72 | 72 |
}, |
| 73 | 73 |
}, |
| 74 | 74 |
}) |
| 75 |
- buildedges.JoinBuilds(bcNode, builds) |
|
| 75 |
+ buildedges.JoinBuilds(bc1Node, builds) |
|
| 76 | 76 |
bcTestNode := buildgraph.EnsureBuildConfigNode(g, &buildapi.BuildConfig{
|
| 77 | 77 |
ObjectMeta: kapi.ObjectMeta{Namespace: "default", Name: "test"},
|
| 78 | 78 |
Parameters: buildapi.BuildParameters{
|
| ... | ... |
@@ -178,13 +179,14 @@ func TestGraph(t *testing.T) {
|
| 178 | 178 |
}, |
| 179 | 179 |
}) |
| 180 | 180 |
|
| 181 |
- deployedges.AddAllFullfillingDeploymentConfigEdges(g) |
|
| 181 |
+ kubeedges.AddAllExposedPodTemplateSpecEdges(g) |
|
| 182 | 182 |
buildedges.AddAllInputOutputEdges(g) |
| 183 | 183 |
deployedges.AddAllTriggerEdges(g) |
| 184 | 184 |
|
| 185 |
+ t.Log(g) |
|
| 186 |
+ |
|
| 185 | 187 |
ir, dc, bc, other := 0, 0, 0, 0 |
| 186 | 188 |
for _, node := range g.NodeList() {
|
| 187 |
- t.Logf("node: %d %v", node.ID(), node)
|
|
| 188 | 189 |
switch g.Object(node).(type) {
|
| 189 | 190 |
case *deployapi.DeploymentConfig: |
| 190 | 191 |
if g.Kind(node) != deploygraph.DeploymentConfigNodeKind {
|
| ... | ... |
@@ -206,18 +208,14 @@ func TestGraph(t *testing.T) {
|
| 206 | 206 |
other++ |
| 207 | 207 |
} |
| 208 | 208 |
} |
| 209 |
- if dc != 2 || bc != 3 || ir != 3 || other != 6 {
|
|
| 209 |
+ |
|
| 210 |
+ if dc != 2 || bc != 3 || ir != 3 || other != 12 {
|
|
| 210 | 211 |
t.Errorf("unexpected nodes: %d %d %d %d", dc, bc, ir, other)
|
| 211 | 212 |
} |
| 212 | 213 |
for _, edge := range g.EdgeList() {
|
| 213 | 214 |
if g.EdgeKind(edge) == osgraph.UnknownEdgeKind {
|
| 214 | 215 |
t.Errorf("edge reported unknown kind: %#v", edge)
|
| 215 | 216 |
} |
| 216 |
- t.Logf("edge: %v", edge)
|
|
| 217 |
- } |
|
| 218 |
- reverse := g.EdgeSubgraph(osgraph.ReverseGraphEdge) |
|
| 219 |
- for _, edge := range reverse.EdgeList() {
|
|
| 220 |
- t.Logf("redge: %v", edge)
|
|
| 221 | 217 |
} |
| 222 | 218 |
|
| 223 | 219 |
// imagestreamtag default/other:base-image |
| ... | ... |
@@ -233,7 +231,6 @@ func TestGraph(t *testing.T) {
|
| 233 | 233 |
if edge == nil {
|
| 234 | 234 |
t.Fatalf("failed to find edge between %d and %d", bcTestNode.ID(), istID)
|
| 235 | 235 |
} |
| 236 |
- |
|
| 237 | 236 |
if len(g.SubgraphWithNodes([]graph.Node{edge.Head(), edge.Tail()}, osgraph.ExistingDirectEdge).EdgeList()) != 1 {
|
| 238 | 237 |
t.Fatalf("expected one edge")
|
| 239 | 238 |
} |
| 240 | 239 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,68 @@ |
| 0 |
+package kubegraph |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/gonum/graph" |
|
| 4 |
+ |
|
| 5 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
|
| 6 |
+ |
|
| 7 |
+ osgraph "github.com/openshift/origin/pkg/api/graph" |
|
| 8 |
+ kubegraph "github.com/openshift/origin/pkg/api/kubegraph/nodes" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+const ( |
|
| 12 |
+ // ExposedThroughServiceEdgeKind is an edge that goes from a podtemplatespec or a pod to service. |
|
| 13 |
+ // The head should make the service's selector |
|
| 14 |
+ ExposedThroughServiceEdgeKind = "ExposedThroughService" |
|
| 15 |
+) |
|
| 16 |
+ |
|
| 17 |
+// AddExposedPodTemplateSpecEdges ensures that a directed edge exists between a service and all the PodTemplateSpecs |
|
| 18 |
+// in the graph that match the service selector |
|
| 19 |
+func AddExposedPodTemplateSpecEdges(g osgraph.MutableUniqueGraph, node *kubegraph.ServiceNode) {
|
|
| 20 |
+ if node.Service.Spec.Selector == nil {
|
|
| 21 |
+ return |
|
| 22 |
+ } |
|
| 23 |
+ query := labels.SelectorFromSet(node.Service.Spec.Selector) |
|
| 24 |
+ for _, n := range g.(graph.Graph).NodeList() {
|
|
| 25 |
+ switch target := n.(type) {
|
|
| 26 |
+ case *kubegraph.PodTemplateSpecNode: |
|
| 27 |
+ if query.Matches(labels.Set(target.PodTemplateSpec.Labels)) {
|
|
| 28 |
+ g.AddEdge(target, node, ExposedThroughServiceEdgeKind) |
|
| 29 |
+ } |
|
| 30 |
+ } |
|
| 31 |
+ } |
|
| 32 |
+} |
|
| 33 |
+ |
|
| 34 |
+// AddAllExposedPodTemplateSpecEdges calls AddExposedPodTemplateSpecEdges for every ServiceNode in the graph |
|
| 35 |
+func AddAllExposedPodTemplateSpecEdges(g osgraph.MutableUniqueGraph) {
|
|
| 36 |
+ for _, node := range g.(graph.Graph).NodeList() {
|
|
| 37 |
+ if serviceNode, ok := node.(*kubegraph.ServiceNode); ok {
|
|
| 38 |
+ AddExposedPodTemplateSpecEdges(g, serviceNode) |
|
| 39 |
+ } |
|
| 40 |
+ } |
|
| 41 |
+} |
|
| 42 |
+ |
|
| 43 |
+// AddExposedPodEdges ensures that a directed edge exists between a service and all the pods |
|
| 44 |
+// in the graph that match the service selector |
|
| 45 |
+func AddExposedPodEdges(g osgraph.MutableUniqueGraph, node *kubegraph.ServiceNode) {
|
|
| 46 |
+ if node.Service.Spec.Selector == nil {
|
|
| 47 |
+ return |
|
| 48 |
+ } |
|
| 49 |
+ query := labels.SelectorFromSet(node.Service.Spec.Selector) |
|
| 50 |
+ for _, n := range g.(graph.Graph).NodeList() {
|
|
| 51 |
+ switch target := n.(type) {
|
|
| 52 |
+ case *kubegraph.PodNode: |
|
| 53 |
+ if query.Matches(labels.Set(target.Labels)) {
|
|
| 54 |
+ g.AddEdge(target, node, ExposedThroughServiceEdgeKind) |
|
| 55 |
+ } |
|
| 56 |
+ } |
|
| 57 |
+ } |
|
| 58 |
+} |
|
| 59 |
+ |
|
| 60 |
+// AddAllExposedPodEdges calls AddExposedPodEdges for every ServiceNode in the graph |
|
| 61 |
+func AddAllExposedPodEdges(g osgraph.MutableUniqueGraph) {
|
|
| 62 |
+ for _, node := range g.(graph.Graph).NodeList() {
|
|
| 63 |
+ if serviceNode, ok := node.(*kubegraph.ServiceNode); ok {
|
|
| 64 |
+ AddExposedPodEdges(g, serviceNode) |
|
| 65 |
+ } |
|
| 66 |
+ } |
|
| 67 |
+} |
| ... | ... |
@@ -9,12 +9,27 @@ import ( |
| 9 | 9 |
) |
| 10 | 10 |
|
| 11 | 11 |
func EnsurePodNode(g osgraph.MutableUniqueGraph, pod *kapi.Pod) *PodNode {
|
| 12 |
- return osgraph.EnsureUnique(g, |
|
| 13 |
- PodNodeName(pod), |
|
| 12 |
+ podNodeName := PodNodeName(pod) |
|
| 13 |
+ podNode := osgraph.EnsureUnique(g, |
|
| 14 |
+ podNodeName, |
|
| 14 | 15 |
func(node osgraph.Node) graph.Node {
|
| 15 | 16 |
return &PodNode{node, pod}
|
| 16 | 17 |
}, |
| 17 | 18 |
).(*PodNode) |
| 19 |
+ |
|
| 20 |
+ podSpecNode := EnsurePodSpecNode(g, &pod.Spec, podNodeName) |
|
| 21 |
+ g.AddEdge(podNode, podSpecNode, osgraph.ContainsEdgeKind) |
|
| 22 |
+ |
|
| 23 |
+ return podNode |
|
| 24 |
+} |
|
| 25 |
+ |
|
| 26 |
+func EnsurePodSpecNode(g osgraph.MutableUniqueGraph, podSpec *kapi.PodSpec, ownerName osgraph.UniqueName) *PodSpecNode {
|
|
| 27 |
+ return osgraph.EnsureUnique(g, |
|
| 28 |
+ PodSpecNodeName(podSpec, ownerName), |
|
| 29 |
+ func(node osgraph.Node) graph.Node {
|
|
| 30 |
+ return &PodSpecNode{node, podSpec, ownerName}
|
|
| 31 |
+ }, |
|
| 32 |
+ ).(*PodSpecNode) |
|
| 18 | 33 |
} |
| 19 | 34 |
|
| 20 | 35 |
// EnsureServiceNode adds the provided service to the graph if it does not already exist. |
| ... | ... |
@@ -29,10 +44,48 @@ func EnsureServiceNode(g osgraph.MutableUniqueGraph, svc *kapi.Service) *Service |
| 29 | 29 |
|
| 30 | 30 |
// EnsureReplicationControllerNode adds a graph node for the ReplicationController if it does not already exist. |
| 31 | 31 |
func EnsureReplicationControllerNode(g osgraph.MutableUniqueGraph, rc *kapi.ReplicationController) *ReplicationControllerNode {
|
| 32 |
- return osgraph.EnsureUnique(g, |
|
| 33 |
- ReplicationControllerNodeName(rc), |
|
| 32 |
+ rcNodeName := ReplicationControllerNodeName(rc) |
|
| 33 |
+ rcNode := osgraph.EnsureUnique(g, |
|
| 34 |
+ rcNodeName, |
|
| 34 | 35 |
func(node osgraph.Node) graph.Node {
|
| 35 | 36 |
return &ReplicationControllerNode{node, rc}
|
| 36 | 37 |
}, |
| 37 | 38 |
).(*ReplicationControllerNode) |
| 39 |
+ |
|
| 40 |
+ rcSpecNode := EnsureReplicationControllerSpecNode(g, &rc.Spec, rcNodeName) |
|
| 41 |
+ g.AddEdge(rcNode, rcSpecNode, osgraph.ContainsEdgeKind) |
|
| 42 |
+ |
|
| 43 |
+ return rcNode |
|
| 44 |
+} |
|
| 45 |
+ |
|
| 46 |
+func EnsureReplicationControllerSpecNode(g osgraph.MutableUniqueGraph, rcSpec *kapi.ReplicationControllerSpec, ownerName osgraph.UniqueName) *ReplicationControllerSpecNode {
|
|
| 47 |
+ rcSpecName := ReplicationControllerSpecNodeName(rcSpec, ownerName) |
|
| 48 |
+ rcSpecNode := osgraph.EnsureUnique(g, |
|
| 49 |
+ rcSpecName, |
|
| 50 |
+ func(node osgraph.Node) graph.Node {
|
|
| 51 |
+ return &ReplicationControllerSpecNode{node, rcSpec, ownerName}
|
|
| 52 |
+ }, |
|
| 53 |
+ ).(*ReplicationControllerSpecNode) |
|
| 54 |
+ |
|
| 55 |
+ if rcSpec.Template != nil {
|
|
| 56 |
+ ptSpecNode := EnsurePodTemplateSpecNode(g, rcSpec.Template, rcSpecName) |
|
| 57 |
+ g.AddEdge(rcSpecNode, ptSpecNode, osgraph.ContainsEdgeKind) |
|
| 58 |
+ } |
|
| 59 |
+ |
|
| 60 |
+ return rcSpecNode |
|
| 61 |
+} |
|
| 62 |
+ |
|
| 63 |
+func EnsurePodTemplateSpecNode(g osgraph.MutableUniqueGraph, ptSpec *kapi.PodTemplateSpec, ownerName osgraph.UniqueName) *PodTemplateSpecNode {
|
|
| 64 |
+ ptSpecName := PodTemplateSpecNodeName(ptSpec, ownerName) |
|
| 65 |
+ ptSpecNode := osgraph.EnsureUnique(g, |
|
| 66 |
+ ptSpecName, |
|
| 67 |
+ func(node osgraph.Node) graph.Node {
|
|
| 68 |
+ return &PodTemplateSpecNode{node, ptSpec, ownerName}
|
|
| 69 |
+ }, |
|
| 70 |
+ ).(*PodTemplateSpecNode) |
|
| 71 |
+ |
|
| 72 |
+ podSpecNode := EnsurePodSpecNode(g, &ptSpec.Spec, ptSpecName) |
|
| 73 |
+ g.AddEdge(ptSpecNode, podSpecNode, osgraph.ContainsEdgeKind) |
|
| 74 |
+ |
|
| 75 |
+ return ptSpecNode |
|
| 38 | 76 |
} |
| 39 | 77 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,90 @@ |
| 0 |
+package nodes |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "testing" |
|
| 4 |
+ |
|
| 5 |
+ kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
| 6 |
+ |
|
| 7 |
+ osgraph "github.com/openshift/origin/pkg/api/graph" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+func TestPodSpecNode(t *testing.T) {
|
|
| 11 |
+ g := osgraph.New() |
|
| 12 |
+ |
|
| 13 |
+ pod := &kapi.Pod{}
|
|
| 14 |
+ pod.Namespace = "ns" |
|
| 15 |
+ pod.Name = "foo" |
|
| 16 |
+ pod.Spec.Host = "any-host" |
|
| 17 |
+ |
|
| 18 |
+ podNode := EnsurePodNode(g, pod) |
|
| 19 |
+ |
|
| 20 |
+ if len(g.NodeList()) != 2 {
|
|
| 21 |
+ t.Errorf("expected 2 nodes, got %v", g.NodeList())
|
|
| 22 |
+ } |
|
| 23 |
+ |
|
| 24 |
+ if len(g.EdgeList()) != 1 {
|
|
| 25 |
+ t.Errorf("expected 1 edge, got %v", g.EdgeList())
|
|
| 26 |
+ } |
|
| 27 |
+ |
|
| 28 |
+ edge := g.EdgeList()[0] |
|
| 29 |
+ if g.EdgeKind(edge) != osgraph.ContainsEdgeKind {
|
|
| 30 |
+ t.Errorf("expected %v, got %v", osgraph.ContainsEdgeKind, g.EdgeKind(edge))
|
|
| 31 |
+ } |
|
| 32 |
+ if edge.Head().ID() != podNode.ID() {
|
|
| 33 |
+ t.Errorf("expected %v, got %v", podNode.ID(), edge.Head())
|
|
| 34 |
+ } |
|
| 35 |
+} |
|
| 36 |
+ |
|
| 37 |
+func TestReplicationControllerSpecNode(t *testing.T) {
|
|
| 38 |
+ g := osgraph.New() |
|
| 39 |
+ |
|
| 40 |
+ rc := &kapi.ReplicationController{}
|
|
| 41 |
+ rc.Namespace = "ns" |
|
| 42 |
+ rc.Name = "foo" |
|
| 43 |
+ rc.Spec.Template = &kapi.PodTemplateSpec{}
|
|
| 44 |
+ |
|
| 45 |
+ rcNode := EnsureReplicationControllerNode(g, rc) |
|
| 46 |
+ |
|
| 47 |
+ if len(g.NodeList()) != 4 {
|
|
| 48 |
+ t.Errorf("expected 4 nodes, got %v", g.NodeList())
|
|
| 49 |
+ } |
|
| 50 |
+ |
|
| 51 |
+ if len(g.EdgeList()) != 3 {
|
|
| 52 |
+ t.Errorf("expected 3 edge, got %v", g.EdgeList())
|
|
| 53 |
+ } |
|
| 54 |
+ |
|
| 55 |
+ rcEdges := g.OutboundEdges(rcNode) |
|
| 56 |
+ if len(rcEdges) != 1 {
|
|
| 57 |
+ t.Fatalf("expected 1 edge, got %v", rcEdges)
|
|
| 58 |
+ } |
|
| 59 |
+ if g.EdgeKind(rcEdges[0]) != osgraph.ContainsEdgeKind {
|
|
| 60 |
+ t.Errorf("expected %v, got %v", osgraph.ContainsEdgeKind, rcEdges[0])
|
|
| 61 |
+ } |
|
| 62 |
+ |
|
| 63 |
+ uncastRCSpec := rcEdges[0].Tail() |
|
| 64 |
+ rcSpec, ok := uncastRCSpec.(*ReplicationControllerSpecNode) |
|
| 65 |
+ if !ok {
|
|
| 66 |
+ t.Fatalf("expected rcSpec, got %v", uncastRCSpec)
|
|
| 67 |
+ } |
|
| 68 |
+ rcSpecEdges := g.OutboundEdges(rcSpec) |
|
| 69 |
+ if len(rcSpecEdges) != 1 {
|
|
| 70 |
+ t.Fatalf("expected 1 edge, got %v", rcSpecEdges)
|
|
| 71 |
+ } |
|
| 72 |
+ if g.EdgeKind(rcSpecEdges[0]) != osgraph.ContainsEdgeKind {
|
|
| 73 |
+ t.Errorf("expected %v, got %v", osgraph.ContainsEdgeKind, rcSpecEdges[0])
|
|
| 74 |
+ } |
|
| 75 |
+ |
|
| 76 |
+ uncastPTSpec := rcSpecEdges[0].Tail() |
|
| 77 |
+ ptSpec, ok := uncastPTSpec.(*PodTemplateSpecNode) |
|
| 78 |
+ if !ok {
|
|
| 79 |
+ t.Fatalf("expected ptspec, got %v", uncastPTSpec)
|
|
| 80 |
+ } |
|
| 81 |
+ ptSpecEdges := g.OutboundEdges(ptSpec) |
|
| 82 |
+ if len(ptSpecEdges) != 1 {
|
|
| 83 |
+ t.Fatalf("expected 1 edge, got %v", ptSpecEdges)
|
|
| 84 |
+ } |
|
| 85 |
+ if g.EdgeKind(ptSpecEdges[0]) != osgraph.ContainsEdgeKind {
|
|
| 86 |
+ t.Errorf("expected %v, got %v", osgraph.ContainsEdgeKind, ptSpecEdges[0])
|
|
| 87 |
+ } |
|
| 88 |
+ |
|
| 89 |
+} |
| ... | ... |
@@ -10,9 +10,12 @@ import ( |
| 10 | 10 |
) |
| 11 | 11 |
|
| 12 | 12 |
var ( |
| 13 |
- ServiceNodeKind = reflect.TypeOf(kapi.Service{}).Name()
|
|
| 14 |
- PodNodeKind = reflect.TypeOf(kapi.Pod{}).Name()
|
|
| 15 |
- ReplicationControllerNodeKind = reflect.TypeOf(kapi.ReplicationController{}).Name()
|
|
| 13 |
+ ServiceNodeKind = reflect.TypeOf(kapi.Service{}).Name()
|
|
| 14 |
+ PodNodeKind = reflect.TypeOf(kapi.Pod{}).Name()
|
|
| 15 |
+ PodSpecNodeKind = reflect.TypeOf(kapi.PodSpec{}).Name()
|
|
| 16 |
+ PodTemplateSpecNodeKind = reflect.TypeOf(kapi.PodTemplateSpec{}).Name()
|
|
| 17 |
+ ReplicationControllerNodeKind = reflect.TypeOf(kapi.ReplicationController{}).Name()
|
|
| 18 |
+ ReplicationControllerSpecNodeKind = reflect.TypeOf(kapi.ReplicationControllerSpec{}).Name()
|
|
| 16 | 19 |
) |
| 17 | 20 |
|
| 18 | 21 |
func ServiceNodeName(o *kapi.Service) osgraph.UniqueName {
|
| ... | ... |
@@ -53,10 +56,41 @@ func (n PodNode) String() string {
|
| 53 | 53 |
return fmt.Sprintf("<pod %s/%s>", n.Namespace, n.Name)
|
| 54 | 54 |
} |
| 55 | 55 |
|
| 56 |
+func (n PodNode) UniqueName() osgraph.UniqueName {
|
|
| 57 |
+ return PodNodeName(n.Pod) |
|
| 58 |
+} |
|
| 59 |
+ |
|
| 56 | 60 |
func (*PodNode) Kind() string {
|
| 57 | 61 |
return PodNodeKind |
| 58 | 62 |
} |
| 59 | 63 |
|
| 64 |
+func PodSpecNodeName(o *kapi.PodSpec, ownerName osgraph.UniqueName) osgraph.UniqueName {
|
|
| 65 |
+ return osgraph.UniqueName(fmt.Sprintf("%s|%v", PodSpecNodeKind, ownerName))
|
|
| 66 |
+} |
|
| 67 |
+ |
|
| 68 |
+type PodSpecNode struct {
|
|
| 69 |
+ osgraph.Node |
|
| 70 |
+ *kapi.PodSpec |
|
| 71 |
+ |
|
| 72 |
+ OwnerName osgraph.UniqueName |
|
| 73 |
+} |
|
| 74 |
+ |
|
| 75 |
+func (n PodSpecNode) Object() interface{} {
|
|
| 76 |
+ return n.PodSpec |
|
| 77 |
+} |
|
| 78 |
+ |
|
| 79 |
+func (n PodSpecNode) String() string {
|
|
| 80 |
+ return string(n.UniqueName()) |
|
| 81 |
+} |
|
| 82 |
+ |
|
| 83 |
+func (n PodSpecNode) UniqueName() osgraph.UniqueName {
|
|
| 84 |
+ return PodSpecNodeName(n.PodSpec, n.OwnerName) |
|
| 85 |
+} |
|
| 86 |
+ |
|
| 87 |
+func (*PodSpecNode) Kind() string {
|
|
| 88 |
+ return PodSpecNodeKind |
|
| 89 |
+} |
|
| 90 |
+ |
|
| 60 | 91 |
func ReplicationControllerNodeName(o *kapi.ReplicationController) osgraph.UniqueName {
|
| 61 | 92 |
return osgraph.GetUniqueRuntimeObjectNodeName(ReplicationControllerNodeKind, o) |
| 62 | 93 |
} |
| ... | ... |
@@ -74,6 +108,64 @@ func (n ReplicationControllerNode) String() string {
|
| 74 | 74 |
return fmt.Sprintf("<replicationcontroller %s/%s>", n.Namespace, n.Name)
|
| 75 | 75 |
} |
| 76 | 76 |
|
| 77 |
+func (n ReplicationControllerNode) UniqueName() osgraph.UniqueName {
|
|
| 78 |
+ return ReplicationControllerNodeName(n.ReplicationController) |
|
| 79 |
+} |
|
| 80 |
+ |
|
| 77 | 81 |
func (*ReplicationControllerNode) Kind() string {
|
| 78 | 82 |
return ReplicationControllerNodeKind |
| 79 | 83 |
} |
| 84 |
+ |
|
| 85 |
+func ReplicationControllerSpecNodeName(o *kapi.ReplicationControllerSpec, ownerName osgraph.UniqueName) osgraph.UniqueName {
|
|
| 86 |
+ return osgraph.UniqueName(fmt.Sprintf("%s|%v", ReplicationControllerSpecNodeKind, ownerName))
|
|
| 87 |
+} |
|
| 88 |
+ |
|
| 89 |
+type ReplicationControllerSpecNode struct {
|
|
| 90 |
+ osgraph.Node |
|
| 91 |
+ *kapi.ReplicationControllerSpec |
|
| 92 |
+ |
|
| 93 |
+ OwnerName osgraph.UniqueName |
|
| 94 |
+} |
|
| 95 |
+ |
|
| 96 |
+func (n ReplicationControllerSpecNode) Object() interface{} {
|
|
| 97 |
+ return n.ReplicationControllerSpec |
|
| 98 |
+} |
|
| 99 |
+ |
|
| 100 |
+func (n ReplicationControllerSpecNode) String() string {
|
|
| 101 |
+ return string(n.UniqueName()) |
|
| 102 |
+} |
|
| 103 |
+ |
|
| 104 |
+func (n ReplicationControllerSpecNode) UniqueName() osgraph.UniqueName {
|
|
| 105 |
+ return ReplicationControllerSpecNodeName(n.ReplicationControllerSpec, n.OwnerName) |
|
| 106 |
+} |
|
| 107 |
+ |
|
| 108 |
+func (*ReplicationControllerSpecNode) Kind() string {
|
|
| 109 |
+ return ReplicationControllerSpecNodeKind |
|
| 110 |
+} |
|
| 111 |
+ |
|
| 112 |
+func PodTemplateSpecNodeName(o *kapi.PodTemplateSpec, ownerName osgraph.UniqueName) osgraph.UniqueName {
|
|
| 113 |
+ return osgraph.UniqueName(fmt.Sprintf("%s|%v", PodTemplateSpecNodeKind, ownerName))
|
|
| 114 |
+} |
|
| 115 |
+ |
|
| 116 |
+type PodTemplateSpecNode struct {
|
|
| 117 |
+ osgraph.Node |
|
| 118 |
+ *kapi.PodTemplateSpec |
|
| 119 |
+ |
|
| 120 |
+ OwnerName osgraph.UniqueName |
|
| 121 |
+} |
|
| 122 |
+ |
|
| 123 |
+func (n PodTemplateSpecNode) Object() interface{} {
|
|
| 124 |
+ return n.PodTemplateSpec |
|
| 125 |
+} |
|
| 126 |
+ |
|
| 127 |
+func (n PodTemplateSpecNode) String() string {
|
|
| 128 |
+ return string(n.UniqueName()) |
|
| 129 |
+} |
|
| 130 |
+ |
|
| 131 |
+func (n PodTemplateSpecNode) UniqueName() osgraph.UniqueName {
|
|
| 132 |
+ return PodTemplateSpecNodeName(n.PodTemplateSpec, n.OwnerName) |
|
| 133 |
+} |
|
| 134 |
+ |
|
| 135 |
+func (*PodTemplateSpecNode) Kind() string {
|
|
| 136 |
+ return PodTemplateSpecNodeKind |
|
| 137 |
+} |
| ... | ... |
@@ -14,6 +14,7 @@ import ( |
| 14 | 14 |
|
| 15 | 15 |
"github.com/openshift/origin/pkg/api/graph" |
| 16 | 16 |
graphveneers "github.com/openshift/origin/pkg/api/graph/veneers" |
| 17 |
+ kubeedges "github.com/openshift/origin/pkg/api/kubegraph" |
|
| 17 | 18 |
kubegraph "github.com/openshift/origin/pkg/api/kubegraph/nodes" |
| 18 | 19 |
buildapi "github.com/openshift/origin/pkg/build/api" |
| 19 | 20 |
buildedges "github.com/openshift/origin/pkg/build/graph" |
| ... | ... |
@@ -80,7 +81,7 @@ func (d *ProjectStatusDescriber) Describe(namespace, name string) (string, error |
| 80 | 80 |
} |
| 81 | 81 |
for i := range svcs.Items {
|
| 82 | 82 |
service := kubegraph.EnsureServiceNode(g, &svcs.Items[i]) |
| 83 |
- deployedges.AddFullfillingDeploymentConfigEdges(g, service) |
|
| 83 |
+ kubeedges.AddExposedPodTemplateSpecEdges(g, service) |
|
| 84 | 84 |
} |
| 85 | 85 |
groups := graphveneers.ServiceAndDeploymentGroups(g) |
| 86 | 86 |
|
| ... | ... |
@@ -6,10 +6,8 @@ import ( |
| 6 | 6 |
"github.com/gonum/graph" |
| 7 | 7 |
|
| 8 | 8 |
kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
| 9 |
- "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
|
| 10 | 9 |
|
| 11 | 10 |
osgraph "github.com/openshift/origin/pkg/api/graph" |
| 12 |
- kubegraph "github.com/openshift/origin/pkg/api/kubegraph/nodes" |
|
| 13 | 11 |
deploygraph "github.com/openshift/origin/pkg/deploy/graph/nodes" |
| 14 | 12 |
deployutil "github.com/openshift/origin/pkg/deploy/util" |
| 15 | 13 |
imagegraph "github.com/openshift/origin/pkg/image/graph/nodes" |
| ... | ... |
@@ -18,41 +16,8 @@ import ( |
| 18 | 18 |
const ( |
| 19 | 19 |
TriggersDeploymentEdgeKind = "TriggersDeployment" |
| 20 | 20 |
UsedInDeploymentEdgeKind = "UsedInDeployment" |
| 21 |
- |
|
| 22 |
- ExposedThroughServiceEdgeKind = "ExposedThroughService" |
|
| 23 | 21 |
) |
| 24 | 22 |
|
| 25 |
-// AddFullfillingDeploymentConfigEdges ensures that a directed edge exists between all deployment configs and the |
|
| 26 |
-// services that expose them (via label selectors). |
|
| 27 |
-func AddFullfillingDeploymentConfigEdges(g osgraph.MutableUniqueGraph, node *kubegraph.ServiceNode) *kubegraph.ServiceNode {
|
|
| 28 |
- if node.Service.Spec.Selector == nil {
|
|
| 29 |
- return node |
|
| 30 |
- } |
|
| 31 |
- query := labels.SelectorFromSet(node.Service.Spec.Selector) |
|
| 32 |
- for _, n := range g.(graph.Graph).NodeList() {
|
|
| 33 |
- switch target := n.(type) {
|
|
| 34 |
- case *deploygraph.DeploymentConfigNode: |
|
| 35 |
- template := target.DeploymentConfig.Template.ControllerTemplate.Template |
|
| 36 |
- if template == nil {
|
|
| 37 |
- continue |
|
| 38 |
- } |
|
| 39 |
- if query.Matches(labels.Set(template.Labels)) {
|
|
| 40 |
- g.AddEdge(target, node, ExposedThroughServiceEdgeKind) |
|
| 41 |
- } |
|
| 42 |
- } |
|
| 43 |
- } |
|
| 44 |
- |
|
| 45 |
- return node |
|
| 46 |
-} |
|
| 47 |
- |
|
| 48 |
-func AddAllFullfillingDeploymentConfigEdges(g osgraph.MutableUniqueGraph) {
|
|
| 49 |
- for _, node := range g.(graph.Graph).NodeList() {
|
|
| 50 |
- if serviceNode, ok := node.(*kubegraph.ServiceNode); ok {
|
|
| 51 |
- AddFullfillingDeploymentConfigEdges(g, serviceNode) |
|
| 52 |
- } |
|
| 53 |
- } |
|
| 54 |
-} |
|
| 55 |
- |
|
| 56 | 23 |
// AddTriggerEdges creates edges that point to named Docker image repositories for each image used in the deployment. |
| 57 | 24 |
func AddTriggerEdges(g osgraph.MutableUniqueGraph, node *deploygraph.DeploymentConfigNode) *deploygraph.DeploymentConfigNode {
|
| 58 | 25 |
rcTemplate := node.DeploymentConfig.Template.ControllerTemplate.Template |
| ... | ... |
@@ -4,16 +4,23 @@ import ( |
| 4 | 4 |
"github.com/gonum/graph" |
| 5 | 5 |
|
| 6 | 6 |
osgraph "github.com/openshift/origin/pkg/api/graph" |
| 7 |
+ kubegraph "github.com/openshift/origin/pkg/api/kubegraph/nodes" |
|
| 7 | 8 |
depoyapi "github.com/openshift/origin/pkg/deploy/api" |
| 8 | 9 |
) |
| 9 | 10 |
|
| 10 | 11 |
// EnsureDeploymentConfigNode adds the provided deployment config to the graph if it does not exist |
| 11 |
-func EnsureDeploymentConfigNode(g osgraph.MutableUniqueGraph, config *depoyapi.DeploymentConfig) *DeploymentConfigNode {
|
|
| 12 |
- return osgraph.EnsureUnique( |
|
| 12 |
+func EnsureDeploymentConfigNode(g osgraph.MutableUniqueGraph, dc *depoyapi.DeploymentConfig) *DeploymentConfigNode {
|
|
| 13 |
+ dcName := DeploymentConfigNodeName(dc) |
|
| 14 |
+ dcNode := osgraph.EnsureUnique( |
|
| 13 | 15 |
g, |
| 14 |
- DeploymentConfigNodeName(config), |
|
| 16 |
+ dcName, |
|
| 15 | 17 |
func(node osgraph.Node) graph.Node {
|
| 16 |
- return &DeploymentConfigNode{Node: node, DeploymentConfig: config}
|
|
| 18 |
+ return &DeploymentConfigNode{Node: node, DeploymentConfig: dc}
|
|
| 17 | 19 |
}, |
| 18 | 20 |
).(*DeploymentConfigNode) |
| 21 |
+ |
|
| 22 |
+ rcSpecNode := kubegraph.EnsureReplicationControllerSpecNode(g, &dc.Template.ControllerTemplate, dcName) |
|
| 23 |
+ g.AddEdge(dcNode, rcSpecNode, osgraph.ContainsEdgeKind) |
|
| 24 |
+ |
|
| 25 |
+ return dcNode |
|
| 19 | 26 |
} |
| 20 | 27 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,34 @@ |
| 0 |
+package nodes |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "testing" |
|
| 4 |
+ |
|
| 5 |
+ osgraph "github.com/openshift/origin/pkg/api/graph" |
|
| 6 |
+ deployapi "github.com/openshift/origin/pkg/deploy/api" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+func TestDCRCSpecNode(t *testing.T) {
|
|
| 10 |
+ g := osgraph.New() |
|
| 11 |
+ |
|
| 12 |
+ dc := &deployapi.DeploymentConfig{}
|
|
| 13 |
+ dc.Namespace = "ns" |
|
| 14 |
+ dc.Name = "foo" |
|
| 15 |
+ |
|
| 16 |
+ dcNode := EnsureDeploymentConfigNode(g, dc) |
|
| 17 |
+ |
|
| 18 |
+ if len(g.NodeList()) != 2 {
|
|
| 19 |
+ t.Errorf("expected 2 nodes, got %v", g.NodeList())
|
|
| 20 |
+ } |
|
| 21 |
+ |
|
| 22 |
+ if len(g.EdgeList()) != 1 {
|
|
| 23 |
+ t.Errorf("expected 2 edge, got %v", g.EdgeList())
|
|
| 24 |
+ } |
|
| 25 |
+ |
|
| 26 |
+ edge := g.EdgeList()[0] |
|
| 27 |
+ if g.EdgeKind(edge) != osgraph.ContainsEdgeKind {
|
|
| 28 |
+ t.Errorf("expected %v, got %v", osgraph.ContainsEdgeKind, g.EdgeKind(edge))
|
|
| 29 |
+ } |
|
| 30 |
+ if edge.Head().ID() != dcNode.ID() {
|
|
| 31 |
+ t.Errorf("expected %v, got %v", dcNode.ID(), edge.Head())
|
|
| 32 |
+ } |
|
| 33 |
+} |