| 1 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,39 @@ |
| 0 |
+package graphview |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ osgraph "github.com/openshift/origin/pkg/api/graph" |
|
| 4 |
+ kubegraph "github.com/openshift/origin/pkg/api/kubegraph/nodes" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+type Pod struct {
|
|
| 8 |
+ Pod *kubegraph.PodNode |
|
| 9 |
+} |
|
| 10 |
+ |
|
| 11 |
+// AllPods returns all Pods and the set of covered NodeIDs |
|
| 12 |
+func AllPods(g osgraph.Graph, excludeNodeIDs IntSet) ([]Pod, IntSet) {
|
|
| 13 |
+ covered := IntSet{}
|
|
| 14 |
+ pods := []Pod{}
|
|
| 15 |
+ |
|
| 16 |
+ for _, uncastNode := range g.NodesByKind(kubegraph.PodNodeKind) {
|
|
| 17 |
+ if excludeNodeIDs.Has(uncastNode.ID()) {
|
|
| 18 |
+ continue |
|
| 19 |
+ } |
|
| 20 |
+ |
|
| 21 |
+ pod, covers := NewPod(g, uncastNode.(*kubegraph.PodNode)) |
|
| 22 |
+ covered.Insert(covers.List()...) |
|
| 23 |
+ pods = append(pods, pod) |
|
| 24 |
+ } |
|
| 25 |
+ |
|
| 26 |
+ return pods, covered |
|
| 27 |
+} |
|
| 28 |
+ |
|
| 29 |
+// NewPod returns the Pod and a set of all the NodeIDs covered by the Pod |
|
| 30 |
+func NewPod(g osgraph.Graph, podNode *kubegraph.PodNode) (Pod, IntSet) {
|
|
| 31 |
+ covered := IntSet{}
|
|
| 32 |
+ covered.Insert(podNode.ID()) |
|
| 33 |
+ |
|
| 34 |
+ podView := Pod{}
|
|
| 35 |
+ podView.Pod = podNode |
|
| 36 |
+ |
|
| 37 |
+ return podView, covered |
|
| 38 |
+} |
| ... | ... |
@@ -97,6 +97,11 @@ func NewServiceGroup(g osgraph.Graph, serviceNode *kubegraph.ServiceNode) (Servi |
| 97 | 97 |
service.ReplicationControllers = append(service.ReplicationControllers, rcView) |
| 98 | 98 |
} |
| 99 | 99 |
|
| 100 |
+ for _, fulfillingPod := range service.FulfillingPods {
|
|
| 101 |
+ _, podCovers := NewPod(g, fulfillingPod) |
|
| 102 |
+ covered.Insert(podCovers.List()...) |
|
| 103 |
+ } |
|
| 104 |
+ |
|
| 100 | 105 |
return service, covered |
| 101 | 106 |
} |
| 102 | 107 |
|
| ... | ... |
@@ -152,6 +152,9 @@ func (d *ProjectStatusDescriber) Describe(namespace, name string) (string, error |
| 152 | 152 |
standaloneImages, coveredByImages := graphview.AllImagePipelinesFromBuildConfig(g, coveredNodes) |
| 153 | 153 |
coveredNodes.Insert(coveredByImages.List()...) |
| 154 | 154 |
|
| 155 |
+ standalonePods, coveredByPods := graphview.AllPods(g, coveredNodes) |
|
| 156 |
+ coveredNodes.Insert(coveredByPods.List()...) |
|
| 157 |
+ |
|
| 155 | 158 |
return tabbedString(func(out *tabwriter.Writer) error {
|
| 156 | 159 |
indent := " " |
| 157 | 160 |
if allNamespaces {
|
| ... | ... |
@@ -218,6 +221,15 @@ func (d *ProjectStatusDescriber) Describe(namespace, name string) (string, error |
| 218 | 218 |
printLines(out, indent, 0, describeRCInServiceGroup(f, standaloneRC.RC)...) |
| 219 | 219 |
} |
| 220 | 220 |
|
| 221 |
+ monopods, err := filterBoringPods(standalonePods) |
|
| 222 |
+ if err != nil {
|
|
| 223 |
+ return err |
|
| 224 |
+ } |
|
| 225 |
+ for _, monopod := range monopods {
|
|
| 226 |
+ fmt.Fprintln(out) |
|
| 227 |
+ printLines(out, indent, 0, describeMonopod(f, monopod.Pod)...) |
|
| 228 |
+ } |
|
| 229 |
+ |
|
| 221 | 230 |
allMarkers := osgraph.Markers{}
|
| 222 | 231 |
allMarkers = append(allMarkers, createForbiddenMarkers(forbiddenResources)...) |
| 223 | 232 |
for _, scanner := range getMarkerScanners(d.LogsCommandName, d.SecurityPolicyCommandFormat, d.SetProbeCommandName) {
|
| ... | ... |
@@ -502,6 +514,16 @@ func describePodInServiceGroup(f formatter, podNode *kubegraph.PodNode) []string |
| 502 | 502 |
return lines |
| 503 | 503 |
} |
| 504 | 504 |
|
| 505 |
+func describeMonopod(f formatter, podNode *kubegraph.PodNode) []string {
|
|
| 506 |
+ images := []string{}
|
|
| 507 |
+ for _, container := range podNode.Pod.Spec.Containers {
|
|
| 508 |
+ images = append(images, container.Image) |
|
| 509 |
+ } |
|
| 510 |
+ |
|
| 511 |
+ lines := []string{fmt.Sprintf("%s runs %s", f.ResourceName(podNode), strings.Join(images, ", "))}
|
|
| 512 |
+ return lines |
|
| 513 |
+} |
|
| 514 |
+ |
|
| 505 | 515 |
// exposedRoutes orders strings by their leading prefix (https:// -> http:// other prefixes), then by |
| 506 | 516 |
// the shortest distance up to the first space (indicating a break), then alphabetically: |
| 507 | 517 |
// |
| ... | ... |
@@ -1044,6 +1066,30 @@ func describeServicePorts(spec kapi.ServiceSpec) string {
|
| 1044 | 1044 |
} |
| 1045 | 1045 |
} |
| 1046 | 1046 |
|
| 1047 |
+func filterBoringPods(pods []graphview.Pod) ([]graphview.Pod, error) {
|
|
| 1048 |
+ monopods := []graphview.Pod{}
|
|
| 1049 |
+ |
|
| 1050 |
+ for _, pod := range pods {
|
|
| 1051 |
+ actualPod, ok := pod.Pod.Object().(*kapi.Pod) |
|
| 1052 |
+ if !ok {
|
|
| 1053 |
+ continue |
|
| 1054 |
+ } |
|
| 1055 |
+ meta, err := kapi.ObjectMetaFor(actualPod) |
|
| 1056 |
+ if err != nil {
|
|
| 1057 |
+ return nil, err |
|
| 1058 |
+ } |
|
| 1059 |
+ _, isDeployerPod := meta.Labels[deployapi.DeployerPodForDeploymentLabel] |
|
| 1060 |
+ _, isBuilderPod := meta.Annotations[buildapi.BuildAnnotation] |
|
| 1061 |
+ isFinished := actualPod.Status.Phase == kapi.PodSucceeded || actualPod.Status.Phase == kapi.PodFailed |
|
| 1062 |
+ if isDeployerPod || isBuilderPod || isFinished {
|
|
| 1063 |
+ continue |
|
| 1064 |
+ } |
|
| 1065 |
+ monopods = append(monopods, pod) |
|
| 1066 |
+ } |
|
| 1067 |
+ |
|
| 1068 |
+ return monopods, nil |
|
| 1069 |
+} |
|
| 1070 |
+ |
|
| 1047 | 1071 |
// GraphLoader is a stateful interface that provides methods for building the nodes of a graph |
| 1048 | 1072 |
type GraphLoader interface {
|
| 1049 | 1073 |
// Load is responsible for gathering and saving the objects this GraphLoader should AddToGraph |
| ... | ... |
@@ -293,6 +293,20 @@ func TestProjectStatus(t *testing.T) {
|
| 293 | 293 |
`View details with 'oc describe <resource>/<name>' or list everything with 'oc get all'.`, |
| 294 | 294 |
}, |
| 295 | 295 |
}, |
| 296 |
+ "monopod": {
|
|
| 297 |
+ Path: "../../../../test/fixtures/app-scenarios/k8s-lonely-pod.json", |
|
| 298 |
+ Extra: []runtime.Object{
|
|
| 299 |
+ &projectapi.Project{
|
|
| 300 |
+ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""},
|
|
| 301 |
+ }, |
|
| 302 |
+ }, |
|
| 303 |
+ ErrFn: func(err error) bool { return err == nil },
|
|
| 304 |
+ Contains: []string{
|
|
| 305 |
+ "In project example on server https://example.com:8443\n", |
|
| 306 |
+ "pod/lonely-pod runs openshift/hello-openshift", |
|
| 307 |
+ "You have no services, deployment configs, or build configs.", |
|
| 308 |
+ }, |
|
| 309 |
+ }, |
|
| 296 | 310 |
} |
| 297 | 311 |
oldTimeFn := timeNowFn |
| 298 | 312 |
defer func() { timeNowFn = oldTimeFn }()
|