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): automatically roll back stacks if necessary #31920

Merged
merged 14 commits into from
Nov 5, 2024

Conversation

rix0rrr
Copy link
Contributor

@rix0rrr rix0rrr commented Oct 28, 2024

If a user is deploying with --no-rollback, and the stack contains replacements (or the --no-rollback flag is dropped), then a rollback needs to be performed before a regular deployment can happen again.

In this PR, we add a prompt where we ask the user to confirm that they are okay with performing a rollback and then a normal deployment.

The way this works is that deployStack detects a disallowed combination (replacement and no-rollback, or being in a stuck state and not being called with no-rollback), and returns a special status code. The driver of the calls, CdkToolkit, will see those special return codes, prompt the user, and retry.

Also get rid of a stray Stack undefined that gets printed to the console.

Closes #30546, Closes #31685


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license

If a user is deploying with `--no-rollback`, and the stack contains
replacements (or the `--no-rollback` flag is dropped), then a rollback
needs to be performed before a regular deployment can happen again.

In this PR, we add a prompt where we ask the user to confirm that
they are okay with performing a rollback and then a normal deployment.

Closes #30546.
@rix0rrr rix0rrr requested a review from a team October 28, 2024 16:26
@rix0rrr
Copy link
Contributor Author

rix0rrr commented Oct 28, 2024

Still needs tests

@aws-cdk-automation aws-cdk-automation requested a review from a team October 28, 2024 16:26
@github-actions github-actions bot added effort/medium Medium work item – several days of effort feature-request A feature should be added or improved. p1 labels Oct 28, 2024
@mergify mergify bot added the contribution/core This is a PR that came from AWS. label Oct 28, 2024
Copy link
Collaborator

@aws-cdk-automation aws-cdk-automation left a comment

Choose a reason for hiding this comment

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

The pull request linter has failed. See the aws-cdk-automation comment below for failure reasons. If you believe this pull request should receive an exemption, please comment and provide a justification.

A comment requesting an exemption should contain the text Exemption Request. Additionally, if clarification is needed add Clarification Request to a comment.

@aws-cdk-automation aws-cdk-automation added the pr/needs-cli-test-run This PR needs CLI tests run against it. label Oct 28, 2024
@rix0rrr rix0rrr added pr-linter/exempt-readme The PR linter will not require README changes pr-linter/exempt-integ-test The PR linter will not require integ test changes labels Oct 29, 2024
@mrgrain
Copy link
Contributor

mrgrain commented Oct 29, 2024

If a user is deploying with --no-rollback, and the stack contains replacements (or the --no-rollback flag is dropped),

Is this trying to say that the case applies either when:

  • the --no-rollback flag is NOT used (i.e. --rollback), or
  • --no-rollback, and the stack contains replacements

?

;

export interface RegularDeployStackResult {
readonly type: 'did-deploy-stack';
Copy link
Contributor

@mrgrain mrgrain Oct 29, 2024

Choose a reason for hiding this comment

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

The type system is doing some heavy lifting here. Would a constant prevent having to types this so many times?

Copy link
Contributor Author

@rix0rrr rix0rrr Oct 29, 2024

Choose a reason for hiding this comment

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

The type system is doing some heavy lifting here

Yep! Nice, isn't it?

Would a constant prevent having to types this so many times?

It really doesn't make a difference. This is a string-as-a-type, and therefore just as safe to use as the hypothetical constant const DID_DEPLOY_STACK: 'did-deploy-stack' = 'did-deploy-stack';. The only difference would be 2 keystrokes.

Copy link
Contributor

Choose a reason for hiding this comment

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

Cool! Does it auto-complete as well?

I'm all for avoiding enums to be honest.

const confirmed = await promptly.confirm(`${chalk.cyan(question)} (y/n)?`);
if (!confirmed) { throw new Error('Aborted by user'); }
});
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
}
}

@@ -153,6 +153,10 @@ export class ResourceImporter {
resourcesToImport,
});

if (result.type !== 'did-deploy-stack') {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is where const/enum will shine. If there is a typo on the RHS, the check will do unexpected things.

Copy link
Contributor Author

@rix0rrr rix0rrr Oct 29, 2024

Choose a reason for hiding this comment

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

It's already typed. If there's a typo in the RHS, it will not compile.

image

// Do a deployment with a replacement and --force: this will roll back first and then deploy normally
phase = '3';
await fixture.cdkDeploy('test-rollback', {
options: ['--no-rollback', '--force'],
Copy link
Contributor

Choose a reason for hiding this comment

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

That's a weird UX....

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What is weird UX?

Copy link
Contributor

Choose a reason for hiding this comment

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

--no-rollback --force is a weird DX. Why not just --rollback ?

Copy link
Contributor Author

@rix0rrr rix0rrr Nov 1, 2024

Choose a reason for hiding this comment

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

That will work too. What we are testing is the case where a developer does up + Enter.

The --force is there to skip the interactive prompt, which we have a unit test for but not an integration test.

stackName?: string,
changeSet?: DescribeChangeSetOutput,
stream: FormatStream = process.stderr,
): boolean {
const diff = fullDiff(oldTemplate, newTemplate.template, changeSet);

// must output the stack name if there are differences, even if quiet
if (!quiet || !diff.isEmpty) {
if (diffRequiresApproval(diff, requireApproval)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Are you sure this isn't changing the behavior of quiet? We are bound to get another issue for that....

Copy link
Contributor Author

@rix0rrr rix0rrr Nov 1, 2024

Choose a reason for hiding this comment

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

What this was doing is always printing Stack undefined when quiet was not set, regardless of whether there was a diff to print. Followed up by the diff if the diff requires approval.

Right now, we only print the stack name if the diff requires approval, period, so it functions as a header.

I suppose the difference would be this:

quiet: false quiet: true Change
Before (no diff) stackName (nothing)
After (no diff) (nothing) (nothing) Now doesn't unnecesessarily print stackName anymore
Before (diff) stackName, diff stackName, diff
After (diff) stackName, diff stackName, diff No change, but confusingly written across 2 statements

The new behavior seems more sensible, no?

Copy link
Contributor

Choose a reason for hiding this comment

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

Closes #31685

Seems to be what #30186 tried to do but failed-ish.

Copy link
Contributor Author

@rix0rrr rix0rrr Nov 1, 2024

Choose a reason for hiding this comment

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

Proof that the refactoring is safe:

open util/boolean 

enum Change { PermBroadened, PermNarrowed }
enum ConfirmationSetting { Never, Broadened, Any }
enum Visible { StackName, Diff }

one sig Operation {
  quiet: Bool,
  changes: set Change,
  confirmationSetting: ConfirmationSetting,
  old: set Visible,
  new: set Visible,
}

pred diffRequiresApproval {
  Operation.confirmationSetting != Never
  (Operation.confirmationSetting = Broadened and PermBroadened in Operation.changes) or (Operation.confirmationSetting = Any and some Operation.changes)
}

pred isEmpty[o: Operation] { no o.changes }

fact {
  -- Old
  (Operation.quiet = False or not Operation.isEmpty) <=> StackName in Operation.old
  diffRequiresApproval <=> Diff in Operation.old

  -- New
  diffRequiresApproval <=> (StackName + Diff) = Operation.new
  not diffRequiresApproval <=> no Operation.new
}

check { 
 -- If the new code shows something, the old code also used to show it
 StackName in Operation.new => StackName in Operation.old
 Diff in Operation.new => Diff in Operation.old

 -- This is a restatement of what's above but just to be safe: 
  (Operation.confirmationSetting = Any and some Operation.changes) => Diff in Operation.new
  (Operation.confirmationSetting = Broadened and PermBroadened in Operation.changes) => Diff in Operation.new
}

Copy link
Contributor

@mrgrain mrgrain left a comment

Choose a reason for hiding this comment

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

Conditinally approved that you triple check you didn't accidentally change the behavior of cdk diff --quiet

https://github.com/aws/aws-cdk/pull/31920/files#r1825686382

@mrgrain mrgrain added the pr/do-not-merge This PR should not be merged at this time. label Nov 1, 2024
@github-actions github-actions bot added bug This issue is a bug. and removed feature-request A feature should be added or improved. labels Nov 1, 2024
@rix0rrr rix0rrr removed the pr/do-not-merge This PR should not be merged at this time. label Nov 1, 2024
@aws-cdk-automation
Copy link
Collaborator

➡️ PR build request submitted to test-main-pipeline ⬅️

A maintainer must now check the pipeline and add the pr-linter/cli-integ-tested label once the pipeline succeeds.

@rix0rrr rix0rrr added the pr-linter/cli-integ-tested Assert that any CLI changes have been integ tested label Nov 5, 2024
@aws-cdk-automation aws-cdk-automation dismissed their stale review November 5, 2024 14:52

✅ Updated pull request passes all PRLinter validations. Dismissing previous PRLinter review.

@aws-cdk-automation aws-cdk-automation removed the pr/needs-cli-test-run This PR needs CLI tests run against it. label Nov 5, 2024
Copy link
Contributor

mergify bot commented Nov 5, 2024

Thank you for contributing! Your pull request will be updated from main and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

@aws-cdk-automation
Copy link
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: AutoBuildv2Project1C6BFA3F-wQm2hXv2jqQv
  • Commit ID: 51b1812
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@mergify mergify bot merged commit 2f9fb1e into main Nov 5, 2024
11 of 12 checks passed
Copy link
Contributor

mergify bot commented Nov 5, 2024

Thank you for contributing! Your pull request will be updated from main and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

@mergify mergify bot deleted the huijbers/auto-rollback branch November 5, 2024 15:25
Copy link

github-actions bot commented Nov 5, 2024

Comments on closed issues and PRs are hard for our team to see.
If you need help, please open a new issue that references this one.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Nov 5, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug This issue is a bug. contribution/core This is a PR that came from AWS. effort/medium Medium work item – several days of effort p1 pr-linter/cli-integ-tested Assert that any CLI changes have been integ tested pr-linter/exempt-integ-test The PR linter will not require integ test changes pr-linter/exempt-readme The PR linter will not require README changes
Projects
None yet
3 participants