Skip to content

Commit 69f68e5

Browse files
Max in flight flag (#3085) (#3096)
* Allow the user to set max-in-flight while pushing app * Add max-in-flight flag to the restart command * Add max-in-flight flag to restage command * Add max-in-flight flag to rollback command * Add max-in-flight flag to copy-src command * Remove `--no-wait` to ensure no flakes --------- Signed-off-by: João Pereira <[email protected]>
1 parent f0585b1 commit 69f68e5

29 files changed

+471
-98
lines changed

actor/v7pushaction/actor.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ func NewActor(v3Actor V7Actor, sharedActor SharedActor) *Actor {
7171
SetDefaultBitsPathForPushPlan,
7272
SetupDropletPathForPushPlan,
7373
actor.SetupAllResourcesForPushPlan,
74-
SetupDeploymentStrategyForPushPlan,
74+
SetupDeploymentInformationForPushPlan,
7575
SetupNoStartForPushPlan,
7676
SetupNoWaitForPushPlan,
7777
SetupTaskAppForPushPlan,

actor/v7pushaction/actor_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ var _ = Describe("Actor", func() {
2222
SetDefaultBitsPathForPushPlan,
2323
SetupDropletPathForPushPlan,
2424
actor.SetupAllResourcesForPushPlan,
25-
SetupDeploymentStrategyForPushPlan,
25+
SetupDeploymentInformationForPushPlan,
2626
SetupNoStartForPushPlan,
2727
SetupNoWaitForPushPlan,
2828
SetupTaskAppForPushPlan,

actor/v7pushaction/create_deployment_for_push_plan.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,16 @@ import (
88
func (actor Actor) CreateDeploymentForApplication(pushPlan PushPlan, eventStream chan<- *PushEvent, progressBar ProgressBar) (PushPlan, Warnings, error) {
99
eventStream <- &PushEvent{Plan: pushPlan, Event: StartingDeployment}
1010

11-
var dep resources.Deployment
12-
dep.DropletGUID = pushPlan.DropletGUID
13-
dep.Strategy = pushPlan.Strategy
14-
dep.Relationships = resources.Relationships{constant.RelationshipTypeApplication: resources.Relationship{GUID: pushPlan.Application.GUID}}
11+
dep := resources.Deployment{
12+
Strategy: pushPlan.Strategy,
13+
DropletGUID: pushPlan.DropletGUID,
14+
Relationships: resources.Relationships{constant.RelationshipTypeApplication: resources.Relationship{GUID: pushPlan.Application.GUID}},
15+
}
16+
17+
if pushPlan.MaxInFlight != 0 {
18+
dep.Options = resources.DeploymentOpts{MaxInFlight: pushPlan.MaxInFlight}
19+
}
20+
1521
deploymentGUID, warnings, err := actor.V7Actor.CreateDeployment(dep)
1622

1723
if err != nil {

actor/v7pushaction/create_deployment_for_push_plan_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"code.cloudfoundry.org/cli/actor/v7action"
77
. "code.cloudfoundry.org/cli/actor/v7pushaction"
88
"code.cloudfoundry.org/cli/actor/v7pushaction/v7pushactionfakes"
9+
"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
910
"code.cloudfoundry.org/cli/resources"
1011
. "github.com/onsi/ginkgo/v2"
1112
. "github.com/onsi/gomega"
@@ -106,6 +107,51 @@ var _ = Describe("CreateDeploymentForApplication()", func() {
106107
Expect(events).To(ConsistOf(StartingDeployment))
107108
})
108109
})
110+
111+
When("strategy is provided", func() {
112+
BeforeEach(func() {
113+
fakeV7Actor.PollStartForDeploymentCalls(func(_ resources.Application, _ string, _ bool, handleInstanceDetails func(string)) (warnings v7action.Warnings, err error) {
114+
handleInstanceDetails("Instances starting...")
115+
return nil, nil
116+
})
117+
118+
fakeV7Actor.CreateDeploymentReturns(
119+
"some-deployment-guid",
120+
v7action.Warnings{"some-deployment-warning"},
121+
nil,
122+
)
123+
paramPlan.Strategy = "rolling"
124+
paramPlan.MaxInFlight = 10
125+
})
126+
127+
It("waits for the app to start", func() {
128+
Expect(fakeV7Actor.PollStartForDeploymentCallCount()).To(Equal(1))
129+
givenApp, givenDeploymentGUID, noWait, _ := fakeV7Actor.PollStartForDeploymentArgsForCall(0)
130+
Expect(givenApp).To(Equal(resources.Application{GUID: "some-app-guid"}))
131+
Expect(givenDeploymentGUID).To(Equal("some-deployment-guid"))
132+
Expect(noWait).To(Equal(false))
133+
Expect(events).To(ConsistOf(StartingDeployment, InstanceDetails, WaitingForDeployment))
134+
Expect(fakeV7Actor.CreateDeploymentCallCount()).To(Equal(1))
135+
dep := fakeV7Actor.CreateDeploymentArgsForCall(0)
136+
Expect(dep).To(Equal(resources.Deployment{
137+
Strategy: "rolling",
138+
Options: resources.DeploymentOpts{MaxInFlight: 10},
139+
Relationships: resources.Relationships{
140+
constant.RelationshipTypeApplication: resources.Relationship{GUID: "some-app-guid"},
141+
},
142+
}))
143+
})
144+
145+
It("returns errors and warnings", func() {
146+
Expect(returnedPushPlan).To(Equal(paramPlan))
147+
Expect(executeErr).NotTo(HaveOccurred())
148+
Expect(warnings).To(ConsistOf("some-deployment-warning"))
149+
})
150+
151+
It("records deployment events", func() {
152+
Expect(events).To(ConsistOf(StartingDeployment, InstanceDetails, WaitingForDeployment))
153+
})
154+
})
109155
})
110156

111157
Describe("waiting for app to start", func() {

actor/v7pushaction/push_plan.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type PushPlan struct {
2020
NoStart bool
2121
NoWait bool
2222
Strategy constant.DeploymentStrategy
23+
MaxInFlight int
2324
TaskTypeApplication bool
2425

2526
DockerImageCredentials v7action.DockerImageCredentials
@@ -47,6 +48,7 @@ type FlagOverrides struct {
4748
HealthCheckType constant.HealthCheckType
4849
Instances types.NullInt
4950
Memory string
51+
MaxInFlight *int
5052
NoStart bool
5153
NoWait bool
5254
ProvidedAppPath string
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package v7pushaction
2+
3+
import "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
4+
5+
func SetupDeploymentInformationForPushPlan(pushPlan PushPlan, overrides FlagOverrides) (PushPlan, error) {
6+
pushPlan.Strategy = overrides.Strategy
7+
8+
if overrides.Strategy != constant.DeploymentStrategyDefault && overrides.MaxInFlight != nil {
9+
pushPlan.MaxInFlight = *overrides.MaxInFlight
10+
}
11+
12+
return pushPlan, nil
13+
}

actor/v7pushaction/setup_deployment_strategy_for_push_plan_test.go renamed to actor/v7pushaction/setup_deployment_information_for_push_plan_test.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
. "github.com/onsi/gomega"
1010
)
1111

12-
var _ = Describe("SetupDeploymentStrategyForPushPlan", func() {
12+
var _ = Describe("SetupDeploymentInformationForPushPlan", func() {
1313
var (
1414
pushPlan PushPlan
1515
overrides FlagOverrides
@@ -24,24 +24,47 @@ var _ = Describe("SetupDeploymentStrategyForPushPlan", func() {
2424
})
2525

2626
JustBeforeEach(func() {
27-
expectedPushPlan, executeErr = SetupDeploymentStrategyForPushPlan(pushPlan, overrides)
27+
expectedPushPlan, executeErr = SetupDeploymentInformationForPushPlan(pushPlan, overrides)
2828
})
2929

3030
When("flag overrides specifies strategy", func() {
3131
BeforeEach(func() {
3232
overrides.Strategy = "rolling"
33+
maxInFlight := 5
34+
overrides.MaxInFlight = &maxInFlight
3335
})
3436

3537
It("sets the strategy on the push plan", func() {
3638
Expect(executeErr).ToNot(HaveOccurred())
3739
Expect(expectedPushPlan.Strategy).To(Equal(constant.DeploymentStrategyRolling))
3840
})
41+
42+
It("sets the max in flight on the push plan", func() {
43+
Expect(executeErr).ToNot(HaveOccurred())
44+
Expect(expectedPushPlan.MaxInFlight).To(Equal(5))
45+
})
3946
})
4047

4148
When("flag overrides does not specify strategy", func() {
49+
BeforeEach(func() {
50+
maxInFlight := 10
51+
overrides.MaxInFlight = &maxInFlight
52+
})
4253
It("leaves the strategy as its default value on the push plan", func() {
4354
Expect(executeErr).ToNot(HaveOccurred())
4455
Expect(expectedPushPlan.Strategy).To(Equal(constant.DeploymentStrategyDefault))
4556
})
57+
58+
It("does not set MaxInFlight", func() {
59+
Expect(executeErr).ToNot(HaveOccurred())
60+
Expect(expectedPushPlan.MaxInFlight).To(Equal(0))
61+
})
62+
})
63+
64+
When("flag not provided", func() {
65+
It("does not set MaxInFlight", func() {
66+
Expect(executeErr).ToNot(HaveOccurred())
67+
Expect(expectedPushPlan.MaxInFlight).To(Equal(0))
68+
})
4669
})
4770
})

actor/v7pushaction/setup_deployment_strategy_for_push_plan.go

Lines changed: 0 additions & 7 deletions
This file was deleted.

command/translatableerror/convert_to_translatable_error.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,13 @@ func ConvertToTranslatableError(err error) error {
188188
return RunTaskError{Message: "App is not staged."}
189189
}
190190

191+
if strings.Contains(e.Message, "Unknown field(s): 'options'") {
192+
return MinimumCFAPIVersionNotMetError{
193+
Command: "'--max-in-flight' flag",
194+
MinimumVersion: "3.173.0",
195+
}
196+
}
197+
191198
// JSON Errors
192199
case *json.SyntaxError:
193200
return JSONSyntaxError{Err: e}

command/v7/copy_source_command.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ type CopySourceCommand struct {
1717
RequiredArgs flag.CopySourceArgs `positional-args:"yes"`
1818
usage interface{} `usage:"CF_NAME copy-source SOURCE_APP DESTINATION_APP [-s TARGET_SPACE [-o TARGET_ORG]] [--no-restart] [--strategy STRATEGY] [--no-wait]"`
1919
Strategy flag.DeploymentStrategy `long:"strategy" description:"Deployment strategy can be canary, rolling or null"`
20+
MaxInFlight *int `long:"max-in-flight" description:"Defines the maximum number of instances that will be actively being started. Only applies when --strategy flag is specified."`
2021
NoWait bool `long:"no-wait" description:"Exit when the first instance of the web process is healthy"`
2122
NoRestart bool `long:"no-restart" description:"Do not restage the destination application"`
2223
Organization string `short:"o" long:"organization" description:"Org that contains the destination application"`
@@ -48,6 +49,14 @@ func (cmd *CopySourceCommand) ValidateFlags() error {
4849
}
4950
}
5051

52+
if cmd.Strategy.Name == constant.DeploymentStrategyDefault && cmd.MaxInFlight != nil {
53+
return translatableerror.RequiredFlagsError{Arg1: "--max-in-flight", Arg2: "--strategy"}
54+
}
55+
56+
if cmd.Strategy.Name != constant.DeploymentStrategyDefault && cmd.MaxInFlight != nil && *cmd.MaxInFlight < 1 {
57+
return translatableerror.IncorrectUsageError{Message: "--max-in-flight must be greater than or equal to 1"}
58+
}
59+
5160
return nil
5261
}
5362

@@ -160,10 +169,15 @@ func (cmd CopySourceCommand) Execute(args []string) error {
160169
cmd.UI.DisplayNewline()
161170

162171
opts := shared.AppStartOpts{
163-
Strategy: cmd.Strategy.Name,
164-
NoWait: cmd.NoWait,
165172
AppAction: constant.ApplicationRestarting,
173+
NoWait: cmd.NoWait,
174+
Strategy: cmd.Strategy.Name,
166175
}
176+
177+
if cmd.MaxInFlight != nil {
178+
opts.MaxInFlight = *cmd.MaxInFlight
179+
}
180+
167181
err = cmd.Stager.StageAndStart(targetApp, targetSpace, targetOrg, pkg.GUID, opts)
168182
if err != nil {
169183
return mapErr(cmd.Config, targetApp.Name, err)

0 commit comments

Comments
 (0)