diff --git a/pkg/api/graph/test/available-deployment.yaml b/pkg/api/graph/test/available-deployment.yaml new file mode 100644 index 000000000000..d4d85881b48f --- /dev/null +++ b/pkg/api/graph/test/available-deployment.yaml @@ -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: {} diff --git a/pkg/api/graph/test/new-project-deployed-app.yaml b/pkg/api/graph/test/new-project-deployed-app.yaml index 62c7b06302e6..f7d6e07d7aa3 100644 --- a/pkg/api/graph/test/new-project-deployed-app.yaml +++ b/pkg/api/graph/test/new-project-deployed-app.yaml @@ -130,6 +130,7 @@ items: restartPolicy: Always status: replicas: 2 + readyReplicas: 2 - apiVersion: v1 kind: ReplicationController metadata: @@ -187,6 +188,7 @@ items: restartPolicy: Always status: replicas: 1 + readyReplicas: 1 - apiVersion: v1 kind: DeploymentConfig metadata: @@ -362,6 +364,7 @@ items: restartPolicy: Always status: replicas: 1 + readyReplicas: 1 - apiVersion: v1 kind: DeploymentConfig metadata: diff --git a/pkg/cmd/cli/describe/projectstatus.go b/pkg/cmd/cli/describe/projectstatus.go index d0b242c057aa..e3797abdffa1 100644 --- a/pkg/cmd/cli/describe/projectstatus.go +++ b/pkg/cmd/cli/describe/projectstatus.go @@ -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 } @@ -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) { diff --git a/pkg/cmd/cli/describe/projectstatus_test.go b/pkg/cmd/cli/describe/projectstatus_test.go index b7a495833975..cf1a610bc24b 100644 --- a/pkg/cmd/cli/describe/projectstatus_test.go +++ b/pkg/cmd/cli/describe/projectstatus_test.go @@ -367,6 +367,20 @@ func TestProjectStatus(t *testing.T) { `View details with 'oc describe /' 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 }()