Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cli): support --no-rollback flag #16293

Merged
merged 3 commits into from
Aug 31, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion packages/aws-cdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ $ cdk diff --app='node bin/main.js' MyStackName --template=path/to/template.yml

### `cdk deploy`

Deploys a stack of your CDK app to it's environment. During the deployment, the toolkit will output progress
Deploys a stack of your CDK app to its environment. During the deployment, the toolkit will output progress
indications, similar to what can be observed in the AWS CloudFormation Console. If the environment was never
bootstrapped (using `cdk bootstrap`), only stacks that are not using assets and synthesize to a template that is under
51,200 bytes will successfully deploy.
Expand All @@ -154,6 +154,24 @@ currently deployed stack to the template and tags that are about to be deployed
will skip deployment if they are identical. Use `--force` to override this behavior
and always deploy the stack.

#### Disabling Rollback

If a resource fails to be created or updated, the deployment will *roll back* before the CLI returns. All changes made
up to that point will be undone (resources that were created will be deleted, updates that were made will be changed
back) in order to leave the stack in a consistent state at the end of the operation. If you are using the CDK CLI
to iterate on a development stack in your personal account, you might not require CloudFormation to leave your
stack in a consistent state, but instead would prefer to update your CDK application and try again.

To disable the rollback feature, specify `--no-rollback` (`-R` for short):

```console
$ cdk deploy --no-rollback
Copy link
Contributor

@eladb eladb Aug 31, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel this deserves a short alias as well. Maybe -R (stands for "rollback" or "retry")

$ cdk deploy -R
```

NOTE: you cannot use `--no-rollback` for any updates that would cause a resource replacement, only for updates
and creations of new resources.

#### Deploying multiple stacks

You can have multiple stacks in a cdk app. An example can be found in [how to create multiple stacks](https://docs.aws.amazon.com/cdk/latest/guide/stack_how_to_create_multiple_stacks.html).
Expand Down
16 changes: 15 additions & 1 deletion packages/aws-cdk/bin/cdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,11 @@ async function parseCommandLineArguments() {
.option('parameters', { type: 'array', desc: 'Additional parameters passed to CloudFormation at deploy time (STACK:KEY=VALUE)', nargs: 1, requiresArg: true, default: {} })
.option('outputs-file', { type: 'string', alias: 'O', desc: 'Path to file where stack outputs will be written as JSON', requiresArg: true })
.option('previous-parameters', { type: 'boolean', default: true, desc: 'Use previous values for existing parameters (you must specify all parameters on every deployment if this is disabled)' })
.option('progress', { type: 'string', choices: [StackActivityProgress.BAR, StackActivityProgress.EVENTS], desc: 'Display mode for stack activity events' }),
.option('progress', { type: 'string', choices: [StackActivityProgress.BAR, StackActivityProgress.EVENTS], desc: 'Display mode for stack activity events' })
.option('rollback', { type: 'boolean', default: true, desc: 'Rollback stack to stable state on failure (iterate more rapidly with --no-rollback or -R)' })
// Hack to get '-R' as an alias for '--no-rollback', suggested by: https://github.com/yargs/yargs/issues/1729
.option('R', { type: 'boolean', hidden: true })
.middleware(yargsNegativeAlias('R', 'rollback'), true),
)
.command('destroy [STACKS..]', 'Destroy the stack(s) named STACKS', yargs => yargs
.option('all', { type: 'boolean', default: false, desc: 'Destroy all available stacks' })
Expand Down Expand Up @@ -319,6 +323,7 @@ async function initCommandLine() {
outputsFile: configuration.settings.get(['outputsFile']),
progress: configuration.settings.get(['progress']),
ci: args.ci,
rollback: configuration.settings.get(['rollback']),
});

case 'destroy':
Expand Down Expand Up @@ -421,6 +426,15 @@ function arrayFromYargs(xs: string[]): string[] | undefined {
return xs.filter(x => x !== '');
}

function yargsNegativeAlias<T extends { [x in S | L ]: boolean | undefined }, S extends string, L extends string>(shortName: S, longName: L) {
return (argv: T) => {
if (shortName in argv && argv[shortName]) {
(argv as any)[longName] = false;
}
return argv;
};
}

initCommandLine()
.then(value => {
if (value == null) { return; }
Expand Down
8 changes: 8 additions & 0 deletions packages/aws-cdk/lib/api/cloudformation-deployments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,13 @@ export interface DeployStackOptions {
* @default false
*/
readonly ci?: boolean;

/**
* Rollback failed deployments
*
* @default true
*/
readonly rollback?: boolean;
}

export interface DestroyStackOptions {
Expand Down Expand Up @@ -204,6 +211,7 @@ export class CloudFormationDeployments {
usePreviousParameters: options.usePreviousParameters,
progress: options.progress,
ci: options.ci,
rollback: options.rollback,
});
}

Expand Down
14 changes: 13 additions & 1 deletion packages/aws-cdk/lib/api/deploy-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,13 @@ export interface DeployStackOptions {
* @default false
*/
readonly ci?: boolean;

/**
* Rollback failed deployments
*
* @default true
*/
readonly rollback?: boolean;
}

const LARGE_TEMPLATE_SIZE_KB = 50;
Expand Down Expand Up @@ -282,7 +289,12 @@ export async function deployStack(options: DeployStackOptions): Promise<DeploySt
const execute = options.execute === undefined ? true : options.execute;
if (execute) {
debug('Initiating execution of changeset %s on stack %s', changeSet.Id, deployName);
await cfn.executeChangeSet({ StackName: deployName, ChangeSetName: changeSetName }).promise();

// Do a bit of contortions to only pass the `DisableRollback` flag if it's true. That way,
// CloudFormation won't balk at the unrecognized option in regions where the feature is not available yet.
const disableRollback = options.rollback === false ? { DisableRollback: true } : undefined;
await cfn.executeChangeSet({ StackName: deployName, ChangeSetName: changeSetName, ...disableRollback }).promise();

// eslint-disable-next-line max-len
const monitor = options.quiet ? undefined : StackActivityMonitor.withDefaultPrinter(cfn, deployName, stackArtifact, {
resourcesTotal: (changeSetDescription.Changes ?? []).length,
Expand Down
8 changes: 8 additions & 0 deletions packages/aws-cdk/lib/cdk-toolkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ export class CdkToolkit {
usePreviousParameters: options.usePreviousParameters,
progress: options.progress,
ci: options.ci,
rollback: options.rollback,
});

const message = result.noOp
Expand Down Expand Up @@ -625,6 +626,13 @@ export interface DeployOptions {
* @default false
*/
readonly ci?: boolean;

/**
* Rollback failed deployments
*
* @default true
*/
readonly rollback?: boolean;
}

export interface DestroyOptions {
Expand Down
9 changes: 9 additions & 0 deletions packages/aws-cdk/lib/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,14 @@ export class Settings {

/**
* Parse Settings out of CLI arguments.
*
* CLI arguments in must be accessed in the CLI code via
* `configuration.settings.get(['argName'])` instead of via `args.argName`.
*
* The advantage is that they can be configured via `cdk.json` and
* `$HOME/.cdk.json`. Arguments not listed below and accessed via this object
* can only be specified on the command line.
*
* @param argv the received CLI arguments.
* @returns a new Settings object.
*/
Expand Down Expand Up @@ -272,6 +280,7 @@ export class Settings {
progress: argv.progress,
bundlingStacks,
lookups: argv.lookups,
rollback: argv.rollback,
});
}

Expand Down
2 changes: 1 addition & 1 deletion packages/aws-cdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
"@aws-cdk/region-info": "0.0.0",
"@jsii/check-node": "1.33.0",
"archiver": "^5.3.0",
"aws-sdk": "^2.848.0",
"aws-sdk": "^2.979.0",
"camelcase": "^6.2.0",
"cdk-assets": "0.0.0",
"colors": "^1.4.0",
Expand Down
29 changes: 29 additions & 0 deletions packages/aws-cdk/test/api/deploy-stack.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,35 @@ test('updateTerminationProtection called when termination protection is undefine
}));
});

describe('disable rollback', () => {
test('by default, we do not disable rollback (and also do not pass the flag)', async () => {
// WHEN
await deployStack({
...standardDeployStackArguments(),
});

// THEN
expect(cfnMocks.executeChangeSet).toHaveBeenCalledTimes(1);
expect(cfnMocks.executeChangeSet).not.toHaveBeenCalledWith(expect.objectContaining({
DisableRollback: expect.anything(),
}));
});

test('rollback can be disabled by setting rollback: false', async () => {
// WHEN
await deployStack({
...standardDeployStackArguments(),
rollback: false,
});

// THEN
expect(cfnMocks.executeChangeSet).toHaveBeenCalledWith(expect.objectContaining({
DisableRollback: true,
}));
});

});

/**
* Set up the mocks so that it looks like the stack exists to start with
*
Expand Down
15 changes: 15 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2319,6 +2319,21 @@ aws-sdk@^2.848.0, aws-sdk@^2.928.0:
uuid "3.3.2"
xml2js "0.4.19"

aws-sdk@^2.979.0:
version "2.979.0"
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.979.0.tgz#d0104fec763cc3eafb123e709f94866790109da4"
integrity sha512-pKKhpYZwmihCvuH3757WHY8JQI9g2wvtF3s0aiyH2xCUmX/6uekhExz/utD4uqZP3m3PwKZPGQkQkH30DtHrPw==
dependencies:
buffer "4.9.2"
events "1.1.1"
ieee754 "1.1.13"
jmespath "0.15.0"
querystring "0.2.0"
sax "1.2.1"
url "0.10.3"
uuid "3.3.2"
xml2js "0.4.19"

aws-sign2@~0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
Expand Down