Skip to content

Commit 1ef8c9e

Browse files
gururajshMichael Chinigo
andauthored
Display deployment status for cf app command [main] (#3042)
* Display deployment status Co-authored-by: Michael Chinigo <[email protected]>
1 parent 9b35b37 commit 1ef8c9e

File tree

9 files changed

+266
-6
lines changed

9 files changed

+266
-6
lines changed

.github/ops-files/replace-redis.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
- type: replace
3+
path: /instance_groups/name=api/jobs/name=redis?
4+
value:
5+
name: valkey
6+
release: capi

.github/workflows/tests-integration-reusable.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ jobs:
179179
bosh -d cf manifest > /tmp/manifest.yml
180180
bosh interpolate /tmp/manifest.yml \
181181
-o .github/ops-files/use-cflinuxfs3.yml \
182+
-o .github/ops-files/replace-redis.yml \
182183
-o cf-deployment/operations/use-internal-lookup-for-route-services.yml \
183184
-o cf-deployment/operations/add-persistent-isolation-segment-diego-cell.yml \
184185
-o cli-ci/ci/infrastructure/operations/use-latest-capi.yml \

actor/v7action/application_summary.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package v7action
22

33
import (
4+
"errors"
5+
46
"code.cloudfoundry.org/cli/actor/actionerror"
57
"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
68
"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3"
@@ -18,6 +20,7 @@ type ApplicationSummary struct {
1820
type DetailedApplicationSummary struct {
1921
ApplicationSummary
2022
CurrentDroplet resources.Droplet
23+
Deployment resources.Deployment
2124
}
2225

2326
func (a ApplicationSummary) GetIsolationSegmentName() (string, bool) {
@@ -120,6 +123,12 @@ func (actor Actor) GetDetailedAppSummary(appName, spaceGUID string, withObfuscat
120123
return DetailedApplicationSummary{}, allWarnings, err
121124
}
122125

126+
detailedSummary, warnings, err = actor.addDeployment(detailedSummary)
127+
allWarnings = append(allWarnings, warnings...)
128+
if err != nil {
129+
return DetailedApplicationSummary{}, allWarnings, err
130+
}
131+
123132
return detailedSummary, allWarnings, err
124133
}
125134

@@ -206,6 +215,19 @@ func (actor Actor) addDroplet(summary ApplicationSummary) (DetailedApplicationSu
206215
}, allWarnings, nil
207216
}
208217

218+
func (actor Actor) addDeployment(detailedSummary DetailedApplicationSummary) (DetailedApplicationSummary, Warnings, error) {
219+
var allWarnings Warnings
220+
221+
deployment, warnings, err := actor.GetLatestActiveDeploymentForApp(detailedSummary.GUID)
222+
allWarnings = append(allWarnings, warnings...)
223+
if err != nil && !errors.Is(err, actionerror.ActiveDeploymentNotFoundError{}) {
224+
return DetailedApplicationSummary{}, allWarnings, err
225+
}
226+
227+
detailedSummary.Deployment = deployment
228+
return detailedSummary, allWarnings, nil
229+
}
230+
209231
func toAppGUIDs(apps []resources.Application) []string {
210232
guids := make([]string, len(apps))
211233

actor/v7action/application_summary_test.go

Lines changed: 111 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"errors"
55
"fmt"
66

7-
"code.cloudfoundry.org/cli/actor/v7action"
87
. "code.cloudfoundry.org/cli/actor/v7action"
98
"code.cloudfoundry.org/cli/actor/v7action/v7actionfakes"
109
"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
@@ -574,6 +573,115 @@ var _ = Describe("Application Summary Actions", func() {
574573
)
575574
})
576575

576+
When("getting application deployment succeeds", func() {
577+
When("the deployment is active", func() {
578+
When("the deployment strategy is rolling", func() {
579+
When("the deployment is in progress", func() {
580+
BeforeEach(func() {
581+
fakeCloudControllerClient.GetDeploymentsReturns(
582+
[]resources.Deployment{
583+
{
584+
GUID: "some-deployment-guid",
585+
Strategy: "rolling",
586+
StatusValue: "ACTIVE",
587+
StatusReason: "DEPLOYING",
588+
},
589+
},
590+
nil,
591+
nil,
592+
)
593+
})
594+
It("returns the deployment information", func() {
595+
Expect(summary.Deployment).To(Equal(resources.Deployment{
596+
GUID: "some-deployment-guid",
597+
Strategy: "rolling",
598+
StatusValue: "ACTIVE",
599+
StatusReason: "DEPLOYING",
600+
}))
601+
})
602+
})
603+
604+
When("the deployment is canceled", func() {
605+
When("the deployment is in progress", func() {
606+
BeforeEach(func() {
607+
fakeCloudControllerClient.GetDeploymentsReturns(
608+
[]resources.Deployment{
609+
{
610+
GUID: "some-deployment-guid",
611+
Strategy: "rolling",
612+
StatusValue: "ACTIVE",
613+
StatusReason: "CANCELLING",
614+
},
615+
},
616+
nil,
617+
nil,
618+
)
619+
})
620+
It("returns the deployment information", func() {
621+
Expect(summary.Deployment).To(Equal(resources.Deployment{
622+
GUID: "some-deployment-guid",
623+
Strategy: "rolling",
624+
StatusValue: "ACTIVE",
625+
StatusReason: "CANCELLING",
626+
}))
627+
})
628+
})
629+
})
630+
})
631+
})
632+
633+
When("the deployment is not active", func() {
634+
BeforeEach(func() {
635+
fakeCloudControllerClient.GetDeploymentsReturns(
636+
[]resources.Deployment{
637+
{
638+
GUID: "",
639+
Strategy: "",
640+
StatusValue: "",
641+
StatusReason: "",
642+
},
643+
},
644+
nil,
645+
nil,
646+
)
647+
})
648+
It("returns no deployment information", func() {
649+
Expect(summary.Deployment).To(Equal(resources.Deployment{
650+
GUID: "",
651+
Strategy: "",
652+
StatusValue: "",
653+
StatusReason: "",
654+
}))
655+
})
656+
})
657+
})
658+
659+
When("getting application deployment fails", func() {
660+
BeforeEach(func() {
661+
fakeCloudControllerClient.GetDeploymentsReturns(
662+
nil,
663+
ccv3.Warnings{"get-deployments-warning"},
664+
errors.New("some-error"),
665+
)
666+
})
667+
668+
It("returns the warnings and error", func() {
669+
Expect(executeErr).To(MatchError("some-error"))
670+
Expect(warnings).To(ConsistOf(
671+
"get-apps-warning",
672+
"get-app-processes-warning",
673+
"get-process-by-type-warning",
674+
"get-process-sidecars-warning",
675+
"get-process-instances-warning",
676+
"get-process-by-type-warning",
677+
"get-process-sidecars-warning",
678+
"get-process-instances-warning",
679+
"get-app-droplet-warning",
680+
"get-deployments-warning",
681+
))
682+
})
683+
})
684+
577685
When("getting application routes succeeds", func() {
578686
BeforeEach(func() {
579687
fakeCloudControllerClient.GetApplicationRoutesReturns(
@@ -589,7 +697,7 @@ var _ = Describe("Application Summary Actions", func() {
589697
It("returns the summary and warnings with droplet information", func() {
590698
Expect(executeErr).ToNot(HaveOccurred())
591699
Expect(summary).To(Equal(DetailedApplicationSummary{
592-
ApplicationSummary: v7action.ApplicationSummary{
700+
ApplicationSummary: ApplicationSummary{
593701
Application: resources.Application{
594702
Name: "some-app-name",
595703
GUID: "some-app-guid",
@@ -733,7 +841,7 @@ var _ = Describe("Application Summary Actions", func() {
733841
It("returns the summary and warnings without droplet information", func() {
734842
Expect(executeErr).ToNot(HaveOccurred())
735843
Expect(summary).To(Equal(DetailedApplicationSummary{
736-
ApplicationSummary: v7action.ApplicationSummary{
844+
ApplicationSummary: ApplicationSummary{
737845
Application: resources.Application{
738846
Name: "some-app-name",
739847
GUID: "some-app-guid",

api/cloudcontroller/ccv3/constant/deployment.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const (
1414
// DeploymentDeployed means the deployment is in state 'DEPLOYED'
1515
DeploymentDeployed DeploymentState = "DEPLOYED"
1616

17-
// DeploymentCanceled means the deployment is in state 'CANCELING'
17+
// DeploymentCanceling means the deployment is in state 'CANCELING'
1818
DeploymentCanceling DeploymentState = "CANCELING"
1919

2020
// DeploymentFailing means the deployment is in state 'FAILING'
@@ -28,6 +28,12 @@ const (
2828
type DeploymentStatusReason string
2929

3030
const (
31+
// DeploymentStatusReasonDeploying means the deployment is in state 'DEPLOYING'
32+
DeploymentStatusReasonDeploying DeploymentStatusReason = "DEPLOYING"
33+
34+
// DeploymentCanceled means the deployment is in state 'CANCELING'
35+
DeploymentStatusReasonCanceling DeploymentStatusReason = "CANCELING"
36+
3137
// DeploymentStatusReasonDeployed means the deployment's status.value is
3238
// 'DEPLOYED'
3339
DeploymentStatusReasonDeployed DeploymentStatusReason = "DEPLOYED"

command/v7/shared/app_summary_displayer.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212
"code.cloudfoundry.org/cli/resources"
1313
"code.cloudfoundry.org/cli/util/ui"
1414
log "github.com/sirupsen/logrus"
15+
"golang.org/x/text/cases"
16+
"golang.org/x/text/language"
1517
)
1618

1719
type AppSummaryDisplayer struct {
@@ -156,6 +158,13 @@ func (display AppSummaryDisplayer) displayProcessTable(summary v7action.Detailed
156158
}
157159
display.displayAppInstancesTable(process)
158160
}
161+
162+
if summary.Deployment.StatusValue == constant.DeploymentStatusValueActive {
163+
display.UI.DisplayNewline()
164+
display.UI.DisplayText(fmt.Sprintf("%s deployment currently %s.",
165+
cases.Title(language.English, cases.NoLower).String(string(summary.Deployment.Strategy)),
166+
summary.Deployment.StatusReason))
167+
}
159168
}
160169

161170
func (display AppSummaryDisplayer) getCreatedTime(summary v7action.DetailedApplicationSummary) string {

command/v7/shared/app_summary_displayer_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package shared_test
22

33
import (
4+
"fmt"
45
"time"
56

67
"code.cloudfoundry.org/cli/actor/v7action"
@@ -13,6 +14,8 @@ import (
1314
. "github.com/onsi/ginkgo/v2"
1415
. "github.com/onsi/gomega"
1516
. "github.com/onsi/gomega/gbytes"
17+
"golang.org/x/text/cases"
18+
"golang.org/x/text/language"
1619
)
1720

1821
var _ = Describe("app summary displayer", func() {
@@ -691,5 +694,59 @@ var _ = Describe("app summary displayer", func() {
691694
Expect(testUI.Out).To(Say(`some-buildpack`))
692695
})
693696
})
697+
698+
When("there is an active deployment", func() {
699+
When("the deployment strategy is rolling", func() {
700+
When("the deployment is in progress", func() {
701+
BeforeEach(func() {
702+
summary = v7action.DetailedApplicationSummary{
703+
Deployment: resources.Deployment{
704+
Strategy: constant.DeploymentStrategyRolling,
705+
StatusValue: constant.DeploymentStatusValueActive,
706+
StatusReason: constant.DeploymentStatusReasonDeploying,
707+
},
708+
}
709+
})
710+
711+
It("displays the message", func() {
712+
Expect(testUI.Out).To(Say("Rolling deployment currently DEPLOYING."))
713+
})
714+
})
715+
716+
When("the deployment is cancelled", func() {
717+
BeforeEach(func() {
718+
summary = v7action.DetailedApplicationSummary{
719+
Deployment: resources.Deployment{
720+
Strategy: constant.DeploymentStrategyRolling,
721+
StatusValue: constant.DeploymentStatusValueActive,
722+
StatusReason: constant.DeploymentStatusReasonCanceling,
723+
},
724+
}
725+
})
726+
727+
It("displays the message", func() {
728+
Expect(testUI.Out).To(Say("Rolling deployment currently CANCELING."))
729+
})
730+
})
731+
})
732+
})
733+
734+
When("there is no active deployment", func() {
735+
BeforeEach(func() {
736+
summary = v7action.DetailedApplicationSummary{
737+
Deployment: resources.Deployment{
738+
Strategy: "",
739+
StatusValue: "",
740+
StatusReason: "",
741+
},
742+
}
743+
})
744+
745+
It("does not display deployment info", func() {
746+
Expect(testUI.Out).NotTo(Say(fmt.Sprintf("%s deployment currently %s",
747+
cases.Title(language.English, cases.NoLower).String(string(summary.Deployment.Strategy)),
748+
summary.Deployment.StatusReason)))
749+
})
750+
})
694751
})
695752
})

integration/v7/isolated/app_command_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,53 @@ applications:
253253
Eventually(session).Should(Exit(0))
254254
})
255255
})
256+
257+
When("there is an active deployment", func() {
258+
BeforeEach(func() {
259+
helpers.WithHelloWorldApp(func(appDir string) {
260+
Eventually(helpers.CF("push", appName, "-p", appDir, "-b", "staticfile_buildpack")).Should(Exit(0))
261+
})
262+
})
263+
264+
When("the deployment strategy is rolling", func() {
265+
When("the deployment is in progress", func() {
266+
It("displays the message", func() {
267+
session := helpers.CF("restart", appName, "--strategy", "rolling")
268+
269+
session1 := helpers.CF("app", appName)
270+
Eventually(session1).Should(Say("Rolling deployment currently DEPLOYING."))
271+
Eventually(session).Should(Exit(0))
272+
Eventually(session1).Should(Exit(0))
273+
})
274+
})
275+
When("the deployment is cancelled", func() {
276+
It("displays the message", func() {
277+
helpers.CF("restart", appName, "--strategy", "rolling")
278+
Eventually(func() *Session {
279+
return helpers.CF("cancel-deployment", appName).Wait()
280+
}).Should(Exit(0))
281+
282+
session2 := helpers.CF("app", appName)
283+
Eventually(session2).Should(Say("Rolling deployment currently CANCELING."))
284+
Eventually(session2).Should(Exit(0))
285+
})
286+
})
287+
})
288+
})
289+
290+
When("there is no active deployment", func() {
291+
BeforeEach(func() {
292+
helpers.WithHelloWorldApp(func(appDir string) {
293+
Eventually(helpers.CF("push", appName, "-p", appDir, "-b", "staticfile_buildpack")).Should(Exit(0))
294+
})
295+
})
296+
297+
It("doesn not display the message", func() {
298+
session := helpers.CF("app", appName)
299+
Eventually(session).Should(Exit(0))
300+
Eventually(session).ShouldNot(Say(`\w+ deployment currently \w+`))
301+
})
302+
})
256303
})
257304

258305
Describe("version independent display", func() {

0 commit comments

Comments
 (0)