Skip to content

oc: show ready pods next to deployments #11291

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 9, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 130 additions & 0 deletions pkg/api/graph/test/available-deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
apiVersion: v1
items:
- apiVersion: v1
kind: DeploymentConfig
metadata:
name: example
spec:
replicas: 3
selector:
name: example
strategy:
type: Rolling
template:
metadata:
labels:
name: example
spec:
containers:
- command:
- /bin/sleep
- "100"
image: docker.io/centos:centos7
name: myapp
test: false
triggers:
- type: ConfigChange
status:
availableReplicas: 1
details:
causes:
- type: ConfigChange
message: config change
latestVersion: 2
replicas: 3
updatedReplicas: 2
- apiVersion: v1
kind: ReplicationController
metadata:
annotations:
openshift.io/deployer-pod.name: example-1-deploy
openshift.io/deployment-config.latest-version: "1"
openshift.io/deployment-config.name: example
openshift.io/deployment.phase: Complete
openshift.io/deployment.replicas: "1"
openshift.io/deployment.status-reason: config change
openshift.io/encoded-deployment-config: |
{"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."}]}}
creationTimestamp: 2016-04-07T04:11:25Z
generation: 2
labels:
openshift.io/deployment-config.name: example
name: example-1
spec:
replicas: 2
selector:
deployment: example-1
deploymentconfig: example
name: example
template:
metadata:
annotations:
openshift.io/deployment-config.latest-version: "1"
openshift.io/deployment-config.name: example
openshift.io/deployment.name: example-1
creationTimestamp: null
labels:
deployment: example-1
deploymentconfig: example
name: example
spec:
containers:
- command:
- /bin/sleep
- "100"
image: docker.io/centos:centos7
imagePullPolicy: IfNotPresent
name: myapp
status:
fullyLabeledReplicas: 2
observedGeneration: 2
readyReplicas: 1
replicas: 2
- apiVersion: v1
kind: ReplicationController
metadata:
annotations:
openshift.io/deployer-pod.name: example-2-deploy
openshift.io/deployment-config.latest-version: "2"
openshift.io/deployment-config.name: example
openshift.io/deployment.phase: Running
openshift.io/deployment.replicas: "3"
openshift.io/deployment.status-reason: manual change
openshift.io/encoded-deployment-config: |
{"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"}]}}
creationTimestamp: 2016-04-07T04:11:55Z
generation: 4
labels:
openshift.io/deployment-config.name: example
name: example-2
spec:
replicas: 1
selector:
deployment: example-2
deploymentconfig: example
name: example
template:
metadata:
annotations:
openshift.io/deployment-config.latest-version: "2"
openshift.io/deployment-config.name: example
openshift.io/deployment.name: example-2
creationTimestamp: null
labels:
deployment: example-2
deploymentconfig: example
name: example
spec:
containers:
- command:
- /bin/sleep
- "100"
image: docker.io/centos:centos7
imagePullPolicy: IfNotPresent
name: myapp
status:
fullyLabeledReplicas: 1
readyReplicas: 0
replicas: 1
kind: List
metadata: {}
3 changes: 3 additions & 0 deletions pkg/api/graph/test/new-project-deployed-app.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ items:
restartPolicy: Always
status:
replicas: 2
readyReplicas: 2
- apiVersion: v1
kind: ReplicationController
metadata:
Expand Down Expand Up @@ -187,6 +188,7 @@ items:
restartPolicy: Always
status:
replicas: 1
readyReplicas: 1
- apiVersion: v1
kind: DeploymentConfig
metadata:
Expand Down Expand Up @@ -362,6 +364,7 @@ items:
restartPolicy: Always
status:
replicas: 1
readyReplicas: 1
- apiVersion: v1
kind: DeploymentConfig
metadata:
Expand Down
41 changes: 21 additions & 20 deletions pkg/cmd/cli/describe/projectstatus.go
Original file line number Diff line number Diff line change
Expand Up @@ -1013,52 +1013,53 @@ func describeDeployments(f formatter, dcNode *deploygraph.DeploymentConfigNode,
return out
}

func describeDeploymentStatus(deploy *kapi.ReplicationController, first, test bool, restartCount int32) string {
timeAt := strings.ToLower(formatRelativeTime(deploy.CreationTimestamp.Time))
status := deployutil.DeploymentStatusFor(deploy)
version := deployutil.DeploymentVersionFor(deploy)
func describeDeploymentStatus(rc *kapi.ReplicationController, first, test bool, restartCount int32) string {
timeAt := strings.ToLower(formatRelativeTime(rc.CreationTimestamp.Time))
status := deployutil.DeploymentStatusFor(rc)
version := deployutil.DeploymentVersionFor(rc)
maybeCancelling := ""
if deployutil.IsDeploymentCancelled(deploy) && !deployutil.IsTerminatedDeployment(deploy) {
if deployutil.IsDeploymentCancelled(rc) && !deployutil.IsTerminatedDeployment(rc) {
maybeCancelling = " (cancelling)"
}

switch status {
case deployapi.DeploymentStatusFailed:
reason := deployutil.DeploymentStatusReasonFor(deploy)
reason := deployutil.DeploymentStatusReasonFor(rc)
if len(reason) > 0 {
reason = fmt.Sprintf(": %s", reason)
}
// TODO: encode fail time in the rc
return fmt.Sprintf("deployment #%d failed %s ago%s%s", version, timeAt, reason, describePodSummaryInline(deploy.Status.Replicas, deploy.Spec.Replicas, false, restartCount))
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))
case deployapi.DeploymentStatusComplete:
// TODO: pod status output
if test {
return fmt.Sprintf("test deployment #%d deployed %s ago", version, timeAt)
}
return fmt.Sprintf("deployment #%d deployed %s ago%s", version, timeAt, describePodSummaryInline(deploy.Status.Replicas, deploy.Spec.Replicas, first, restartCount))
return fmt.Sprintf("deployment #%d deployed %s ago%s", version, timeAt, describePodSummaryInline(rc.Status.ReadyReplicas, rc.Status.Replicas, rc.Spec.Replicas, first, restartCount))
case deployapi.DeploymentStatusRunning:
format := "deployment #%d running%s for %s%s"
if test {
format = "test deployment #%d running%s for %s%s"
}
return fmt.Sprintf(format, version, maybeCancelling, timeAt, describePodSummaryInline(deploy.Status.Replicas, deploy.Spec.Replicas, false, restartCount))
return fmt.Sprintf(format, version, maybeCancelling, timeAt, describePodSummaryInline(rc.Status.ReadyReplicas, rc.Status.Replicas, rc.Spec.Replicas, false, restartCount))
default:
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))
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))
}
}

func describePetSetStatus(p *kapps.PetSet) string {
timeAt := strings.ToLower(formatRelativeTime(p.CreationTimestamp.Time))
return fmt.Sprintf("created %s ago%s", timeAt, describePodSummaryInline(int32(p.Status.Replicas), int32(p.Spec.Replicas), false, 0))
// TODO: Replace first argument in describePodSummaryInline with ReadyReplicas once that's a thing for pet sets.
return fmt.Sprintf("created %s ago%s", timeAt, describePodSummaryInline(int32(p.Status.Replicas), int32(p.Status.Replicas), int32(p.Spec.Replicas), false, 0))
}

func describeRCStatus(rc *kapi.ReplicationController) string {
timeAt := strings.ToLower(formatRelativeTime(rc.CreationTimestamp.Time))
return fmt.Sprintf("rc/%s created %s ago%s", rc.Name, timeAt, describePodSummaryInline(rc.Status.Replicas, rc.Spec.Replicas, false, 0))
return fmt.Sprintf("rc/%s created %s ago%s", rc.Name, timeAt, describePodSummaryInline(rc.Status.ReadyReplicas, rc.Status.Replicas, rc.Spec.Replicas, false, 0))
}

func describePodSummaryInline(actual, requested int32, includeEmpty bool, restartCount int32) string {
s := describePodSummary(actual, requested, includeEmpty, restartCount)
func describePodSummaryInline(ready, actual, requested int32, includeEmpty bool, restartCount int32) string {
s := describePodSummary(ready, requested, includeEmpty, restartCount)
if len(s) == 0 {
return s
}
Expand All @@ -1072,25 +1073,25 @@ func describePodSummaryInline(actual, requested int32, includeEmpty bool, restar
return fmt.Sprintf(" - %s%s", s, change)
}

func describePodSummary(actual, requested int32, includeEmpty bool, restartCount int32) string {
func describePodSummary(ready, requested int32, includeEmpty bool, restartCount int32) string {
var restartWarn string
if restartCount > 0 {
restartWarn = fmt.Sprintf(" (warning: %d restarts)", restartCount)
}
if actual == requested {
if ready == requested {
switch {
case actual == 0:
case ready == 0:
if !includeEmpty {
return ""
}
return "0 pods"
case actual > 1:
return fmt.Sprintf("%d pods", actual) + restartWarn
case ready > 1:
return fmt.Sprintf("%d pods", ready) + restartWarn
default:
return "1 pod" + restartWarn
}
}
return fmt.Sprintf("%d/%d pods", actual, requested) + restartWarn
return fmt.Sprintf("%d/%d pods", ready, requested) + restartWarn
}

func describeDeploymentConfigTriggers(config *deployapi.DeploymentConfig) (string, bool) {
Expand Down
14 changes: 14 additions & 0 deletions pkg/cmd/cli/describe/projectstatus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,20 @@ func TestProjectStatus(t *testing.T) {
`View details with 'oc describe <resource>/<name>' or list everything with 'oc get all'.`,
},
},
"deployment with unavailable pods": {
File: "available-deployment.yaml",
Extra: []runtime.Object{
&projectapi.Project{
ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""},
},
},
ErrFn: func(err error) bool { return err == nil },
Contains: []string{
"deployment #2 running for 30 seconds - 0/1 pods\n",
"deployment #1 deployed about a minute ago - 1/2 pods",
},
Time: mustParseTime("2016-04-07T04:12:25Z"),
},
}
oldTimeFn := timeNowFn
defer func() { timeNowFn = oldTimeFn }()
Expand Down