Skip to content

Commit

Permalink
feat(cli): --hotswap will not use CFN anymore, --hotswap-fallback to …
Browse files Browse the repository at this point in the history
…fall back if necessary (#23653)

Changes the behavior of `--hotswap` to ignore all non-hotswappable changes and hotswap what it can. This works at two levels: changes to non-hotswappable resources are ignored, as well as non-hotswappable changes to hotswappable resources (eg `Tags` on a Lambda Function).

In addition, non-hotswappable changes are now logged; the logical ID, rejected changes, resource type, and reason why the changes were rejected are all provided for each non-hotswappable change.

At some point, support for tags of lambda functions was added. This either broke or simply never worked, and so this PR removes all logic to handle Tags.

The existing behavior of `--hotswap` can be used in `--hotswap-fallback`. It is preserved and unmodified by this change.

Closes #22784, #21773, #21556, #23640.

----

### All Submissions:

* [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md)

### Adding new Construct Runtime Dependencies:

* [ ] This PR adds new construct runtime dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-construct-runtime-dependencies)

### New Features

* [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)?
	* [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)?

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
comcalvi committed Feb 8, 2023
1 parent 08a2f36 commit a5317ca
Show file tree
Hide file tree
Showing 28 changed files with 5,935 additions and 4,844 deletions.
15 changes: 10 additions & 5 deletions packages/aws-cdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -375,15 +375,20 @@ $ cdk deploy --hotswap [StackNames]
```

This will attempt to perform a faster, short-circuit deployment if possible
(for example, if you only changed the code of a Lambda function in your CDK app,
but nothing else in your CDK code),
(for example, if you changed the code of a Lambda function in your CDK app),
skipping CloudFormation, and updating the affected resources directly;
this includes changes to resources in nested stacks.
If the tool detects that the change does not support hotswapping,
it will fall back and perform a full CloudFormation deployment,
exactly like `cdk deploy` does without the `--hotswap` flag.
it will ignore it and display that ignored change.
To have hotswap fall back and perform a full CloudFormation deployment,
exactly like `cdk deploy` does without the `--hotswap` flag,
specify `--hotswap-fallback`, like so:

Passing this option to `cdk deploy` will make it use your current AWS credentials to perform the API calls -
```console
$ cdk deploy --hotswap-fallback [StackNames]
```

Passing either option to `cdk deploy` will make it use your current AWS credentials to perform the API calls -
it will not assume the Roles from your bootstrap stack,
even if the `@aws-cdk/core:newStyleStackSynthesis` feature flag is set to `true`
(as those Roles do not have the necessary permissions to update AWS resources directly, without using CloudFormation).
Expand Down
5 changes: 3 additions & 2 deletions packages/aws-cdk/lib/api/cloudformation-deployments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Mode } from './aws-auth/credentials';
import { ISDK } from './aws-auth/sdk';
import { SdkProvider } from './aws-auth/sdk-provider';
import { deployStack, DeployStackResult, destroyStack, makeBodyParameterAndUpload, DeploymentMethod } from './deploy-stack';
import { HotswapMode } from './hotswap/common';
import { loadCurrentTemplateWithNestedStacks, loadCurrentTemplate } from './nested-stack-helpers';
import { ToolkitInfo } from './toolkit-info';
import { CloudFormationStack, Template, ResourcesToImport, ResourceIdentifierSummaries } from './util/cloudformation';
Expand Down Expand Up @@ -224,9 +225,9 @@ export interface DeployStackOptions {
* A 'hotswap' deployment will attempt to short-circuit CloudFormation
* and update the affected resources like Lambda functions directly.
*
* @default - false for regular deployments, true for 'watch' deployments
* @default - `HotswapMode.FULL_DEPLOYMENT` for regular deployments, `HotswapMode.HOTSWAP_ONLY` for 'watch' deployments
*/
readonly hotswap?: boolean;
readonly hotswap?: HotswapMode;

/**
* The extra string to append to the User-Agent header when performing AWS SDK calls.
Expand Down
23 changes: 15 additions & 8 deletions packages/aws-cdk/lib/api/deploy-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { contentHash } from '../util/content-hash';
import { ISDK, SdkProvider } from './aws-auth';
import { CfnEvaluationException } from './evaluate-cloudformation-template';
import { tryHotswapDeployment } from './hotswap-deployments';
import { ICON } from './hotswap/common';
import { HotswapMode, ICON } from './hotswap/common';
import { ToolkitInfo } from './toolkit-info';
import {
changeSetHasNoChanges, CloudFormationStack, TemplateParameters, waitForChangeSet,
Expand Down Expand Up @@ -175,9 +175,9 @@ export interface DeployStackOptions {
* A 'hotswap' deployment will attempt to short-circuit CloudFormation
* and update the affected resources like Lambda functions directly.
*
* @default - false for regular deployments, true for 'watch' deployments
* @default - `HotswapMode.FULL_DEPLOYMENT` for regular deployments, `HotswapMode.HOTSWAP_ONLY` for 'watch' deployments
*/
readonly hotswap?: boolean;
readonly hotswap?: HotswapMode;

/**
* The extra string to append to the User-Agent header when performing AWS SDK calls.
Expand Down Expand Up @@ -298,10 +298,13 @@ export async function deployStack(options: DeployStackOptions): Promise<DeploySt
parallel: options.assetParallelism,
});

if (options.hotswap) {
const hotswapMode = options.hotswap;
if (hotswapMode && hotswapMode !== HotswapMode.FULL_DEPLOYMENT) {
// attempt to short-circuit the deployment if possible
try {
const hotswapDeploymentResult = await tryHotswapDeployment(options.sdkProvider, assetParams, cloudFormationStack, stackArtifact);
const hotswapDeploymentResult = await tryHotswapDeployment(
options.sdkProvider, stackParams.values, cloudFormationStack, stackArtifact, hotswapMode,
);
if (hotswapDeploymentResult) {
return hotswapDeploymentResult;
}
Expand All @@ -312,16 +315,20 @@ export async function deployStack(options: DeployStackOptions): Promise<DeploySt
}
print('Could not perform a hotswap deployment, because the CloudFormation template could not be resolved: %s', e.message);
}
print('Falling back to doing a full deployment');
options.sdk.appendCustomUserAgent('cdk-hotswap/fallback');

if (hotswapMode === HotswapMode.FALL_BACK) {
print('Falling back to doing a full deployment');
options.sdk.appendCustomUserAgent('cdk-hotswap/fallback');
} else {
return { noOp: true, stackArn: cloudFormationStack.stackId, outputs: cloudFormationStack.outputs };
}
}

// could not short-circuit the deployment, perform a full CFN deploy instead
const fullDeployment = new FullCloudFormationDeployment(options, cloudFormationStack, stackArtifact, stackParams, bodyParameter);
return fullDeployment.performDeployment();
}


type CommonPrepareOptions =
& keyof CloudFormation.CreateStackInput
& keyof CloudFormation.UpdateStackInput
Expand Down
Loading

0 comments on commit a5317ca

Please sign in to comment.