From 8fa6bdd86ca673649d2498304311f288fad524c0 Mon Sep 17 00:00:00 2001 From: Praveen Gupta Date: Tue, 21 Nov 2023 02:57:44 +0100 Subject: [PATCH 1/5] fix(cli): hotswap cannot evaluate nested stacks within nested stacks --- .../api/evaluate-cloudformation-template.ts | 15 ++++- .../api/hotswap/nested-stacks-hotswap.test.ts | 55 ++++++++++++++++--- ...with-two-nested-stacks-stack.template.json | 38 +++++++++++++ 3 files changed, 98 insertions(+), 10 deletions(-) create mode 100644 packages/aws-cdk/test/nested-stack-templates/one-stack-with-two-nested-stacks-stack.template.json diff --git a/packages/aws-cdk/lib/api/evaluate-cloudformation-template.ts b/packages/aws-cdk/lib/api/evaluate-cloudformation-template.ts index 258b64829e62a..329bf2ec9ba59 100644 --- a/packages/aws-cdk/lib/api/evaluate-cloudformation-template.ts +++ b/packages/aws-cdk/lib/api/evaluate-cloudformation-template.ts @@ -387,7 +387,7 @@ export class EvaluateCloudFormationTemplate { if (foundResource.ResourceType == 'AWS::CloudFormation::Stack' && attribute?.startsWith('Outputs.')) { // need to resolve attributes from another stack's Output section - const dependantStackName = this.nestedStackNames[logicalId]?.nestedStackPhysicalName; + const dependantStackName = this.findNestedStack(logicalId, this.nestedStackNames); if (!dependantStackName) { //this is a newly created nested stack and cannot be hotswapped return undefined; @@ -406,6 +406,19 @@ export class EvaluateCloudFormationTemplate { return this.formatResourceAttribute(foundResource, attribute); } + private findNestedStack(logicalId: string, nestedStackNames: { + [nestedStackLogicalId: string]: NestedStackNames; + }): string | undefined { + for (const [nestedStackLogicalId, { nestedChildStackNames, nestedStackPhysicalName }] of Object.entries(nestedStackNames)) { + if (nestedStackLogicalId === logicalId) { + return nestedStackPhysicalName; + } + const checkInNestedChildStacks = this.findNestedStack(logicalId, nestedChildStackNames); + if (checkInNestedChildStacks) return checkInNestedChildStacks; + } + return undefined; + } + private formatResourceAttribute(resource: AWS.CloudFormation.StackResourceSummary, attribute: string | undefined): string | undefined { const physicalId = resource.PhysicalResourceId; diff --git a/packages/aws-cdk/test/api/hotswap/nested-stacks-hotswap.test.ts b/packages/aws-cdk/test/api/hotswap/nested-stacks-hotswap.test.ts index 77d159074c8da..cb499f24bd3b7 100644 --- a/packages/aws-cdk/test/api/hotswap/nested-stacks-hotswap.test.ts +++ b/packages/aws-cdk/test/api/hotswap/nested-stacks-hotswap.test.ts @@ -826,10 +826,12 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot }); }); - test('can hotswap a lambda function in a 1-level nested stack with dependency on a output of sibling stack', async () => { - // GIVEN: RootStack has two child stacks `NestedLambdaStack` and `NestedSiblingStack`. `NestedLambdaStack` - // takes two parameters s3Key and s3Bucket and use them for a Lambda function. - // RootStack resolves s3Bucket from a root template parameter and s3Key through output of `NestedSiblingStack` + test('can hotswap a lambda function in a 2-level nested stack with dependency on a output of 2nd level sibling stack', async () => { + // GIVEN: RootStack has one child stack `FirstLevelRootStack` which further has two child stacks + // `NestedLambdaStack` and `NestedSiblingStack`. `NestedLambdaStack` takes two parameters s3Key + // and s3Bucket and use them for a Lambda function. + // RootStack resolves s3Bucket from a root template parameter and passed to FirstLevelRootStack which + // resolves s3Key through output of `NestedSiblingStack` hotswapMockSdkProvider = setup.setupHotswapNestedStackTests('RootStack'); mockUpdateLambdaCode = jest.fn().mockReturnValue({}); hotswapMockSdkProvider.stubLambda({ @@ -838,6 +840,34 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot const rootStack = testStack({ stackName: 'RootStack', + template: { + Resources: { + FirstLevelRootStack: { + Type: 'AWS::CloudFormation::Stack', + Properties: { + TemplateURL: 'https://www.magic-url.com', + Parameters: { + S3BucketParam: { + Ref: 'S3BucketParam', + }, + }, + }, + Metadata: { + 'aws:asset:path': 'one-stack-with-two-nested-stacks-stack.template.json', + }, + }, + }, + Parameters: { + S3BucketParam: { + Type: 'String', + Description: 'S3 bucket for asset', + }, + }, + }, + }); + + const firstLevelRootStack = testStack({ + stackName: 'FirstLevelRootStack', template: { Resources: { NestedLambdaStack: { @@ -869,11 +899,11 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot 'aws:asset:path': 'one-output-stack.nested.template.json', }, }, - Parameters: { - S3BucketParam: { - Type: 'String', - Description: 'S3 bucket for asset', - }, + }, + Parameters: { + S3BucketParam: { + Type: 'String', + Description: 'S3 bucket for asset', }, }, }, @@ -913,10 +943,17 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot }); setup.addTemplateToCloudFormationLookupMock(rootStack); + setup.addTemplateToCloudFormationLookupMock(firstLevelRootStack); setup.addTemplateToCloudFormationLookupMock(nestedLambdaStack); setup.addTemplateToCloudFormationLookupMock(nestedSiblingStack); setup.pushNestedStackResourceSummaries('RootStack', + setup.stackSummaryOf('FirstLevelRootStack', 'AWS::CloudFormation::Stack', + 'arn:aws:cloudformation:bermuda-triangle-1337:123456789012:stack/FirstLevelRootStack/abcd', + ), + ); + + setup.pushNestedStackResourceSummaries('FirstLevelRootStack', setup.stackSummaryOf('NestedLambdaStack', 'AWS::CloudFormation::Stack', 'arn:aws:cloudformation:bermuda-triangle-1337:123456789012:stack/NestedLambdaStack/abcd', ), diff --git a/packages/aws-cdk/test/nested-stack-templates/one-stack-with-two-nested-stacks-stack.template.json b/packages/aws-cdk/test/nested-stack-templates/one-stack-with-two-nested-stacks-stack.template.json new file mode 100644 index 0000000000000..bdf9ed5cb2c51 --- /dev/null +++ b/packages/aws-cdk/test/nested-stack-templates/one-stack-with-two-nested-stacks-stack.template.json @@ -0,0 +1,38 @@ +{ + "Resources": { + "NestedLambdaStack": { + "Type": "AWS::CloudFormation::Stack", + "Properties": { + "TemplateURL": "https://www.magic-url.com", + "Parameters": { + "referenceToS3BucketParam": { + "Ref": "S3BucketParam" + }, + "referenceToS3StackKeyOutput": { + "Fn::GetAtt": [ + "NestedSiblingStack", + "Outputs.NestedOutput" + ] + } + } + }, + "Metadata": { + "aws:asset:path": "one-lambda-stack-with-dependency-on-sibling-stack-output.nested.template.json" + } + }, + "NestedSiblingStack": { + "Type": "AWS::CloudFormation::Stack", + "Properties": { + "TemplateURL": "https://www.magic-url.com" + }, + "Metadata": { + "aws:asset:path": "one-output-stack.nested.template.json" + } + } + }, + "Parameters": { + "S3BucketParam": { + "Type": "String" + } + } +} From 9202974bcaa083be7f518cb26f15732d1428950a Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Tue, 21 Nov 2023 16:59:49 +0100 Subject: [PATCH 2/5] chore(release): 2.110.1 --- CHANGELOG.v2.alpha.md | 2 ++ CHANGELOG.v2.md | 7 +++++++ version.v2.json | 4 ++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.v2.alpha.md b/CHANGELOG.v2.alpha.md index 2c3829e35f9d7..d032c563b43f9 100644 --- a/CHANGELOG.v2.alpha.md +++ b/CHANGELOG.v2.alpha.md @@ -2,6 +2,8 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [2.110.1-alpha.0](https://github.com/aws/aws-cdk/compare/v2.110.0-alpha.0...v2.110.1-alpha.0) (2023-11-21) + ## [2.110.0-alpha.0](https://github.com/aws/aws-cdk/compare/v2.109.0-alpha.0...v2.110.0-alpha.0) (2023-11-16) ## [2.109.0-alpha.0](https://github.com/aws/aws-cdk/compare/v2.108.1-alpha.0...v2.109.0-alpha.0) (2023-11-15) diff --git a/CHANGELOG.v2.md b/CHANGELOG.v2.md index 134e1f1c262e6..9fbf10a22b7f8 100644 --- a/CHANGELOG.v2.md +++ b/CHANGELOG.v2.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [2.110.1](https://github.com/aws/aws-cdk/compare/v2.110.0...v2.110.1) (2023-11-21) + + +### Bug Fixes + +* **cli:** hotswap cannot evaluate nested stacks within nested stacks ([8fa6bdd](https://github.com/aws/aws-cdk/commit/8fa6bdd86ca673649d2498304311f288fad524c0)) + ## [2.110.0](https://github.com/aws/aws-cdk/compare/v2.109.0...v2.110.0) (2023-11-16) diff --git a/version.v2.json b/version.v2.json index ba3a9e7b2999a..65d49552f872d 100644 --- a/version.v2.json +++ b/version.v2.json @@ -1,4 +1,4 @@ { - "version": "2.110.0", - "alphaVersion": "2.110.0-alpha.0" + "version": "2.110.1", + "alphaVersion": "2.110.1-alpha.0" } \ No newline at end of file From 1cc484bf7de0dc2122e876bbf2d19c8939eb5f92 Mon Sep 17 00:00:00 2001 From: Momo Kornher Date: Mon, 20 Nov 2023 14:55:31 +0100 Subject: [PATCH 3/5] chore: build failing with unsafe-perm is not a valid npm option (#28069) We switched the images used to run our main build to jsii/superchain:bullseye-slim-node18 which comes with Node.js 18 instead of Node.js 16. This Node.js upgrade also includes an upgraded version of npm, and in recent npm versions, the unsef-perm option is not required anymore. This change simply removes the now redundant command. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- buildspec.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/buildspec.yaml b/buildspec.yaml index 9efaf655e95c1..b54c07f3b99c1 100644 --- a/buildspec.yaml +++ b/buildspec.yaml @@ -10,9 +10,6 @@ phases: # this also takes care of launching the docker daemon. - /root/ecr-proxy/start.sh - # CodeBuild always runs as root, allow npm to operate as such - - npm config set unsafe-perm true - # Install yarn if it wasn't already present in the image - yarn --version || npm -g install yarn From 8f5e3190c6b9e6fb2c28589f5ff12ee14570f182 Mon Sep 17 00:00:00 2001 From: Momo Kornher Date: Mon, 20 Nov 2023 13:57:09 +0100 Subject: [PATCH 4/5] chore: pr-build failing with unsafe-perm is not a valid npm option (#28068) We switched the images used to build PRs to `jsii/superchain:bullseye-slim-node18` which comes with Node.js 18 instead of Node.js 16. This Node.js upgrade also includes an upgraded version of npm, and in recent npm versions, the unsef-perm option is not required anymore. This change simply removes the now redundant command. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- buildspec-pr.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/buildspec-pr.yaml b/buildspec-pr.yaml index 1168f54b338ae..852070e934e86 100644 --- a/buildspec-pr.yaml +++ b/buildspec-pr.yaml @@ -14,9 +14,6 @@ phases: # this also takes care of launching the docker daemon. - /root/ecr-proxy/start.sh - # CodeBuild always runs as root, allow npm to operate as such - - npm config set unsafe-perm true - # Install yarn if it wasn't already present in the image - yarn --version || npm -g install yarn From d57d1d53cb0e4c653a95065288580bc40579e0f9 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Tue, 21 Nov 2023 17:52:49 +0100 Subject: [PATCH 5/5] chore: fix alpha CHANGELOG --- CHANGELOG.v2.alpha.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.v2.alpha.md b/CHANGELOG.v2.alpha.md index d032c563b43f9..fbe33c19b5e4b 100644 --- a/CHANGELOG.v2.alpha.md +++ b/CHANGELOG.v2.alpha.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. -### [2.110.1-alpha.0](https://github.com/aws/aws-cdk/compare/v2.110.0-alpha.0...v2.110.1-alpha.0) (2023-11-21) +## [2.110.1-alpha.0](https://github.com/aws/aws-cdk/compare/v2.110.0-alpha.0...v2.110.1-alpha.0) (2023-11-21) ## [2.110.0-alpha.0](https://github.com/aws/aws-cdk/compare/v2.109.0-alpha.0...v2.110.0-alpha.0) (2023-11-16)