diff --git a/api/cloudcontroller/ccv3/constant/deployment.go b/api/cloudcontroller/ccv3/constant/deployment.go index 2ae6159ebb..b35baa374c 100644 --- a/api/cloudcontroller/ccv3/constant/deployment.go +++ b/api/cloudcontroller/ccv3/constant/deployment.go @@ -31,3 +31,5 @@ const ( DeploymentStatusValueActive DeploymentStatusValue = "ACTIVE" DeploymentStatusValueFinalized DeploymentStatusValue = "FINALIZED" ) + +const DeploymentMaxInFlightDefaultValue int = 1 diff --git a/command/v7/shared/app_summary_displayer.go b/command/v7/shared/app_summary_displayer.go index c784e8f419..23d9affc4e 100644 --- a/command/v7/shared/app_summary_displayer.go +++ b/command/v7/shared/app_summary_displayer.go @@ -162,6 +162,12 @@ func (display AppSummaryDisplayer) displayProcessTable(summary v7action.Detailed if summary.Deployment.StatusValue == constant.DeploymentStatusValueActive { display.UI.DisplayNewline() display.UI.DisplayText(display.getDeploymentStatusText(summary)) + + var maxInFlight = summary.Deployment.Options.MaxInFlight + if maxInFlight > 0 && maxInFlight != constant.DeploymentMaxInFlightDefaultValue { + display.UI.DisplayText(fmt.Sprintf("max-in-flight: %d", maxInFlight)) + } + if summary.Deployment.Strategy == constant.DeploymentStrategyCanary && summary.Deployment.StatusReason == constant.DeploymentStatusReasonPaused { display.UI.DisplayNewline() display.UI.DisplayText(fmt.Sprintf("Please run `cf continue-deployment %s` to promote the canary deployment, or `cf cancel-deployment %s` to rollback to the previous version.", summary.Application.Name, summary.Application.Name)) @@ -171,25 +177,15 @@ func (display AppSummaryDisplayer) displayProcessTable(summary v7action.Detailed func (display AppSummaryDisplayer) getDeploymentStatusText(summary v7action.DetailedApplicationSummary) string { var lastStatusChangeTime = display.getLastStatusChangeTime(summary) - if lastStatusChangeTime != "" { return fmt.Sprintf("%s deployment currently %s (since %s)", cases.Title(language.English, cases.NoLower).String(string(summary.Deployment.Strategy)), summary.Deployment.StatusReason, lastStatusChangeTime) } else { - var sb strings.Builder - sb.WriteString(fmt.Sprintf("%s deployment currently %s.", + return fmt.Sprintf("%s deployment currently %s.", cases.Title(language.English, cases.NoLower).String(string(summary.Deployment.Strategy)), - summary.Deployment.StatusReason)) - - if summary.Deployment.Strategy == constant.DeploymentStrategyCanary && summary.Deployment.StatusReason == constant.DeploymentStatusReasonPaused { - sb.WriteString("\n") - sb.WriteString(fmt.Sprintf( - "Please run `cf continue-deployment %s` to promote the canary deployment, or `cf cancel-deployment %s` to rollback to the previous version.", - summary.Application.Name, summary.Application.Name)) - } - return sb.String() + summary.Deployment.StatusReason) } } diff --git a/command/v7/shared/app_summary_displayer_test.go b/command/v7/shared/app_summary_displayer_test.go index c34a4df3b1..4085a6df08 100644 --- a/command/v7/shared/app_summary_displayer_test.go +++ b/command/v7/shared/app_summary_displayer_test.go @@ -698,10 +698,58 @@ var _ = Describe("app summary displayer", func() { When("there is an active deployment", func() { var LastStatusChangeTimeString = "2024-07-29T17:32:29Z" var dateTimeRegexPattern = `[a-zA-Z]{3}\s\d{2}\s[a-zA-Z]{3}\s\d{2}\:\d{2}\:\d{2}\s[A-Z]{3}\s\d{4}` + var maxInFlightDefaultValue = 1 When("the deployment strategy is rolling", func() { When("the deployment is in progress", func() { - When("last status change has a timestamp", func() { + When("last status change has a timestamp and max-in-flight is non-default", func() { + BeforeEach(func() { + summary = v7action.DetailedApplicationSummary{ + Deployment: resources.Deployment{ + Strategy: constant.DeploymentStrategyRolling, + StatusValue: constant.DeploymentStatusValueActive, + StatusReason: constant.DeploymentStatusReasonDeploying, + LastStatusChange: LastStatusChangeTimeString, + Options: resources.DeploymentOpts{ + MaxInFlight: 2, + }, + }, + } + }) + + It("displays the message", func() { + var actualOut = fmt.Sprintf("%s", testUI.Out) + Expect(actualOut).To(MatchRegexp(`Rolling deployment currently DEPLOYING \(since %s\)`, dateTimeRegexPattern)) + }) + It("displays max-in-flight value", func() { + Expect(testUI.Out).To(Say(`max-in-flight: 2`)) + }) + }) + When("last status change has a timestamp and max-in-flight is default", func() { + BeforeEach(func() { + summary = v7action.DetailedApplicationSummary{ + Deployment: resources.Deployment{ + Strategy: constant.DeploymentStrategyRolling, + StatusValue: constant.DeploymentStatusValueActive, + StatusReason: constant.DeploymentStatusReasonDeploying, + LastStatusChange: LastStatusChangeTimeString, + Options: resources.DeploymentOpts{ + MaxInFlight: maxInFlightDefaultValue, + }, + }, + } + }) + + It("displays the message", func() { + var actualOut = fmt.Sprintf("%s", testUI.Out) + Expect(actualOut).To(MatchRegexp(`Rolling deployment currently DEPLOYING \(since %s\)`, dateTimeRegexPattern)) + }) + It("does not display max-in-flight", func() { + Expect(testUI.Out).NotTo(Say(`max-in-flight`)) + }) + }) + // 'unset' is important for the newer-CLI-than-CAPI scenario + When("last status change has a timestamp and max-in-flight is unset", func() { BeforeEach(func() { summary = v7action.DetailedApplicationSummary{ Deployment: resources.Deployment{ @@ -717,9 +765,35 @@ var _ = Describe("app summary displayer", func() { var actualOut = fmt.Sprintf("%s", testUI.Out) Expect(actualOut).To(MatchRegexp(`Rolling deployment currently DEPLOYING \(since %s\)`, dateTimeRegexPattern)) }) + It("does not display max-in-flight", func() { + Expect(testUI.Out).NotTo(Say(`max-in-flight`)) + }) }) - When("last status change is an empty string", func() { + When("last status change is an empty string and max-in-flight is non-default", func() { + BeforeEach(func() { + summary = v7action.DetailedApplicationSummary{ + Deployment: resources.Deployment{ + Strategy: constant.DeploymentStrategyRolling, + StatusValue: constant.DeploymentStatusValueActive, + StatusReason: constant.DeploymentStatusReasonDeploying, + LastStatusChange: "", + Options: resources.DeploymentOpts{ + MaxInFlight: 2, + }, + }, + } + }) + + It("displays the message", func() { + Expect(testUI.Out).To(Say(`Rolling deployment currently DEPLOYING`)) + Expect(testUI.Out).NotTo(Say(`\(since`)) + }) + It("displays max-in-flight value", func() { + Expect(testUI.Out).To(Say(`max-in-flight: 2`)) + }) + }) + When("last status change is an empty string and max-in-flight is default", func() { BeforeEach(func() { summary = v7action.DetailedApplicationSummary{ Deployment: resources.Deployment{ @@ -727,6 +801,9 @@ var _ = Describe("app summary displayer", func() { StatusValue: constant.DeploymentStatusValueActive, StatusReason: constant.DeploymentStatusReasonDeploying, LastStatusChange: "", + Options: resources.DeploymentOpts{ + MaxInFlight: maxInFlightDefaultValue, + }, }, } }) @@ -735,89 +812,223 @@ var _ = Describe("app summary displayer", func() { Expect(testUI.Out).To(Say(`Rolling deployment currently DEPLOYING`)) Expect(testUI.Out).NotTo(Say(`\(since`)) }) + It("does not display max-in-flight", func() { + Expect(testUI.Out).NotTo(Say(`max-in-flight`)) + }) }) }) When("the deployment is cancelled", func() { - BeforeEach(func() { - summary = v7action.DetailedApplicationSummary{ - Deployment: resources.Deployment{ - Strategy: constant.DeploymentStrategyRolling, - StatusValue: constant.DeploymentStatusValueActive, - StatusReason: constant.DeploymentStatusReasonCanceling, - LastStatusChange: LastStatusChangeTimeString, - }, - } + When("max-in-flight value is non-default", func() { + BeforeEach(func() { + summary = v7action.DetailedApplicationSummary{ + Deployment: resources.Deployment{ + Strategy: constant.DeploymentStrategyRolling, + StatusValue: constant.DeploymentStatusValueActive, + StatusReason: constant.DeploymentStatusReasonCanceling, + LastStatusChange: LastStatusChangeTimeString, + Options: resources.DeploymentOpts{ + MaxInFlight: 2, + }, + }, + } + }) + + It("displays the message", func() { + var actualOut = fmt.Sprintf("%s", testUI.Out) + Expect(actualOut).To(MatchRegexp(`Rolling deployment currently CANCELING \(since %s\)`, dateTimeRegexPattern)) + }) + It("displays max-in-flight value", func() { + Expect(testUI.Out).To(Say(`max-in-flight: 2`)) + }) }) + When("max-in-flight value is default", func() { + BeforeEach(func() { + summary = v7action.DetailedApplicationSummary{ + Deployment: resources.Deployment{ + Strategy: constant.DeploymentStrategyRolling, + StatusValue: constant.DeploymentStatusValueActive, + StatusReason: constant.DeploymentStatusReasonCanceling, + LastStatusChange: LastStatusChangeTimeString, + Options: resources.DeploymentOpts{ + MaxInFlight: maxInFlightDefaultValue, + }, + }, + } + }) - It("displays the message", func() { - var actualOut = fmt.Sprintf("%s", testUI.Out) - Expect(actualOut).To(MatchRegexp(`Rolling deployment currently CANCELING \(since %s\)`, dateTimeRegexPattern)) + It("displays the message", func() { + var actualOut = fmt.Sprintf("%s", testUI.Out) + Expect(actualOut).To(MatchRegexp(`Rolling deployment currently CANCELING \(since %s\)`, dateTimeRegexPattern)) + }) + It("does not display max-in-flight", func() { + Expect(testUI.Out).NotTo(Say(`max-in-flight`)) + }) }) }) }) When("the deployment strategy is canary", func() { When("the deployment is in progress", func() { - BeforeEach(func() { - summary = v7action.DetailedApplicationSummary{ - Deployment: resources.Deployment{ - Strategy: constant.DeploymentStrategyCanary, - StatusValue: constant.DeploymentStatusValueActive, - StatusReason: constant.DeploymentStatusReasonDeploying, - LastStatusChange: LastStatusChangeTimeString, - }, - } + When("max-in-flight value is non-default", func() { + BeforeEach(func() { + summary = v7action.DetailedApplicationSummary{ + Deployment: resources.Deployment{ + Strategy: constant.DeploymentStrategyCanary, + StatusValue: constant.DeploymentStatusValueActive, + StatusReason: constant.DeploymentStatusReasonDeploying, + LastStatusChange: LastStatusChangeTimeString, + Options: resources.DeploymentOpts{ + MaxInFlight: 2, + }, + }, + } + }) + + It("displays the message", func() { + var actualOut = fmt.Sprintf("%s", testUI.Out) + Expect(actualOut).To(MatchRegexp(`Canary deployment currently DEPLOYING \(since %s\)`, dateTimeRegexPattern)) + Expect(testUI.Out).NotTo(Say(`promote the canary deployment`)) + }) + It("displays max-in-flight value", func() { + Expect(testUI.Out).To(Say(`max-in-flight: 2`)) + }) }) + When("max-in-flight value is default", func() { + BeforeEach(func() { + summary = v7action.DetailedApplicationSummary{ + Deployment: resources.Deployment{ + Strategy: constant.DeploymentStrategyCanary, + StatusValue: constant.DeploymentStatusValueActive, + StatusReason: constant.DeploymentStatusReasonDeploying, + LastStatusChange: LastStatusChangeTimeString, + Options: resources.DeploymentOpts{ + MaxInFlight: maxInFlightDefaultValue, + }, + }, + } + }) - It("displays the message", func() { - var actualOut = fmt.Sprintf("%s", testUI.Out) - Expect(actualOut).To(MatchRegexp(`Canary deployment currently DEPLOYING \(since %s\)`, dateTimeRegexPattern)) - Expect(testUI.Out).NotTo(Say(`promote the canary deployment`)) + It("displays the message", func() { + var actualOut = fmt.Sprintf("%s", testUI.Out) + Expect(actualOut).To(MatchRegexp(`Canary deployment currently DEPLOYING \(since %s\)`, dateTimeRegexPattern)) + Expect(testUI.Out).NotTo(Say(`promote the canary deployment`)) + }) + It("does not display max-in-flight", func() { + Expect(testUI.Out).NotTo(Say(`max-in-flight`)) + }) }) }) When("the deployment is paused", func() { - BeforeEach(func() { - summary = v7action.DetailedApplicationSummary{ - ApplicationSummary: v7action.ApplicationSummary{ - Application: resources.Application{ - Name: "foobar", + When("max-in-flight value is non-default", func() { + BeforeEach(func() { + summary = v7action.DetailedApplicationSummary{ + ApplicationSummary: v7action.ApplicationSummary{ + Application: resources.Application{ + Name: "foobar", + }, }, - }, - Deployment: resources.Deployment{ - Strategy: constant.DeploymentStrategyCanary, - StatusValue: constant.DeploymentStatusValueActive, - StatusReason: constant.DeploymentStatusReasonPaused, - LastStatusChange: LastStatusChangeTimeString, - }, - } + Deployment: resources.Deployment{ + Strategy: constant.DeploymentStrategyCanary, + StatusValue: constant.DeploymentStatusValueActive, + StatusReason: constant.DeploymentStatusReasonPaused, + LastStatusChange: LastStatusChangeTimeString, + Options: resources.DeploymentOpts{ + MaxInFlight: 2, + }, + }, + } + }) + + It("displays the message", func() { + var actualOut = fmt.Sprintf("%s", testUI.Out) + Expect(actualOut).To(MatchRegexp(`Canary deployment currently PAUSED \(since %s\)`, dateTimeRegexPattern)) + Expect(testUI.Out).To(Say("Please run `cf continue-deployment foobar` to promote the canary deployment, or `cf cancel-deployment foobar` to rollback to the previous version.")) + }) + It("displays max-in-flight value", func() { + Expect(testUI.Out).To(Say(`max-in-flight: 2`)) + }) }) + When("max-in-flight value is default", func() { + BeforeEach(func() { + summary = v7action.DetailedApplicationSummary{ + ApplicationSummary: v7action.ApplicationSummary{ + Application: resources.Application{ + Name: "foobar", + }, + }, + Deployment: resources.Deployment{ + Strategy: constant.DeploymentStrategyCanary, + StatusValue: constant.DeploymentStatusValueActive, + StatusReason: constant.DeploymentStatusReasonPaused, + LastStatusChange: LastStatusChangeTimeString, + Options: resources.DeploymentOpts{ + MaxInFlight: maxInFlightDefaultValue, + }, + }, + } + }) - It("displays the message", func() { - var actualOut = fmt.Sprintf("%s", testUI.Out) - Expect(actualOut).To(MatchRegexp(`Canary deployment currently PAUSED \(since %s\)`, dateTimeRegexPattern)) - Expect(testUI.Out).To(Say("Please run `cf continue-deployment foobar` to promote the canary deployment, or `cf cancel-deployment foobar` to rollback to the previous version.")) + It("displays the message", func() { + var actualOut = fmt.Sprintf("%s", testUI.Out) + Expect(actualOut).To(MatchRegexp(`Canary deployment currently PAUSED \(since %s\)`, dateTimeRegexPattern)) + Expect(testUI.Out).To(Say("Please run `cf continue-deployment foobar` to promote the canary deployment, or `cf cancel-deployment foobar` to rollback to the previous version.")) + }) + It("does not display max-in-flight", func() { + Expect(testUI.Out).NotTo(Say(`max-in-flight`)) + }) }) }) When("the deployment is canceling", func() { - BeforeEach(func() { - summary = v7action.DetailedApplicationSummary{ - Deployment: resources.Deployment{ - Strategy: constant.DeploymentStrategyCanary, - StatusValue: constant.DeploymentStatusValueActive, - StatusReason: constant.DeploymentStatusReasonCanceling, - LastStatusChange: LastStatusChangeTimeString, - }, - } - }) + When("max-in-flight value is non-default", func() { + BeforeEach(func() { + summary = v7action.DetailedApplicationSummary{ + Deployment: resources.Deployment{ + Strategy: constant.DeploymentStrategyCanary, + StatusValue: constant.DeploymentStatusValueActive, + StatusReason: constant.DeploymentStatusReasonCanceling, + LastStatusChange: LastStatusChangeTimeString, + Options: resources.DeploymentOpts{ + MaxInFlight: 2, + }, + }, + } + }) - It("displays the message", func() { - var actualOut = fmt.Sprintf("%s", testUI.Out) - Expect(actualOut).To(MatchRegexp(`Canary deployment currently CANCELING \(since %s\)`, dateTimeRegexPattern)) - Expect(testUI.Out).NotTo(Say(`promote the canary deployment`)) + It("displays the message", func() { + var actualOut = fmt.Sprintf("%s", testUI.Out) + Expect(actualOut).To(MatchRegexp(`Canary deployment currently CANCELING \(since %s\)`, dateTimeRegexPattern)) + Expect(testUI.Out).NotTo(Say(`promote the canary deployment`)) + }) + It("displays max-in-flight value", func() { + Expect(testUI.Out).To(Say(`max-in-flight: 2`)) + }) }) + When("max-in-flight value is default", func() { + BeforeEach(func() { + summary = v7action.DetailedApplicationSummary{ + Deployment: resources.Deployment{ + Strategy: constant.DeploymentStrategyCanary, + StatusValue: constant.DeploymentStatusValueActive, + StatusReason: constant.DeploymentStatusReasonCanceling, + LastStatusChange: LastStatusChangeTimeString, + Options: resources.DeploymentOpts{ + MaxInFlight: maxInFlightDefaultValue, + }, + }, + } + }) + It("displays the message", func() { + var actualOut = fmt.Sprintf("%s", testUI.Out) + Expect(actualOut).To(MatchRegexp(`Canary deployment currently CANCELING \(since %s\)`, dateTimeRegexPattern)) + Expect(testUI.Out).NotTo(Say(`promote the canary deployment`)) + }) + It("does not display max-in-flight", func() { + Expect(testUI.Out).NotTo(Say(`max-in-flight`)) + }) + }) }) }) }) @@ -838,6 +1049,9 @@ var _ = Describe("app summary displayer", func() { cases.Title(language.English, cases.NoLower).String(string(summary.Deployment.Strategy)), summary.Deployment.StatusReason))) }) + It("does not display max-in-flight", func() { + Expect(testUI.Out).NotTo(Say(`max-in-flight`)) + }) }) }) })