Skip to content

Commit 3e027ae

Browse files
committed
Add --instance-steps to copy-source command
Signed-off-by: João Pereira <[email protected]>
1 parent 912fc63 commit 3e027ae

File tree

3 files changed

+70
-3
lines changed

3 files changed

+70
-3
lines changed

command/v7/copy_source_command.go

+26-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
package v7
22

33
import (
4+
"strconv"
5+
"strings"
6+
47
"code.cloudfoundry.org/cli/actor/v7action"
58
"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
69
"code.cloudfoundry.org/cli/api/logcache"
710
"code.cloudfoundry.org/cli/command"
811
"code.cloudfoundry.org/cli/command/flag"
912
"code.cloudfoundry.org/cli/command/translatableerror"
1013
"code.cloudfoundry.org/cli/command/v7/shared"
14+
"code.cloudfoundry.org/cli/resources"
1115
"code.cloudfoundry.org/cli/util/configv3"
1216
)
1317

@@ -16,12 +20,13 @@ type CopySourceCommand struct {
1620

1721
RequiredArgs flag.CopySourceArgs `positional-args:"yes"`
1822
usage interface{} `usage:"CF_NAME copy-source SOURCE_APP DESTINATION_APP [-s TARGET_SPACE [-o TARGET_ORG]] [--no-restart] [--strategy STRATEGY] [--no-wait]"`
19-
Strategy flag.DeploymentStrategy `long:"strategy" description:"Deployment strategy can be canary, rolling or null"`
23+
InstanceSteps string `long:"instance-steps" description:"An array of percentage steps to deploy when using deployment strategy canary. (e.g. 20,40,60)"`
2024
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."`
2125
NoWait bool `long:"no-wait" description:"Exit when the first instance of the web process is healthy"`
2226
NoRestart bool `long:"no-restart" description:"Do not restage the destination application"`
2327
Organization string `short:"o" long:"organization" description:"Org that contains the destination application"`
2428
Space string `short:"s" long:"space" description:"Space that contains the destination application"`
29+
Strategy flag.DeploymentStrategy `long:"strategy" description:"Deployment strategy can be canary, rolling or null"`
2530
relatedCommands interface{} `related_commands:"apps, push, restage, restart, target"`
2631
envCFStagingTimeout interface{} `environmentName:"CF_STAGING_TIMEOUT" environmentDescription:"Max wait time for staging, in minutes" environmentDefault:"15"`
2732
envCFStartupTimeout interface{} `environmentName:"CF_STARTUP_TIMEOUT" environmentDescription:"Max wait time for app instance startup, in minutes" environmentDefault:"5"`
@@ -57,6 +62,14 @@ func (cmd *CopySourceCommand) ValidateFlags() error {
5762
return translatableerror.IncorrectUsageError{Message: "--max-in-flight must be greater than or equal to 1"}
5863
}
5964

65+
if cmd.Strategy.Name != constant.DeploymentStrategyCanary && cmd.InstanceSteps != "" {
66+
return translatableerror.RequiredFlagsError{Arg1: "--instance-steps", Arg2: "--strategy=canary"}
67+
}
68+
69+
if len(cmd.InstanceSteps) > 0 && !validateInstanceSteps(cmd.InstanceSteps) {
70+
return translatableerror.ParseArgumentError{ArgumentName: "--instance-steps", ExpectedType: "list of weights"}
71+
}
72+
6073
return nil
6174
}
6275

@@ -178,6 +191,18 @@ func (cmd CopySourceCommand) Execute(args []string) error {
178191
opts.MaxInFlight = *cmd.MaxInFlight
179192
}
180193

194+
if cmd.InstanceSteps != "" {
195+
if len(cmd.InstanceSteps) > 0 {
196+
for _, v := range strings.Split(cmd.InstanceSteps, ",") {
197+
parsedInt, err := strconv.ParseInt(v, 0, 64)
198+
if err != nil {
199+
return err
200+
}
201+
opts.CanarySteps = append(opts.CanarySteps, resources.CanaryStep{InstanceWeight: parsedInt})
202+
}
203+
}
204+
}
205+
181206
err = cmd.Stager.StageAndStart(targetApp, targetSpace, targetOrg, pkg.GUID, opts)
182207
if err != nil {
183208
return mapErr(cmd.Config, targetApp.Name, err)

command/v7/copy_source_command_test.go

+40
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,27 @@ var _ = Describe("copy-source Command", func() {
324324
Expect(opts.NoWait).To(Equal(false))
325325
Expect(opts.AppAction).To(Equal(constant.ApplicationRestarting))
326326
})
327+
328+
When("instance steps is provided", func() {
329+
BeforeEach(func() {
330+
cmd.Strategy = flag.DeploymentStrategy{Name: constant.DeploymentStrategyCanary}
331+
cmd.InstanceSteps = "1,2,4"
332+
})
333+
334+
It("starts the new app", func() {
335+
Expect(executeErr).ToNot(HaveOccurred())
336+
Expect(fakeAppStager.StageAndStartCallCount()).To(Equal(1))
337+
338+
inputApp, inputSpace, inputOrg, inputDropletGuid, opts := fakeAppStager.StageAndStartArgsForCall(0)
339+
Expect(inputApp).To(Equal(targetApp))
340+
Expect(inputDropletGuid).To(Equal("target-package-guid"))
341+
Expect(inputSpace).To(Equal(cmd.Config.TargetedSpace()))
342+
Expect(inputOrg).To(Equal(cmd.Config.TargetedOrganization()))
343+
Expect(opts.Strategy).To(Equal(constant.DeploymentStrategyCanary))
344+
Expect(opts.AppAction).To(Equal(constant.ApplicationRestarting))
345+
Expect(opts.CanarySteps).To(Equal([]resources.CanaryStep{{InstanceWeight: 1}, {InstanceWeight: 2}, {InstanceWeight: 4}}))
346+
})
347+
})
327348
})
328349

329350
When("the no-wait flag is set", func() {
@@ -440,5 +461,24 @@ var _ = Describe("copy-source Command", func() {
440461
translatableerror.IncorrectUsageError{
441462
Message: "--max-in-flight must be greater than or equal to 1",
442463
}),
464+
465+
Entry("instance-steps no strategy provided",
466+
func() {
467+
cmd.InstanceSteps = "1,2,3"
468+
},
469+
translatableerror.RequiredFlagsError{
470+
Arg1: "--instance-steps",
471+
Arg2: "--strategy=canary",
472+
}),
473+
474+
Entry("instance-steps a valid list of ints",
475+
func() {
476+
cmd.Strategy = flag.DeploymentStrategy{Name: constant.DeploymentStrategyCanary}
477+
cmd.InstanceSteps = "some,thing,not,right"
478+
},
479+
translatableerror.ParseArgumentError{
480+
ArgumentName: "--instance-steps",
481+
ExpectedType: "list of weights",
482+
}),
443483
)
444484
})

integration/v7/isolated/copy_source_command_test.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -384,13 +384,14 @@ var _ = Describe("copy-source command", func() {
384384

385385
It("copies the app to the provided space using a canary deploy", func() {
386386
username, _ := helpers.GetCredentials()
387-
session := helpers.CF("copy-source", sourceAppName, targetAppName, "--strategy", "canary")
387+
session := helpers.CF("copy-source", sourceAppName, targetAppName, "--strategy", "canary", "--instance-steps", "20,60")
388388
Eventually(session).Should(Say("Copying source from app %s to target app %s in org %s / space %s as %s...", sourceAppName, targetAppName, orgName, spaceName, username))
389389
Eventually(session).Should(Say("Staging app %s in org %s / space %s as %s...", targetAppName, orgName, spaceName, username))
390390
Eventually(session).Should(Say("Waiting for app to deploy..."))
391391
Eventually(session).Should(Say("Active deployment with status PAUSED"))
392392
Eventually(session).Should(Say("strategy: canary"))
393393
Eventually(session).Should(Say("max-in-flight: 1"))
394+
Eventually(session).Should(Say("canary-steps: 1/2"))
394395
Eventually(session).Should(Say("Please run `cf continue-deployment %s` to promote the canary deployment, or `cf cancel-deployment %s` to rollback to the previous version.", targetAppName, targetAppName))
395396
Eventually(session).Should(Exit(0))
396397

@@ -473,12 +474,13 @@ func helpText(session *Session) {
473474
Eventually(session).Should(Say("USAGE:"))
474475
Eventually(session).Should(Say(`cf copy-source SOURCE_APP DESTINATION_APP \[-s TARGET_SPACE \[-o TARGET_ORG\]\] \[--no-restart\] \[--strategy STRATEGY\] \[--no-wait\]`))
475476
Eventually(session).Should(Say("OPTIONS:"))
476-
Eventually(session).Should(Say(`--strategy\s+Deployment strategy can be canary, rolling or null`))
477+
Eventually(session).Should(Say(`--instance-steps`))
477478
Eventually(session).Should(Say(`--max-in-flight\s+Defines the maximum number of instances`))
478479
Eventually(session).Should(Say(`--no-wait\s+ Exit when the first instance of the web process is healthy`))
479480
Eventually(session).Should(Say(`--no-restart\s+Do not restage the destination application`))
480481
Eventually(session).Should(Say(`--organization, -o\s+Org that contains the destination application`))
481482
Eventually(session).Should(Say(`--space, -s\s+Space that contains the destination application`))
483+
Eventually(session).Should(Say(`--strategy\s+Deployment strategy can be canary, rolling or null`))
482484
Eventually(session).Should(Say("ENVIRONMENT:"))
483485
Eventually(session).Should(Say(`CF_STAGING_TIMEOUT=15\s+Max wait time for staging, in minutes`))
484486
Eventually(session).Should(Say(`CF_STARTUP_TIMEOUT=5\s+Max wait time for app instance startup, in minutes`))

0 commit comments

Comments
 (0)