Browse code

oc: show ready pods next to deployments

Michail Kargakis authored on 2016/10/10 22:48:26
Showing 4 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,130 @@
0
+apiVersion: v1
1
+items:
2
+- apiVersion: v1
3
+  kind: DeploymentConfig
4
+  metadata:
5
+    name: example
6
+  spec:
7
+    replicas: 3
8
+    selector:
9
+      name: example
10
+    strategy:
11
+      type: Rolling
12
+    template:
13
+      metadata:
14
+        labels:
15
+          name: example
16
+      spec:
17
+        containers:
18
+        - command:
19
+          - /bin/sleep
20
+          - "100"
21
+          image: docker.io/centos:centos7
22
+          name: myapp
23
+    test: false
24
+    triggers:
25
+    - type: ConfigChange
26
+  status:
27
+    availableReplicas: 1
28
+    details:
29
+      causes:
30
+      - type: ConfigChange
31
+      message: config change
32
+    latestVersion: 2
33
+    replicas: 3
34
+    updatedReplicas: 2
35
+- apiVersion: v1
36
+  kind: ReplicationController
37
+  metadata:
38
+    annotations:
39
+      openshift.io/deployer-pod.name: example-1-deploy
40
+      openshift.io/deployment-config.latest-version: "1"
41
+      openshift.io/deployment-config.name: example
42
+      openshift.io/deployment.phase: Complete
43
+      openshift.io/deployment.replicas: "1"
44
+      openshift.io/deployment.status-reason: config change
45
+      openshift.io/encoded-deployment-config: |
46
+        {"kind":"DeploymentConfig","apiVersion":"v1","metadata":{"name":"example","namespace":"myproject","selfLink":"/oapi/v1/namespaces/myproject/deploymentconfigs/example","uid":"6d298d51-9486-11e6-b581-080027242396","resourceVersion":"1173","generation":2,"creationTimestamp":"2016-10-17T16:26:15Z"},"spec":{"strategy":{"type":"Rolling","rollingParams":{"updatePeriodSeconds":1,"intervalSeconds":1,"timeoutSeconds":600,"maxUnavailable":"25%","maxSurge":"25%","pre":{"failurePolicy":"Abort","execNewPod":{"command":["/bin/echo","test pre hook executed"],"containerName":"myapp"}}},"resources":{}},"triggers":[{"type":"ConfigChange"}],"replicas":1,"test":false,"selector":{"name":"example"},"template":{"metadata":{"creationTimestamp":null,"labels":{"name":"example"}},"spec":{"containers":[{"name":"myapp","image":"docker.io/centos:centos7","command":["/bin/sleep","100"],"resources":{},"terminationMessagePath":"/dev/termination-log","imagePullPolicy":"IfNotPresent"}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","securityContext":{}}}},"status":{"latestVersion":1,"observedGeneration":1,"details":{"message":"config change","causes":[{"type":"ConfigChange"}]},"conditions":[{"type":"Available","status":"False","lastTransitionTime":"2016-10-17T16:26:15Z","message":"Deployment config does not have minimum availability."}]}}
47
+    creationTimestamp: 2016-04-07T04:11:25Z
48
+    generation: 2
49
+    labels:
50
+      openshift.io/deployment-config.name: example
51
+    name: example-1
52
+  spec:
53
+    replicas: 2
54
+    selector:
55
+      deployment: example-1
56
+      deploymentconfig: example
57
+      name: example
58
+    template:
59
+      metadata:
60
+        annotations:
61
+          openshift.io/deployment-config.latest-version: "1"
62
+          openshift.io/deployment-config.name: example
63
+          openshift.io/deployment.name: example-1
64
+        creationTimestamp: null
65
+        labels:
66
+          deployment: example-1
67
+          deploymentconfig: example
68
+          name: example
69
+      spec:
70
+        containers:
71
+        - command:
72
+          - /bin/sleep
73
+          - "100"
74
+          image: docker.io/centos:centos7
75
+          imagePullPolicy: IfNotPresent
76
+          name: myapp
77
+  status:
78
+    fullyLabeledReplicas: 2
79
+    observedGeneration: 2
80
+    readyReplicas: 1
81
+    replicas: 2
82
+- apiVersion: v1
83
+  kind: ReplicationController
84
+  metadata:
85
+    annotations:
86
+      openshift.io/deployer-pod.name: example-2-deploy
87
+      openshift.io/deployment-config.latest-version: "2"
88
+      openshift.io/deployment-config.name: example
89
+      openshift.io/deployment.phase: Running
90
+      openshift.io/deployment.replicas: "3"
91
+      openshift.io/deployment.status-reason: manual change
92
+      openshift.io/encoded-deployment-config: |
93
+        {"kind":"DeploymentConfig","apiVersion":"v1","metadata":{"name":"example","namespace":"myproject","selfLink":"/oapi/v1/namespaces/myproject/deploymentconfigs/example","uid":"6d298d51-9486-11e6-b581-080027242396","resourceVersion":"1314","generation":5,"creationTimestamp":"2016-10-17T16:26:15Z"},"spec":{"strategy":{"type":"Rolling","rollingParams":{"updatePeriodSeconds":1,"intervalSeconds":1,"timeoutSeconds":600,"maxUnavailable":"25%","maxSurge":"25%"},"resources":{}},"triggers":[{"type":"ConfigChange"}],"replicas":3,"test":false,"selector":{"name":"example"},"template":{"metadata":{"creationTimestamp":null,"labels":{"name":"example"}},"spec":{"containers":[{"name":"myapp","image":"docker.io/centos:centos7","command":["/bin/sleep","100"],"resources":{},"terminationMessagePath":"/dev/termination-log","imagePullPolicy":"IfNotPresent"}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","securityContext":{}}}},"status":{"latestVersion":2,"observedGeneration":4,"replicas":3,"updatedReplicas":3,"availableReplicas":2,"unavailableReplicas":1,"details":{"message":"manual change","causes":[{"type":"Manual"}]},"conditions":[{"type":"Available","status":"False","lastTransitionTime":"2016-10-17T16:29:55Z","message":"Deployment config does not have minimum availability."},{"type":"Progressing","status":"True","lastTransitionTime":"2016-10-17T16:29:55Z","reason":"NewReplicationControllerAvailable","message":"Replication controller \"example-1\" has completed progressing"}]}}
94
+    creationTimestamp: 2016-04-07T04:11:55Z
95
+    generation: 4
96
+    labels:
97
+      openshift.io/deployment-config.name: example
98
+    name: example-2
99
+  spec:
100
+    replicas: 1
101
+    selector:
102
+      deployment: example-2
103
+      deploymentconfig: example
104
+      name: example
105
+    template:
106
+      metadata:
107
+        annotations:
108
+          openshift.io/deployment-config.latest-version: "2"
109
+          openshift.io/deployment-config.name: example
110
+          openshift.io/deployment.name: example-2
111
+        creationTimestamp: null
112
+        labels:
113
+          deployment: example-2
114
+          deploymentconfig: example
115
+          name: example
116
+      spec:
117
+        containers:
118
+        - command:
119
+          - /bin/sleep
120
+          - "100"
121
+          image: docker.io/centos:centos7
122
+          imagePullPolicy: IfNotPresent
123
+          name: myapp
124
+  status:
125
+    fullyLabeledReplicas: 1
126
+    readyReplicas: 0
127
+    replicas: 1
128
+kind: List
129
+metadata: {}
... ...
@@ -130,6 +130,7 @@ items:
130 130
         restartPolicy: Always
131 131
   status:
132 132
     replicas: 2
133
+    readyReplicas: 2
133 134
 - apiVersion: v1
134 135
   kind: ReplicationController
135 136
   metadata:
... ...
@@ -187,6 +188,7 @@ items:
187 187
         restartPolicy: Always
188 188
   status:
189 189
     replicas: 1
190
+    readyReplicas: 1
190 191
 - apiVersion: v1
191 192
   kind: DeploymentConfig
192 193
   metadata:
... ...
@@ -362,6 +364,7 @@ items:
362 362
         restartPolicy: Always
363 363
   status:
364 364
     replicas: 1
365
+    readyReplicas: 1
365 366
 - apiVersion: v1
366 367
   kind: DeploymentConfig
367 368
   metadata:
... ...
@@ -1013,52 +1013,53 @@ func describeDeployments(f formatter, dcNode *deploygraph.DeploymentConfigNode,
1013 1013
 	return out
1014 1014
 }
1015 1015
 
1016
-func describeDeploymentStatus(deploy *kapi.ReplicationController, first, test bool, restartCount int32) string {
1017
-	timeAt := strings.ToLower(formatRelativeTime(deploy.CreationTimestamp.Time))
1018
-	status := deployutil.DeploymentStatusFor(deploy)
1019
-	version := deployutil.DeploymentVersionFor(deploy)
1016
+func describeDeploymentStatus(rc *kapi.ReplicationController, first, test bool, restartCount int32) string {
1017
+	timeAt := strings.ToLower(formatRelativeTime(rc.CreationTimestamp.Time))
1018
+	status := deployutil.DeploymentStatusFor(rc)
1019
+	version := deployutil.DeploymentVersionFor(rc)
1020 1020
 	maybeCancelling := ""
1021
-	if deployutil.IsDeploymentCancelled(deploy) && !deployutil.IsTerminatedDeployment(deploy) {
1021
+	if deployutil.IsDeploymentCancelled(rc) && !deployutil.IsTerminatedDeployment(rc) {
1022 1022
 		maybeCancelling = " (cancelling)"
1023 1023
 	}
1024 1024
 
1025 1025
 	switch status {
1026 1026
 	case deployapi.DeploymentStatusFailed:
1027
-		reason := deployutil.DeploymentStatusReasonFor(deploy)
1027
+		reason := deployutil.DeploymentStatusReasonFor(rc)
1028 1028
 		if len(reason) > 0 {
1029 1029
 			reason = fmt.Sprintf(": %s", reason)
1030 1030
 		}
1031 1031
 		// TODO: encode fail time in the rc
1032
-		return fmt.Sprintf("deployment #%d failed %s ago%s%s", version, timeAt, reason, describePodSummaryInline(deploy.Status.Replicas, deploy.Spec.Replicas, false, restartCount))
1032
+		return fmt.Sprintf("deployment #%d failed %s ago%s%s", version, timeAt, reason, describePodSummaryInline(rc.Status.ReadyReplicas, rc.Status.Replicas, rc.Spec.Replicas, false, restartCount))
1033 1033
 	case deployapi.DeploymentStatusComplete:
1034 1034
 		// TODO: pod status output
1035 1035
 		if test {
1036 1036
 			return fmt.Sprintf("test deployment #%d deployed %s ago", version, timeAt)
1037 1037
 		}
1038
-		return fmt.Sprintf("deployment #%d deployed %s ago%s", version, timeAt, describePodSummaryInline(deploy.Status.Replicas, deploy.Spec.Replicas, first, restartCount))
1038
+		return fmt.Sprintf("deployment #%d deployed %s ago%s", version, timeAt, describePodSummaryInline(rc.Status.ReadyReplicas, rc.Status.Replicas, rc.Spec.Replicas, first, restartCount))
1039 1039
 	case deployapi.DeploymentStatusRunning:
1040 1040
 		format := "deployment #%d running%s for %s%s"
1041 1041
 		if test {
1042 1042
 			format = "test deployment #%d running%s for %s%s"
1043 1043
 		}
1044
-		return fmt.Sprintf(format, version, maybeCancelling, timeAt, describePodSummaryInline(deploy.Status.Replicas, deploy.Spec.Replicas, false, restartCount))
1044
+		return fmt.Sprintf(format, version, maybeCancelling, timeAt, describePodSummaryInline(rc.Status.ReadyReplicas, rc.Status.Replicas, rc.Spec.Replicas, false, restartCount))
1045 1045
 	default:
1046
-		return fmt.Sprintf("deployment #%d %s%s %s ago%s", version, strings.ToLower(string(status)), maybeCancelling, timeAt, describePodSummaryInline(deploy.Status.Replicas, deploy.Spec.Replicas, false, restartCount))
1046
+		return fmt.Sprintf("deployment #%d %s%s %s ago%s", version, strings.ToLower(string(status)), maybeCancelling, timeAt, describePodSummaryInline(rc.Status.ReadyReplicas, rc.Status.Replicas, rc.Spec.Replicas, false, restartCount))
1047 1047
 	}
1048 1048
 }
1049 1049
 
1050 1050
 func describePetSetStatus(p *kapps.PetSet) string {
1051 1051
 	timeAt := strings.ToLower(formatRelativeTime(p.CreationTimestamp.Time))
1052
-	return fmt.Sprintf("created %s ago%s", timeAt, describePodSummaryInline(int32(p.Status.Replicas), int32(p.Spec.Replicas), false, 0))
1052
+	// TODO: Replace first argument in describePodSummaryInline with ReadyReplicas once that's a thing for pet sets.
1053
+	return fmt.Sprintf("created %s ago%s", timeAt, describePodSummaryInline(int32(p.Status.Replicas), int32(p.Status.Replicas), int32(p.Spec.Replicas), false, 0))
1053 1054
 }
1054 1055
 
1055 1056
 func describeRCStatus(rc *kapi.ReplicationController) string {
1056 1057
 	timeAt := strings.ToLower(formatRelativeTime(rc.CreationTimestamp.Time))
1057
-	return fmt.Sprintf("rc/%s created %s ago%s", rc.Name, timeAt, describePodSummaryInline(rc.Status.Replicas, rc.Spec.Replicas, false, 0))
1058
+	return fmt.Sprintf("rc/%s created %s ago%s", rc.Name, timeAt, describePodSummaryInline(rc.Status.ReadyReplicas, rc.Status.Replicas, rc.Spec.Replicas, false, 0))
1058 1059
 }
1059 1060
 
1060
-func describePodSummaryInline(actual, requested int32, includeEmpty bool, restartCount int32) string {
1061
-	s := describePodSummary(actual, requested, includeEmpty, restartCount)
1061
+func describePodSummaryInline(ready, actual, requested int32, includeEmpty bool, restartCount int32) string {
1062
+	s := describePodSummary(ready, requested, includeEmpty, restartCount)
1062 1063
 	if len(s) == 0 {
1063 1064
 		return s
1064 1065
 	}
... ...
@@ -1072,25 +1073,25 @@ func describePodSummaryInline(actual, requested int32, includeEmpty bool, restar
1072 1072
 	return fmt.Sprintf(" - %s%s", s, change)
1073 1073
 }
1074 1074
 
1075
-func describePodSummary(actual, requested int32, includeEmpty bool, restartCount int32) string {
1075
+func describePodSummary(ready, requested int32, includeEmpty bool, restartCount int32) string {
1076 1076
 	var restartWarn string
1077 1077
 	if restartCount > 0 {
1078 1078
 		restartWarn = fmt.Sprintf(" (warning: %d restarts)", restartCount)
1079 1079
 	}
1080
-	if actual == requested {
1080
+	if ready == requested {
1081 1081
 		switch {
1082
-		case actual == 0:
1082
+		case ready == 0:
1083 1083
 			if !includeEmpty {
1084 1084
 				return ""
1085 1085
 			}
1086 1086
 			return "0 pods"
1087
-		case actual > 1:
1088
-			return fmt.Sprintf("%d pods", actual) + restartWarn
1087
+		case ready > 1:
1088
+			return fmt.Sprintf("%d pods", ready) + restartWarn
1089 1089
 		default:
1090 1090
 			return "1 pod" + restartWarn
1091 1091
 		}
1092 1092
 	}
1093
-	return fmt.Sprintf("%d/%d pods", actual, requested) + restartWarn
1093
+	return fmt.Sprintf("%d/%d pods", ready, requested) + restartWarn
1094 1094
 }
1095 1095
 
1096 1096
 func describeDeploymentConfigTriggers(config *deployapi.DeploymentConfig) (string, bool) {
... ...
@@ -367,6 +367,20 @@ func TestProjectStatus(t *testing.T) {
367 367
 				`View details with 'oc describe <resource>/<name>' or list everything with 'oc get all'.`,
368 368
 			},
369 369
 		},
370
+		"deployment with unavailable pods": {
371
+			File: "available-deployment.yaml",
372
+			Extra: []runtime.Object{
373
+				&projectapi.Project{
374
+					ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""},
375
+				},
376
+			},
377
+			ErrFn: func(err error) bool { return err == nil },
378
+			Contains: []string{
379
+				"deployment #2 running for 30 seconds - 0/1 pods\n",
380
+				"deployment #1 deployed about a minute ago - 1/2 pods",
381
+			},
382
+			Time: mustParseTime("2016-04-07T04:12:25Z"),
383
+		},
370 384
 	}
371 385
 	oldTimeFn := timeNowFn
372 386
 	defer func() { timeNowFn = oldTimeFn }()