| ... | ... |
@@ -41,6 +41,7 @@ func NewReplicationController(g osgraph.Graph, rcNode *kubegraph.ReplicationCont |
| 41 | 41 |
|
| 42 | 42 |
rcView := ReplicationController{}
|
| 43 | 43 |
rcView.RC = rcNode |
| 44 |
+ rcView.ConflictingRCIDToPods = map[int][]*kubegraph.PodNode{}
|
|
| 44 | 45 |
|
| 45 | 46 |
for _, uncastPodNode := range g.PredecessorNodesByEdgeKind(rcNode, kubeedges.ManagedByRCEdgeKind) {
|
| 46 | 47 |
podNode := uncastPodNode.(*kubegraph.PodNode) |
| 47 | 48 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,96 @@ |
| 0 |
+package graph |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/gonum/graph" |
|
| 4 |
+) |
|
| 5 |
+ |
|
| 6 |
+// Marker is a struct that describes something interesting on a Node |
|
| 7 |
+type Marker struct {
|
|
| 8 |
+ // Node is the optional node that this message is attached to |
|
| 9 |
+ Node graph.Node |
|
| 10 |
+ // RelatedNodes is an optional list of other nodes that are involved in this marker. |
|
| 11 |
+ RelatedNodes []graph.Node |
|
| 12 |
+ |
|
| 13 |
+ // Severity indicates how important this problem is. |
|
| 14 |
+ Severity Severity |
|
| 15 |
+ // Key is a short string to identify this message |
|
| 16 |
+ Key string |
|
| 17 |
+ // Message is a human-readable string that describes what is interesting |
|
| 18 |
+ Message string |
|
| 19 |
+} |
|
| 20 |
+ |
|
| 21 |
+// Severity indicates how important this problem is. |
|
| 22 |
+type Severity string |
|
| 23 |
+ |
|
| 24 |
+const ( |
|
| 25 |
+ // InfoSeverity is interesting |
|
| 26 |
+ InfoSeverity Severity = "info" |
|
| 27 |
+ // WarningSeverity is probably wrong, but we aren't certain |
|
| 28 |
+ WarningSeverity Severity = "warning" |
|
| 29 |
+ // ErrorSeverity is definitely wrong, this won't work |
|
| 30 |
+ ErrorSeverity Severity = "error" |
|
| 31 |
+) |
|
| 32 |
+ |
|
| 33 |
+type Markers []Marker |
|
| 34 |
+ |
|
| 35 |
+// MarkerScanner is a function for analyzing a graph and finding interesting things in it |
|
| 36 |
+type MarkerScanner func(g Graph) []Marker |
|
| 37 |
+ |
|
| 38 |
+func (m Markers) BySeverity(severity Severity) []Marker {
|
|
| 39 |
+ ret := []Marker{}
|
|
| 40 |
+ for i := range m {
|
|
| 41 |
+ if m[i].Severity == severity {
|
|
| 42 |
+ ret = append(ret, m[i]) |
|
| 43 |
+ } |
|
| 44 |
+ } |
|
| 45 |
+ |
|
| 46 |
+ return ret |
|
| 47 |
+} |
|
| 48 |
+ |
|
| 49 |
+type BySeverity []Marker |
|
| 50 |
+ |
|
| 51 |
+func (m BySeverity) Len() int { return len(m) }
|
|
| 52 |
+func (m BySeverity) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
|
|
| 53 |
+func (m BySeverity) Less(i, j int) bool {
|
|
| 54 |
+ lhs := m[i] |
|
| 55 |
+ rhs := m[j] |
|
| 56 |
+ |
|
| 57 |
+ switch lhs.Severity {
|
|
| 58 |
+ case ErrorSeverity: |
|
| 59 |
+ switch rhs.Severity {
|
|
| 60 |
+ case ErrorSeverity: |
|
| 61 |
+ return false |
|
| 62 |
+ } |
|
| 63 |
+ case WarningSeverity: |
|
| 64 |
+ switch rhs.Severity {
|
|
| 65 |
+ case ErrorSeverity, WarningSeverity: |
|
| 66 |
+ return false |
|
| 67 |
+ } |
|
| 68 |
+ case InfoSeverity: |
|
| 69 |
+ switch rhs.Severity {
|
|
| 70 |
+ case ErrorSeverity, WarningSeverity, InfoSeverity: |
|
| 71 |
+ return false |
|
| 72 |
+ } |
|
| 73 |
+ } |
|
| 74 |
+ |
|
| 75 |
+ return true |
|
| 76 |
+} |
|
| 77 |
+ |
|
| 78 |
+type ByNodeID []Marker |
|
| 79 |
+ |
|
| 80 |
+func (m ByNodeID) Len() int { return len(m) }
|
|
| 81 |
+func (m ByNodeID) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
|
|
| 82 |
+func (m ByNodeID) Less(i, j int) bool {
|
|
| 83 |
+ if m[i].Node == nil {
|
|
| 84 |
+ return true |
|
| 85 |
+ } |
|
| 86 |
+ return m[i].Node.ID() < m[j].Node.ID() |
|
| 87 |
+} |
|
| 88 |
+ |
|
| 89 |
+type ByKey []Marker |
|
| 90 |
+ |
|
| 91 |
+func (m ByKey) Len() int { return len(m) }
|
|
| 92 |
+func (m ByKey) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
|
|
| 93 |
+func (m ByKey) Less(i, j int) bool {
|
|
| 94 |
+ return m[i].Key < m[j].Key |
|
| 95 |
+} |
| ... | ... |
@@ -1,13 +1,88 @@ |
| 1 | 1 |
package analysis |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "fmt" |
|
| 5 |
+ |
|
| 6 |
+ "github.com/gonum/graph" |
|
| 7 |
+ |
|
| 4 | 8 |
osgraph "github.com/openshift/origin/pkg/api/graph" |
| 5 | 9 |
kubeedges "github.com/openshift/origin/pkg/api/kubegraph" |
| 6 | 10 |
kubegraph "github.com/openshift/origin/pkg/api/kubegraph/nodes" |
| 7 | 11 |
) |
| 8 | 12 |
|
| 9 |
-// CheckMountedSecrets checks to be sure that all the referenced secrets are mountable (by service account) and present (not synthetic) |
|
| 10 |
-func CheckMountedSecrets(g osgraph.Graph, podSpecNode *kubegraph.PodSpecNode) ( /*unmountable secrets*/ []*kubegraph.SecretNode /*unresolved secrets*/, []*kubegraph.SecretNode) {
|
|
| 13 |
+const ( |
|
| 14 |
+ UnmountableSecretWarning = "UnmountableSecret" |
|
| 15 |
+ MissingSecretWarning = "MissingSecret" |
|
| 16 |
+) |
|
| 17 |
+ |
|
| 18 |
+// FindUnmountableSecrets inspects all PodSpecs for any Secret reference that isn't listed as mountable by the referenced ServiceAccount |
|
| 19 |
+func FindUnmountableSecrets(g osgraph.Graph) []osgraph.Marker {
|
|
| 20 |
+ markers := []osgraph.Marker{}
|
|
| 21 |
+ |
|
| 22 |
+ for _, uncastPodSpecNode := range g.NodesByKind(kubegraph.PodSpecNodeKind) {
|
|
| 23 |
+ podSpecNode := uncastPodSpecNode.(*kubegraph.PodSpecNode) |
|
| 24 |
+ unmountableSecrets := CheckForUnmountableSecrets(g, podSpecNode) |
|
| 25 |
+ |
|
| 26 |
+ topLevelNode := osgraph.GetTopLevelContainerNode(g, podSpecNode) |
|
| 27 |
+ topLevelString := g.Name(topLevelNode) |
|
| 28 |
+ if resourceStringer, ok := topLevelNode.(osgraph.ResourceNode); ok {
|
|
| 29 |
+ topLevelString = resourceStringer.ResourceString() |
|
| 30 |
+ } |
|
| 31 |
+ |
|
| 32 |
+ saString := "MISSING_SA" |
|
| 33 |
+ saNodes := g.SuccessorNodesByEdgeKind(podSpecNode, kubeedges.ReferencedServiceAccountEdgeKind) |
|
| 34 |
+ if len(saNodes) > 0 {
|
|
| 35 |
+ saString = saNodes[0].(*kubegraph.ServiceAccountNode).ResourceString() |
|
| 36 |
+ } |
|
| 37 |
+ |
|
| 38 |
+ for _, unmountableSecret := range unmountableSecrets {
|
|
| 39 |
+ markers = append(markers, osgraph.Marker{
|
|
| 40 |
+ Node: podSpecNode, |
|
| 41 |
+ RelatedNodes: []graph.Node{unmountableSecret},
|
|
| 42 |
+ |
|
| 43 |
+ Severity: osgraph.WarningSeverity, |
|
| 44 |
+ Key: UnmountableSecretWarning, |
|
| 45 |
+ Message: fmt.Sprintf("%s is attempting to mount a secret %s disallowed by %s",
|
|
| 46 |
+ topLevelString, unmountableSecret.ResourceString(), saString), |
|
| 47 |
+ }) |
|
| 48 |
+ } |
|
| 49 |
+ } |
|
| 50 |
+ |
|
| 51 |
+ return markers |
|
| 52 |
+} |
|
| 53 |
+ |
|
| 54 |
+// FindMissingSecrets inspects all PodSpecs for any Secret reference that is a synthetic node (not a pre-existing node in the graph) |
|
| 55 |
+func FindMissingSecrets(g osgraph.Graph) []osgraph.Marker {
|
|
| 56 |
+ markers := []osgraph.Marker{}
|
|
| 57 |
+ |
|
| 58 |
+ for _, uncastPodSpecNode := range g.NodesByKind(kubegraph.PodSpecNodeKind) {
|
|
| 59 |
+ podSpecNode := uncastPodSpecNode.(*kubegraph.PodSpecNode) |
|
| 60 |
+ missingSecrets := CheckMissingMountedSecrets(g, podSpecNode) |
|
| 61 |
+ |
|
| 62 |
+ topLevelNode := osgraph.GetTopLevelContainerNode(g, podSpecNode) |
|
| 63 |
+ topLevelString := g.Name(topLevelNode) |
|
| 64 |
+ if resourceStringer, ok := topLevelNode.(osgraph.ResourceNode); ok {
|
|
| 65 |
+ topLevelString = resourceStringer.ResourceString() |
|
| 66 |
+ } |
|
| 67 |
+ |
|
| 68 |
+ for _, missingSecret := range missingSecrets {
|
|
| 69 |
+ markers = append(markers, osgraph.Marker{
|
|
| 70 |
+ Node: podSpecNode, |
|
| 71 |
+ RelatedNodes: []graph.Node{missingSecret},
|
|
| 72 |
+ |
|
| 73 |
+ Severity: osgraph.WarningSeverity, |
|
| 74 |
+ Key: UnmountableSecretWarning, |
|
| 75 |
+ Message: fmt.Sprintf("%s is attempting to mount a missing secret %s",
|
|
| 76 |
+ topLevelString, missingSecret.ResourceString()), |
|
| 77 |
+ }) |
|
| 78 |
+ } |
|
| 79 |
+ } |
|
| 80 |
+ |
|
| 81 |
+ return markers |
|
| 82 |
+} |
|
| 83 |
+ |
|
| 84 |
+// CheckForUnmountableSecrets checks to be sure that all the referenced secrets are mountable (by service account) |
|
| 85 |
+func CheckForUnmountableSecrets(g osgraph.Graph, podSpecNode *kubegraph.PodSpecNode) []*kubegraph.SecretNode {
|
|
| 11 | 86 |
saNodes := g.SuccessorNodesByNodeAndEdgeKind(podSpecNode, kubegraph.ServiceAccountNodeKind, kubeedges.ReferencedServiceAccountEdgeKind) |
| 12 | 87 |
saMountableSecrets := []*kubegraph.SecretNode{}
|
| 13 | 88 |
|
| ... | ... |
@@ -19,13 +94,9 @@ func CheckMountedSecrets(g osgraph.Graph, podSpecNode *kubegraph.PodSpecNode) ( |
| 19 | 19 |
} |
| 20 | 20 |
|
| 21 | 21 |
unmountableSecrets := []*kubegraph.SecretNode{}
|
| 22 |
- missingSecrets := []*kubegraph.SecretNode{}
|
|
| 23 | 22 |
|
| 24 | 23 |
for _, uncastMountedSecretNode := range g.SuccessorNodesByNodeAndEdgeKind(podSpecNode, kubegraph.SecretNodeKind, kubeedges.MountedSecretEdgeKind) {
|
| 25 | 24 |
mountedSecretNode := uncastMountedSecretNode.(*kubegraph.SecretNode) |
| 26 |
- if !mountedSecretNode.Found() {
|
|
| 27 |
- missingSecrets = append(missingSecrets, mountedSecretNode) |
|
| 28 |
- } |
|
| 29 | 25 |
|
| 30 | 26 |
mountable := false |
| 31 | 27 |
for _, mountableSecretNode := range saMountableSecrets {
|
| ... | ... |
@@ -41,5 +112,19 @@ func CheckMountedSecrets(g osgraph.Graph, podSpecNode *kubegraph.PodSpecNode) ( |
| 41 | 41 |
} |
| 42 | 42 |
} |
| 43 | 43 |
|
| 44 |
- return unmountableSecrets, missingSecrets |
|
| 44 |
+ return unmountableSecrets |
|
| 45 |
+} |
|
| 46 |
+ |
|
| 47 |
+// CheckMissingMountedSecrets checks to be sure that all the referenced secrets are present (not synthetic) |
|
| 48 |
+func CheckMissingMountedSecrets(g osgraph.Graph, podSpecNode *kubegraph.PodSpecNode) []*kubegraph.SecretNode {
|
|
| 49 |
+ missingSecrets := []*kubegraph.SecretNode{}
|
|
| 50 |
+ |
|
| 51 |
+ for _, uncastMountedSecretNode := range g.SuccessorNodesByNodeAndEdgeKind(podSpecNode, kubegraph.SecretNodeKind, kubeedges.MountedSecretEdgeKind) {
|
|
| 52 |
+ mountedSecretNode := uncastMountedSecretNode.(*kubegraph.SecretNode) |
|
| 53 |
+ if !mountedSecretNode.Found() {
|
|
| 54 |
+ missingSecrets = append(missingSecrets, mountedSecretNode) |
|
| 55 |
+ } |
|
| 56 |
+ } |
|
| 57 |
+ |
|
| 58 |
+ return missingSecrets |
|
| 45 | 59 |
} |
| 46 | 60 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,83 @@ |
| 0 |
+package analysis |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "testing" |
|
| 4 |
+ |
|
| 5 |
+ osgraph "github.com/openshift/origin/pkg/api/graph" |
|
| 6 |
+ osgraphtest "github.com/openshift/origin/pkg/api/graph/test" |
|
| 7 |
+ kubeedges "github.com/openshift/origin/pkg/api/kubegraph" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+func TestMissingSecrets(t *testing.T) {
|
|
| 11 |
+ g, _, err := osgraphtest.BuildGraph("../../../api/graph/test/bad_secret_refs.yaml")
|
|
| 12 |
+ if err != nil {
|
|
| 13 |
+ t.Fatalf("unexpected error: %v", err)
|
|
| 14 |
+ } |
|
| 15 |
+ |
|
| 16 |
+ kubeedges.AddAllRequestedServiceAccountEdges(g) |
|
| 17 |
+ kubeedges.AddAllMountableSecretEdges(g) |
|
| 18 |
+ kubeedges.AddAllMountedSecretEdges(g) |
|
| 19 |
+ |
|
| 20 |
+ markers := FindMissingSecrets(g) |
|
| 21 |
+ if e, a := 1, len(markers); e != a {
|
|
| 22 |
+ t.Fatalf("expected %v, got %v", e, a)
|
|
| 23 |
+ } |
|
| 24 |
+ |
|
| 25 |
+ actualDC := osgraph.GetTopLevelContainerNode(g, markers[0].Node) |
|
| 26 |
+ expectedDC := g.Find(osgraph.UniqueName("DeploymentConfig|/docker-nfs-server"))
|
|
| 27 |
+ if e, a := expectedDC.ID(), actualDC.ID(); e != a {
|
|
| 28 |
+ t.Errorf("expected %v, got %v", e, a)
|
|
| 29 |
+ } |
|
| 30 |
+ |
|
| 31 |
+ actualSecret := markers[0].RelatedNodes[0] |
|
| 32 |
+ expectedSecret := g.Find(osgraph.UniqueName("Secret|/missing-secret"))
|
|
| 33 |
+ if e, a := expectedSecret.ID(), actualSecret.ID(); e != a {
|
|
| 34 |
+ t.Errorf("expected %v, got %v", e, a)
|
|
| 35 |
+ |
|
| 36 |
+ } |
|
| 37 |
+} |
|
| 38 |
+ |
|
| 39 |
+func TestUnmountableSecrets(t *testing.T) {
|
|
| 40 |
+ g, _, err := osgraphtest.BuildGraph("../../../api/graph/test/bad_secret_refs.yaml")
|
|
| 41 |
+ if err != nil {
|
|
| 42 |
+ t.Fatalf("unexpected error: %v", err)
|
|
| 43 |
+ } |
|
| 44 |
+ |
|
| 45 |
+ kubeedges.AddAllRequestedServiceAccountEdges(g) |
|
| 46 |
+ kubeedges.AddAllMountableSecretEdges(g) |
|
| 47 |
+ kubeedges.AddAllMountedSecretEdges(g) |
|
| 48 |
+ |
|
| 49 |
+ markers := FindUnmountableSecrets(g) |
|
| 50 |
+ if e, a := 2, len(markers); e != a {
|
|
| 51 |
+ t.Errorf("expected %v, got %v", e, a)
|
|
| 52 |
+ } |
|
| 53 |
+ |
|
| 54 |
+ expectedSecret1 := g.Find(osgraph.UniqueName("Secret|/missing-secret"))
|
|
| 55 |
+ expectedSecret2 := g.Find(osgraph.UniqueName("Secret|/unmountable-secret"))
|
|
| 56 |
+ found1 := false |
|
| 57 |
+ found2 := false |
|
| 58 |
+ |
|
| 59 |
+ for i := 0; i < 2; i++ {
|
|
| 60 |
+ actualDC := osgraph.GetTopLevelContainerNode(g, markers[i].Node) |
|
| 61 |
+ expectedDC := g.Find(osgraph.UniqueName("DeploymentConfig|/docker-nfs-server"))
|
|
| 62 |
+ if e, a := expectedDC.ID(), actualDC.ID(); e != a {
|
|
| 63 |
+ t.Errorf("expected %v, got %v", e, a)
|
|
| 64 |
+ } |
|
| 65 |
+ |
|
| 66 |
+ actualSecret := markers[i].RelatedNodes[0] |
|
| 67 |
+ if e, a := expectedSecret1.ID(), actualSecret.ID(); e == a {
|
|
| 68 |
+ found1 = true |
|
| 69 |
+ } |
|
| 70 |
+ if e, a := expectedSecret2.ID(), actualSecret.ID(); e == a {
|
|
| 71 |
+ found2 = true |
|
| 72 |
+ } |
|
| 73 |
+ } |
|
| 74 |
+ |
|
| 75 |
+ if !found1 {
|
|
| 76 |
+ t.Errorf("expected %v, got %v", expectedSecret1, markers)
|
|
| 77 |
+ } |
|
| 78 |
+ |
|
| 79 |
+ if !found2 {
|
|
| 80 |
+ t.Errorf("expected %v, got %v", expectedSecret2, markers)
|
|
| 81 |
+ } |
|
| 82 |
+} |
| 0 | 83 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,85 @@ |
| 0 |
+package analysis |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "strings" |
|
| 5 |
+ |
|
| 6 |
+ "github.com/gonum/graph" |
|
| 7 |
+ "github.com/gonum/graph/topo" |
|
| 8 |
+ |
|
| 9 |
+ osgraph "github.com/openshift/origin/pkg/api/graph" |
|
| 10 |
+ buildedges "github.com/openshift/origin/pkg/build/graph" |
|
| 11 |
+ buildgraph "github.com/openshift/origin/pkg/build/graph/nodes" |
|
| 12 |
+ imageedges "github.com/openshift/origin/pkg/image/graph" |
|
| 13 |
+ imagegraph "github.com/openshift/origin/pkg/image/graph/nodes" |
|
| 14 |
+) |
|
| 15 |
+ |
|
| 16 |
+const ( |
|
| 17 |
+ MissingRequiredRegistryWarning = "MissingRequiredRegistry" |
|
| 18 |
+ CyclicBuildConfigWarning = "CyclicBuildConfig" |
|
| 19 |
+) |
|
| 20 |
+ |
|
| 21 |
+// FindUnpushableBuildConfigs checks all build configs that will output to an IST backed by an ImageStream and checks to make sure their builds can push. |
|
| 22 |
+func FindUnpushableBuildConfigs(g osgraph.Graph) []osgraph.Marker {
|
|
| 23 |
+ markers := []osgraph.Marker{}
|
|
| 24 |
+ |
|
| 25 |
+bc: |
|
| 26 |
+ for _, bcNode := range g.NodesByKind(buildgraph.BuildConfigNodeKind) {
|
|
| 27 |
+ for _, istNode := range g.SuccessorNodesByEdgeKind(bcNode, buildedges.BuildOutputEdgeKind) {
|
|
| 28 |
+ for _, uncastImageStreamNode := range g.SuccessorNodesByEdgeKind(istNode, imageedges.ReferencedImageStreamGraphEdgeKind) {
|
|
| 29 |
+ imageStreamNode := uncastImageStreamNode.(*imagegraph.ImageStreamNode) |
|
| 30 |
+ |
|
| 31 |
+ if len(imageStreamNode.Status.DockerImageRepository) == 0 {
|
|
| 32 |
+ markers = append(markers, osgraph.Marker{
|
|
| 33 |
+ Node: bcNode, |
|
| 34 |
+ RelatedNodes: []graph.Node{istNode},
|
|
| 35 |
+ |
|
| 36 |
+ Severity: osgraph.WarningSeverity, |
|
| 37 |
+ Key: MissingRequiredRegistryWarning, |
|
| 38 |
+ Message: fmt.Sprintf("%s is pushing to %s that is using %s, but the administrator has not configured the integrated Docker registry. (oadm registry)",
|
|
| 39 |
+ bcNode.(*buildgraph.BuildConfigNode).ResourceString(), istNode.(*imagegraph.ImageStreamTagNode).ResourceString(), imageStreamNode.ResourceString()), |
|
| 40 |
+ }) |
|
| 41 |
+ |
|
| 42 |
+ continue bc |
|
| 43 |
+ } |
|
| 44 |
+ } |
|
| 45 |
+ } |
|
| 46 |
+ } |
|
| 47 |
+ |
|
| 48 |
+ return markers |
|
| 49 |
+} |
|
| 50 |
+ |
|
| 51 |
+// FindCircularBuilds checks all build configs for cycles |
|
| 52 |
+func FindCircularBuilds(g osgraph.Graph) []osgraph.Marker {
|
|
| 53 |
+ // Filter out all but ImageStreamTag and BuildConfig nodes |
|
| 54 |
+ nodeFn := osgraph.NodesOfKind(imagegraph.ImageStreamTagNodeKind, buildgraph.BuildConfigNodeKind) |
|
| 55 |
+ // Filter out all but BuildInputImage and BuildOutput edges |
|
| 56 |
+ edgeFn := osgraph.EdgesOfKind(buildedges.BuildInputImageEdgeKind, buildedges.BuildOutputEdgeKind) |
|
| 57 |
+ |
|
| 58 |
+ // Create desired subgraph |
|
| 59 |
+ sub := g.Subgraph(nodeFn, edgeFn) |
|
| 60 |
+ |
|
| 61 |
+ markers := []osgraph.Marker{}
|
|
| 62 |
+ |
|
| 63 |
+ // Check for cycles |
|
| 64 |
+ for _, cycle := range topo.CyclesIn(sub) {
|
|
| 65 |
+ nodeNames := []string{}
|
|
| 66 |
+ for _, node := range cycle {
|
|
| 67 |
+ if resourceStringer, ok := node.(osgraph.ResourceNode); ok {
|
|
| 68 |
+ nodeNames = append(nodeNames, resourceStringer.ResourceString()) |
|
| 69 |
+ } |
|
| 70 |
+ } |
|
| 71 |
+ |
|
| 72 |
+ markers = append(markers, osgraph.Marker{
|
|
| 73 |
+ Node: cycle[0], |
|
| 74 |
+ RelatedNodes: cycle, |
|
| 75 |
+ |
|
| 76 |
+ Severity: osgraph.WarningSeverity, |
|
| 77 |
+ Key: CyclicBuildConfigWarning, |
|
| 78 |
+ Message: fmt.Sprintf("Cycle detected in build configurations: %s", strings.Join(nodeNames, " -> ")),
|
|
| 79 |
+ }) |
|
| 80 |
+ |
|
| 81 |
+ } |
|
| 82 |
+ |
|
| 83 |
+ return markers |
|
| 84 |
+} |
| 0 | 85 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,75 @@ |
| 0 |
+package analysis |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "testing" |
|
| 4 |
+ |
|
| 5 |
+ osgraph "github.com/openshift/origin/pkg/api/graph" |
|
| 6 |
+ osgraphtest "github.com/openshift/origin/pkg/api/graph/test" |
|
| 7 |
+ buildedges "github.com/openshift/origin/pkg/build/graph" |
|
| 8 |
+ imageedges "github.com/openshift/origin/pkg/image/graph" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+func TestUnpushableBuild(t *testing.T) {
|
|
| 12 |
+ g, _, err := osgraphtest.BuildGraph("../../../api/graph/test/unpushable-build.yaml")
|
|
| 13 |
+ if err != nil {
|
|
| 14 |
+ t.Fatalf("unexpected error: %v", err)
|
|
| 15 |
+ } |
|
| 16 |
+ |
|
| 17 |
+ buildedges.AddAllInputOutputEdges(g) |
|
| 18 |
+ imageedges.AddAllImageStreamRefEdges(g) |
|
| 19 |
+ |
|
| 20 |
+ markers := FindUnpushableBuildConfigs(g) |
|
| 21 |
+ if e, a := 1, len(markers); e != a {
|
|
| 22 |
+ t.Fatalf("expected %v, got %v", e, a)
|
|
| 23 |
+ } |
|
| 24 |
+ |
|
| 25 |
+ actualBC := osgraph.GetTopLevelContainerNode(g, markers[0].Node) |
|
| 26 |
+ expectedBC := g.Find(osgraph.UniqueName("BuildConfig|/ruby-hello-world"))
|
|
| 27 |
+ if e, a := expectedBC.ID(), actualBC.ID(); e != a {
|
|
| 28 |
+ t.Errorf("expected %v, got %v", e, a)
|
|
| 29 |
+ } |
|
| 30 |
+ |
|
| 31 |
+ actualIST := markers[0].RelatedNodes[0] |
|
| 32 |
+ expectedIST := g.Find(osgraph.UniqueName("ImageStreamTag|/ruby-hello-world:latest"))
|
|
| 33 |
+ if e, a := expectedIST.ID(), actualIST.ID(); e != a {
|
|
| 34 |
+ t.Errorf("expected %v, got %v: \n%v", e, a, g)
|
|
| 35 |
+ } |
|
| 36 |
+ |
|
| 37 |
+} |
|
| 38 |
+ |
|
| 39 |
+func TestPushableBuild(t *testing.T) {
|
|
| 40 |
+ g, _, err := osgraphtest.BuildGraph("../../../api/graph/test/pushable-build.yaml")
|
|
| 41 |
+ if err != nil {
|
|
| 42 |
+ t.Fatalf("unexpected error: %v", err)
|
|
| 43 |
+ } |
|
| 44 |
+ |
|
| 45 |
+ buildedges.AddAllInputOutputEdges(g) |
|
| 46 |
+ imageedges.AddAllImageStreamRefEdges(g) |
|
| 47 |
+ |
|
| 48 |
+ if e, a := 0, len(FindUnpushableBuildConfigs(g)); e != a {
|
|
| 49 |
+ t.Errorf("expected %v, got %v", e, a)
|
|
| 50 |
+ } |
|
| 51 |
+} |
|
| 52 |
+ |
|
| 53 |
+func TestCircularDeps(t *testing.T) {
|
|
| 54 |
+ g, _, err := osgraphtest.BuildGraph("../../../api/graph/test/circular.yaml")
|
|
| 55 |
+ if err != nil {
|
|
| 56 |
+ t.Fatalf("unexpected error: %v", err)
|
|
| 57 |
+ } |
|
| 58 |
+ buildedges.AddAllInputOutputEdges(g) |
|
| 59 |
+ |
|
| 60 |
+ if len(FindCircularBuilds(g)) != 1 {
|
|
| 61 |
+ t.Fatalf("expected having circular dependencies")
|
|
| 62 |
+ } |
|
| 63 |
+ |
|
| 64 |
+ not, _, err := osgraphtest.BuildGraph("../../../api/graph/test/circular-not.yaml")
|
|
| 65 |
+ if err != nil {
|
|
| 66 |
+ t.Fatalf("unexpected error: %v", err)
|
|
| 67 |
+ } |
|
| 68 |
+ buildedges.AddAllInputOutputEdges(not) |
|
| 69 |
+ |
|
| 70 |
+ if len(FindCircularBuilds(not)) != 0 {
|
|
| 71 |
+ t.Fatalf("expected not having circular dependencies")
|
|
| 72 |
+ } |
|
| 73 |
+ |
|
| 74 |
+} |
| ... | ... |
@@ -3,6 +3,7 @@ package describe |
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 | 5 |
"io" |
| 6 |
+ "sort" |
|
| 6 | 7 |
"strings" |
| 7 | 8 |
"text/tabwriter" |
| 8 | 9 |
|
| ... | ... |
@@ -12,7 +13,6 @@ import ( |
| 12 | 12 |
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
| 13 | 13 |
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" |
| 14 | 14 |
utilerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors" |
| 15 |
- "github.com/gonum/graph/topo" |
|
| 16 | 15 |
|
| 17 | 16 |
osgraph "github.com/openshift/origin/pkg/api/graph" |
| 18 | 17 |
"github.com/openshift/origin/pkg/api/graph/graphview" |
| ... | ... |
@@ -21,6 +21,7 @@ import ( |
| 21 | 21 |
kubegraph "github.com/openshift/origin/pkg/api/kubegraph/nodes" |
| 22 | 22 |
buildapi "github.com/openshift/origin/pkg/build/api" |
| 23 | 23 |
buildedges "github.com/openshift/origin/pkg/build/graph" |
| 24 |
+ buildanalysis "github.com/openshift/origin/pkg/build/graph/analysis" |
|
| 24 | 25 |
buildgraph "github.com/openshift/origin/pkg/build/graph/nodes" |
| 25 | 26 |
"github.com/openshift/origin/pkg/client" |
| 26 | 27 |
deployapi "github.com/openshift/origin/pkg/deploy/api" |
| ... | ... |
@@ -161,14 +162,23 @@ func (d *ProjectStatusDescriber) Describe(namespace, name string) (string, error |
| 161 | 161 |
// always output warnings |
| 162 | 162 |
fmt.Fprintln(out) |
| 163 | 163 |
|
| 164 |
- if hasUnresolvedImageStreamTag(g) {
|
|
| 165 |
- fmt.Fprintln(out, "Warning: Some of your builds are pointing to image streams, but the administrator has not configured the integrated Docker registry (oadm registry).") |
|
| 164 |
+ allMarkers := osgraph.Markers{}
|
|
| 165 |
+ for _, scanner := range getMarkerScanners() {
|
|
| 166 |
+ allMarkers = append(allMarkers, scanner(g)...) |
|
| 166 | 167 |
} |
| 167 |
- if hasCircularDependencies(g) {
|
|
| 168 |
- fmt.Fprintln(out, "Warning: Some of your build configurations have circular dependencies.") |
|
| 168 |
+ sort.Stable(osgraph.ByKey(allMarkers)) |
|
| 169 |
+ sort.Stable(osgraph.ByNodeID(allMarkers)) |
|
| 170 |
+ if errorMarkers := allMarkers.BySeverity(osgraph.ErrorSeverity); len(errorMarkers) > 0 {
|
|
| 171 |
+ fmt.Fprintln(out, "Errors:") |
|
| 172 |
+ for _, marker := range errorMarkers {
|
|
| 173 |
+ fmt.Fprintln(out, indent+marker.Message) |
|
| 174 |
+ } |
|
| 169 | 175 |
} |
| 170 |
- if lines, _ := describeBadPodSpecs(out, g); len(lines) > 0 {
|
|
| 171 |
- fmt.Fprintln(out, strings.Join(lines, "\n")) |
|
| 176 |
+ if warningMarkers := allMarkers.BySeverity(osgraph.WarningSeverity); len(warningMarkers) > 0 {
|
|
| 177 |
+ fmt.Fprintln(out, "Warnings:") |
|
| 178 |
+ for _, marker := range warningMarkers {
|
|
| 179 |
+ fmt.Fprintln(out, indent+marker.Message) |
|
| 180 |
+ } |
|
| 172 | 181 |
} |
| 173 | 182 |
|
| 174 | 183 |
if (len(services) == 0) && (len(standaloneDCs) == 0) && (len(standaloneImages) == 0) {
|
| ... | ... |
@@ -184,77 +194,13 @@ func (d *ProjectStatusDescriber) Describe(namespace, name string) (string, error |
| 184 | 184 |
}) |
| 185 | 185 |
} |
| 186 | 186 |
|
| 187 |
-// hasUnresolvedImageStreamTag checks all build configs that will output to an IST backed by an ImageStream and checks to make sure their builds can push. |
|
| 188 |
-func hasUnresolvedImageStreamTag(g osgraph.Graph) bool {
|
|
| 189 |
- for _, bcNode := range g.NodesByKind(buildgraph.BuildConfigNodeKind) {
|
|
| 190 |
- for _, istNode := range g.SuccessorNodesByEdgeKind(bcNode, buildedges.BuildOutputEdgeKind) {
|
|
| 191 |
- for _, uncastImageStreamNode := range g.SuccessorNodesByEdgeKind(istNode, imageedges.ReferencedImageStreamGraphEdgeKind) {
|
|
| 192 |
- imageStreamNode := uncastImageStreamNode.(*imagegraph.ImageStreamNode) |
|
| 193 |
- if len(imageStreamNode.Status.DockerImageRepository) == 0 {
|
|
| 194 |
- return true |
|
| 195 |
- } |
|
| 196 |
- } |
|
| 197 |
- } |
|
| 187 |
+func getMarkerScanners() []osgraph.MarkerScanner {
|
|
| 188 |
+ return []osgraph.MarkerScanner{
|
|
| 189 |
+ kubeanalysis.FindUnmountableSecrets, |
|
| 190 |
+ kubeanalysis.FindMissingSecrets, |
|
| 191 |
+ buildanalysis.FindUnpushableBuildConfigs, |
|
| 192 |
+ buildanalysis.FindCircularBuilds, |
|
| 198 | 193 |
} |
| 199 |
- |
|
| 200 |
- return false |
|
| 201 |
-} |
|
| 202 |
- |
|
| 203 |
-func hasCircularDependencies(g osgraph.Graph) bool {
|
|
| 204 |
- // Filter out all but ImageStreamTag and BuildConfig nodes |
|
| 205 |
- nodeFn := osgraph.NodesOfKind(imagegraph.ImageStreamTagNodeKind, buildgraph.BuildConfigNodeKind) |
|
| 206 |
- // Filter out all but BuildInputImage and BuildOutput edges |
|
| 207 |
- edgeFn := osgraph.EdgesOfKind(buildedges.BuildInputImageEdgeKind, buildedges.BuildOutputEdgeKind) |
|
| 208 |
- |
|
| 209 |
- // Create desired subgraph |
|
| 210 |
- sub := g.Subgraph(nodeFn, edgeFn) |
|
| 211 |
- |
|
| 212 |
- // Check for cycles |
|
| 213 |
- return len(topo.CyclesIn(sub)) != 0 |
|
| 214 |
-} |
|
| 215 |
- |
|
| 216 |
-func describeBadPodSpecs(out io.Writer, g osgraph.Graph) ([]string, []*kubegraph.SecretNode) {
|
|
| 217 |
- allMissingSecrets := []*kubegraph.SecretNode{}
|
|
| 218 |
- lines := []string{}
|
|
| 219 |
- |
|
| 220 |
- for _, uncastPodSpec := range g.NodesByKind(kubegraph.PodSpecNodeKind) {
|
|
| 221 |
- podSpecNode := uncastPodSpec.(*kubegraph.PodSpecNode) |
|
| 222 |
- unmountableSecrets, missingSecrets := kubeanalysis.CheckMountedSecrets(g, podSpecNode) |
|
| 223 |
- containingNode := osgraph.GetTopLevelContainerNode(g, podSpecNode) |
|
| 224 |
- |
|
| 225 |
- allMissingSecrets = append(allMissingSecrets, missingSecrets...) |
|
| 226 |
- |
|
| 227 |
- unmountableNames := []string{}
|
|
| 228 |
- for _, secret := range unmountableSecrets {
|
|
| 229 |
- unmountableNames = append(unmountableNames, secret.ResourceString()) |
|
| 230 |
- } |
|
| 231 |
- |
|
| 232 |
- missingNames := []string{}
|
|
| 233 |
- for _, secret := range missingSecrets {
|
|
| 234 |
- missingNames = append(missingNames, secret.ResourceString()) |
|
| 235 |
- } |
|
| 236 |
- |
|
| 237 |
- containingNodeName := g.GraphDescriber.Name(containingNode) |
|
| 238 |
- if resourceNode, ok := containingNode.(osgraph.ResourceNode); ok {
|
|
| 239 |
- containingNodeName = resourceNode.ResourceString() |
|
| 240 |
- } |
|
| 241 |
- |
|
| 242 |
- switch {
|
|
| 243 |
- case len(unmountableSecrets) > 0 && len(missingSecrets) > 0: |
|
| 244 |
- lines = append(lines, fmt.Sprintf("\t%s is not allowed to mount %s and wants to mount these missing secrets %s", containingNodeName, strings.Join(unmountableNames, ","), strings.Join(missingNames, ",")))
|
|
| 245 |
- case len(unmountableSecrets) > 0: |
|
| 246 |
- lines = append(lines, fmt.Sprintf("\t%s is not allowed to mount %s", containingNodeName, strings.Join(unmountableNames, ",")))
|
|
| 247 |
- case len(unmountableSecrets) > 0 && len(missingSecrets) > 0: |
|
| 248 |
- lines = append(lines, fmt.Sprintf("\t%s wants to mount these missing secrets %s", containingNodeName, strings.Join(missingNames, ",")))
|
|
| 249 |
- } |
|
| 250 |
- } |
|
| 251 |
- |
|
| 252 |
- // if we had any failures, prepend the warning line |
|
| 253 |
- if len(lines) > 0 {
|
|
| 254 |
- return append([]string{"Warning: some requested secrets are not allowed:"}, lines...), allMissingSecrets
|
|
| 255 |
- } |
|
| 256 |
- |
|
| 257 |
- return []string{}, allMissingSecrets
|
|
| 258 | 194 |
} |
| 259 | 195 |
|
| 260 | 196 |
func printLines(out io.Writer, indent string, depth int, lines ...string) {
|
| ... | ... |
@@ -10,10 +10,7 @@ import ( |
| 10 | 10 |
ktestclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client/testclient" |
| 11 | 11 |
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" |
| 12 | 12 |
|
| 13 |
- osgraphtest "github.com/openshift/origin/pkg/api/graph/test" |
|
| 14 |
- buildedges "github.com/openshift/origin/pkg/build/graph" |
|
| 15 | 13 |
"github.com/openshift/origin/pkg/client/testclient" |
| 16 |
- imageedges "github.com/openshift/origin/pkg/image/graph" |
|
| 17 | 14 |
projectapi "github.com/openshift/origin/pkg/project/api" |
| 18 | 15 |
) |
| 19 | 16 |
|
| ... | ... |
@@ -25,57 +22,6 @@ func mustParseTime(t string) time.Time {
|
| 25 | 25 |
return out |
| 26 | 26 |
} |
| 27 | 27 |
|
| 28 |
-func TestUnpushableBuild(t *testing.T) {
|
|
| 29 |
- g, _, err := osgraphtest.BuildGraph("../../../api/graph/test/unpushable-build.yaml")
|
|
| 30 |
- if err != nil {
|
|
| 31 |
- t.Fatalf("unexpected error: %v", err)
|
|
| 32 |
- } |
|
| 33 |
- |
|
| 34 |
- buildedges.AddAllInputOutputEdges(g) |
|
| 35 |
- imageedges.AddAllImageStreamRefEdges(g) |
|
| 36 |
- |
|
| 37 |
- if e, a := true, hasUnresolvedImageStreamTag(g); e != a {
|
|
| 38 |
- t.Errorf("expected %v, got %v", e, a)
|
|
| 39 |
- } |
|
| 40 |
-} |
|
| 41 |
- |
|
| 42 |
-func TestPushableBuild(t *testing.T) {
|
|
| 43 |
- g, _, err := osgraphtest.BuildGraph("../../../api/graph/test/pushable-build.yaml")
|
|
| 44 |
- if err != nil {
|
|
| 45 |
- t.Fatalf("unexpected error: %v", err)
|
|
| 46 |
- } |
|
| 47 |
- |
|
| 48 |
- buildedges.AddAllInputOutputEdges(g) |
|
| 49 |
- imageedges.AddAllImageStreamRefEdges(g) |
|
| 50 |
- |
|
| 51 |
- if e, a := false, hasUnresolvedImageStreamTag(g); e != a {
|
|
| 52 |
- t.Errorf("expected %v, got %v", e, a)
|
|
| 53 |
- } |
|
| 54 |
-} |
|
| 55 |
- |
|
| 56 |
-func TestCircularDeps(t *testing.T) {
|
|
| 57 |
- g, _, err := osgraphtest.BuildGraph("../../../api/graph/test/circular.yaml")
|
|
| 58 |
- if err != nil {
|
|
| 59 |
- t.Fatalf("unexpected error: %v", err)
|
|
| 60 |
- } |
|
| 61 |
- buildedges.AddAllInputOutputEdges(g) |
|
| 62 |
- |
|
| 63 |
- if !hasCircularDependencies(g) {
|
|
| 64 |
- t.Fatalf("expected having circular dependencies")
|
|
| 65 |
- } |
|
| 66 |
- |
|
| 67 |
- not, _, err := osgraphtest.BuildGraph("../../../api/graph/test/circular-not.yaml")
|
|
| 68 |
- if err != nil {
|
|
| 69 |
- t.Fatalf("unexpected error: %v", err)
|
|
| 70 |
- } |
|
| 71 |
- buildedges.AddAllInputOutputEdges(not) |
|
| 72 |
- |
|
| 73 |
- if hasCircularDependencies(not) {
|
|
| 74 |
- t.Fatalf("expected not having circular dependencies")
|
|
| 75 |
- } |
|
| 76 |
- |
|
| 77 |
-} |
|
| 78 |
- |
|
| 79 | 28 |
func TestProjectStatus(t *testing.T) {
|
| 80 | 29 |
testCases := map[string]struct {
|
| 81 | 30 |
Path string |
| ... | ... |
@@ -148,8 +94,9 @@ func TestProjectStatus(t *testing.T) {
|
| 148 | 148 |
"In project example\n", |
| 149 | 149 |
"rc/my-rc runs openshift/mysql-55-centos7", |
| 150 | 150 |
"0/1 pods growing to 1", |
| 151 |
- "is not allowed to mount secret/existing-secret", |
|
| 152 |
- "these missing secrets secret/dne", |
|
| 151 |
+ "rc/my-rc is attempting to mount a secret secret/existing-secret disallowed by sa/default", |
|
| 152 |
+ "rc/my-rc is attempting to mount a secret secret/dne disallowed by sa/default", |
|
| 153 |
+ "rc/my-rc is attempting to mount a missing secret secret/dne", |
|
| 153 | 154 |
}, |
| 154 | 155 |
}, |
| 155 | 156 |
"service with pod": {
|
| ... | ... |
@@ -199,6 +146,30 @@ func TestProjectStatus(t *testing.T) {
|
| 199 | 199 |
"To see more, use", |
| 200 | 200 |
}, |
| 201 | 201 |
}, |
| 202 |
+ "unpushable build": {
|
|
| 203 |
+ Path: "../../../../pkg/api/graph/test/unpushable-build.yaml", |
|
| 204 |
+ Extra: []runtime.Object{
|
|
| 205 |
+ &projectapi.Project{
|
|
| 206 |
+ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""},
|
|
| 207 |
+ }, |
|
| 208 |
+ }, |
|
| 209 |
+ ErrFn: func(err error) bool { return err == nil },
|
|
| 210 |
+ Contains: []string{
|
|
| 211 |
+ "bc/ruby-hello-world is pushing to imagestreamtag/ruby-hello-world:latest that is using is/ruby-hello-world, but the administrator has not configured the integrated Docker registry.", |
|
| 212 |
+ }, |
|
| 213 |
+ }, |
|
| 214 |
+ "cyclical build": {
|
|
| 215 |
+ Path: "../../../../pkg/api/graph/test/circular.yaml", |
|
| 216 |
+ Extra: []runtime.Object{
|
|
| 217 |
+ &projectapi.Project{
|
|
| 218 |
+ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""},
|
|
| 219 |
+ }, |
|
| 220 |
+ }, |
|
| 221 |
+ ErrFn: func(err error) bool { return err == nil },
|
|
| 222 |
+ Contains: []string{
|
|
| 223 |
+ "Cycle detected in build configurations:", |
|
| 224 |
+ }, |
|
| 225 |
+ }, |
|
| 202 | 226 |
"running build": {
|
| 203 | 227 |
Path: "../../../../test/fixtures/app-scenarios/new-project-one-build.yaml", |
| 204 | 228 |
Extra: []runtime.Object{
|
| ... | ... |
@@ -294,6 +265,5 @@ func TestProjectStatus(t *testing.T) {
|
| 294 | 294 |
t.Errorf("%s: did not have %q:\n%s\n---", k, s, out)
|
| 295 | 295 |
} |
| 296 | 296 |
} |
| 297 |
- t.Logf("\n%s", out)
|
|
| 298 | 297 |
} |
| 299 | 298 |
} |
| 300 | 299 |
deleted file mode 100644 |
| ... | ... |
@@ -1,54 +0,0 @@ |
| 1 |
-package analysis |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "github.com/gonum/graph" |
|
| 5 |
- |
|
| 6 |
- "github.com/GoogleCloudPlatform/kubernetes/pkg/util" |
|
| 7 |
- |
|
| 8 |
- osgraph "github.com/openshift/origin/pkg/api/graph" |
|
| 9 |
- "github.com/openshift/origin/pkg/api/graph/graphview" |
|
| 10 |
- kubeanalysis "github.com/openshift/origin/pkg/api/kubegraph/analysis" |
|
| 11 |
- kubegraph "github.com/openshift/origin/pkg/api/kubegraph/nodes" |
|
| 12 |
- deploygraph "github.com/openshift/origin/pkg/deploy/graph/nodes" |
|
| 13 |
-) |
|
| 14 |
- |
|
| 15 |
-// DescendentNodesByNodeKind starts at the root navigates down the root. Every edge is checked against the edgeChecker |
|
| 16 |
-// to determine whether or not to follow it. The nodes at the tail end of every chased edge are then checked against the |
|
| 17 |
-// the targetNodeKind. Matches are added to the return and every checked node then has its edges checked: lather, rinse, repeat |
|
| 18 |
-func DescendentNodesByNodeKind(g osgraph.Graph, visitedNodes graphview.IntSet, node graph.Node, targetNodeKind string, edgeChecker osgraph.EdgeFunc) []graph.Node {
|
|
| 19 |
- if visitedNodes.Has(node.ID()) {
|
|
| 20 |
- return []graph.Node{}
|
|
| 21 |
- } |
|
| 22 |
- visitedNodes.Insert(node.ID()) |
|
| 23 |
- |
|
| 24 |
- ret := []graph.Node{}
|
|
| 25 |
- for _, successor := range g.From(node) {
|
|
| 26 |
- edge := g.Edge(node, successor) |
|
| 27 |
- |
|
| 28 |
- if edgeChecker(osgraph.New(), node, successor, g.EdgeKinds(edge)) {
|
|
| 29 |
- if g.Kind(successor) == targetNodeKind {
|
|
| 30 |
- ret = append(ret, successor) |
|
| 31 |
- } |
|
| 32 |
- |
|
| 33 |
- ret = append(ret, DescendentNodesByNodeKind(g, visitedNodes, successor, targetNodeKind, edgeChecker)...) |
|
| 34 |
- } |
|
| 35 |
- } |
|
| 36 |
- |
|
| 37 |
- return ret |
|
| 38 |
-} |
|
| 39 |
- |
|
| 40 |
-// CheckMountedSecrets checks to be sure that all the referenced secrets are mountable (by service account) and present (not synthetic) |
|
| 41 |
-func CheckMountedSecrets(g osgraph.Graph, dcNode *deploygraph.DeploymentConfigNode) ( /*unmountable secrets*/ []*kubegraph.SecretNode /*unresolved secrets*/, []*kubegraph.SecretNode) {
|
|
| 42 |
- podSpecs := DescendentNodesByNodeKind(g, graphview.IntSet{}, dcNode, kubegraph.PodSpecNodeKind, func(g osgraph.Interface, head, tail graph.Node, edgeKinds util.StringSet) bool {
|
|
| 43 |
- if edgeKinds.Has(osgraph.ContainsEdgeKind) {
|
|
| 44 |
- return true |
|
| 45 |
- } |
|
| 46 |
- return false |
|
| 47 |
- }) |
|
| 48 |
- |
|
| 49 |
- if len(podSpecs) > 0 {
|
|
| 50 |
- return kubeanalysis.CheckMountedSecrets(g, podSpecs[0].(*kubegraph.PodSpecNode)) |
|
| 51 |
- } |
|
| 52 |
- |
|
| 53 |
- return []*kubegraph.SecretNode{}, []*kubegraph.SecretNode{}
|
|
| 54 |
-} |
| 55 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,45 +0,0 @@ |
| 1 |
-package analysis |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "testing" |
|
| 5 |
- |
|
| 6 |
- osgraphtest "github.com/openshift/origin/pkg/api/graph/test" |
|
| 7 |
- kubeedges "github.com/openshift/origin/pkg/api/kubegraph" |
|
| 8 |
- deployapi "github.com/openshift/origin/pkg/deploy/api" |
|
| 9 |
- deploygraph "github.com/openshift/origin/pkg/deploy/graph/nodes" |
|
| 10 |
-) |
|
| 11 |
- |
|
| 12 |
-func TestCheckMountedSecrets(t *testing.T) {
|
|
| 13 |
- g, objs, err := osgraphtest.BuildGraph("../../../api/graph/test/bad_secret_refs.yaml")
|
|
| 14 |
- if err != nil {
|
|
| 15 |
- t.Fatalf("unexpected error: %v", err)
|
|
| 16 |
- } |
|
| 17 |
- |
|
| 18 |
- var dc *deployapi.DeploymentConfig |
|
| 19 |
- for _, obj := range objs {
|
|
| 20 |
- if currDC, ok := obj.(*deployapi.DeploymentConfig); ok {
|
|
| 21 |
- if dc != nil {
|
|
| 22 |
- t.Errorf("got more than one dc: %v", currDC)
|
|
| 23 |
- } |
|
| 24 |
- dc = currDC |
|
| 25 |
- } |
|
| 26 |
- } |
|
| 27 |
- |
|
| 28 |
- kubeedges.AddAllRequestedServiceAccountEdges(g) |
|
| 29 |
- kubeedges.AddAllMountableSecretEdges(g) |
|
| 30 |
- kubeedges.AddAllMountedSecretEdges(g) |
|
| 31 |
- |
|
| 32 |
- dcNode := g.Find(deploygraph.DeploymentConfigNodeName(dc)) |
|
| 33 |
- unmountable, missing := CheckMountedSecrets(g, dcNode.(*deploygraph.DeploymentConfigNode)) |
|
| 34 |
- |
|
| 35 |
- if e, a := 2, len(unmountable); e != a {
|
|
| 36 |
- t.Fatalf("expected %v, got %v", e, a)
|
|
| 37 |
- } |
|
| 38 |
- |
|
| 39 |
- if e, a := 1, len(missing); e != a {
|
|
| 40 |
- t.Fatalf("expected %v, got %v", e, a)
|
|
| 41 |
- } |
|
| 42 |
- if e, a := "missing-secret", missing[0].Name; e != a {
|
|
| 43 |
- t.Fatalf("expected %v, got %v", e, a)
|
|
| 44 |
- } |
|
| 45 |
-} |