Skip to content

Commit 992b0a5

Browse files
authored
fix(core): fix asset bundling for nested stack with exclusive flag (#30983)
### Issue #30967 Closes #30967 ### Reason for this change Incorrect bundling required check for child stack return by the function ### Description of changes The current nested stack used parent stack's inherited function which cause incorrect assessment More details description: * Given you have Stage "Stage", root stack "Stack1" and child stack "Stack1Nested". the `this.node.path` used is Stage/Stack1/StackNested, however the context it validate against is only "Stage/Stack1". the current function do not validate against child stack's stack name. To fix this, i simply have the child stack call the parent stack, and if the root stack is match then all child stack will be marked as bundling required. ### Description of how you validated changes Implemented via test driven approach, validate by test case ### Checklist - [ ] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent f2f5dae commit 992b0a5

File tree

3 files changed

+72
-1
lines changed

3 files changed

+72
-1
lines changed

packages/aws-cdk-lib/core/lib/nested-stack.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,10 @@ export class NestedStack extends Stack {
262262
resource.cfnOptions.metadata[cxapi.ASSET_RESOURCE_METADATA_PATH_KEY] = this.templateFile;
263263
resource.cfnOptions.metadata[cxapi.ASSET_RESOURCE_METADATA_PROPERTY_KEY] = resourceProperty;
264264
}
265+
266+
public get bundlingRequired() {
267+
return this._parentStack.bundlingRequired;
268+
}
265269
}
266270

267271
/**

packages/aws-cdk-lib/core/test/nested-stack.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as path from 'path';
22
import { Construct } from 'constructs';
33
import { readFileSync } from 'fs-extra';
44
import { toCloudFormation } from './util';
5+
import * as cxapi from '../../cx-api';
56
import {
67
Stack, NestedStack, CfnStack, Resource, CfnResource, App, CfnOutput,
78
} from '../lib';
@@ -168,6 +169,30 @@ describe('nested-stack', () => {
168169
expect(() => toCloudFormation(stack2)).toThrow(
169170
/Cannot use resource 'Stack1\/MyNestedStack\/MyResource' in a cross-environment fashion/);
170171
});
172+
173+
test('requires bundling when root stack has exact match in BUNDLING_STACKS', () => {
174+
const app = new App();
175+
const stack = new Stack(app, 'Stack');
176+
stack.node.setContext(cxapi.BUNDLING_STACKS, ['Stack']);
177+
178+
const child = new NestedStack(stack, 'Child');
179+
const child2 = new NestedStack(child, 'Child2');
180+
181+
expect(child.bundlingRequired).toBe(true);
182+
expect(child2.bundlingRequired).toBe(true);
183+
});
184+
185+
test('not requires bundling when root stack has no match in BUNDLING_STACKS', () => {
186+
const app = new App();
187+
const stack = new Stack(app, 'Stack');
188+
stack.node.setContext(cxapi.BUNDLING_STACKS, ['Stack2']);
189+
190+
const child = new NestedStack(stack, 'Child');
191+
const child2 = new NestedStack(child, 'Child2');
192+
193+
expect(child.bundlingRequired).toBe(false);
194+
expect(child2.bundlingRequired).toBe(false);
195+
});
171196
});
172197

173198
class MyResource extends Resource {

packages/aws-cdk-lib/core/test/staging.test.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import * as fs from 'fs-extra';
66
import * as sinon from 'sinon';
77
import { FileAssetPackaging } from '../../cloud-assembly-schema';
88
import * as cxapi from '../../cx-api';
9-
import { App, AssetHashType, AssetStaging, DockerImage, BundlingOptions, BundlingOutput, FileSystem, Stack, Stage, BundlingFileAccess } from '../lib';
9+
import { App, AssetHashType, AssetStaging, DockerImage, BundlingOptions, BundlingOutput, FileSystem, Stack, NestedStack, Stage, BundlingFileAccess } from '../lib';
1010

1111
const STUB_INPUT_FILE = '/tmp/docker-stub.input';
1212
const STUB_INPUT_CONCAT_FILE = '/tmp/docker-stub.input.concat';
@@ -1145,6 +1145,48 @@ describe('staging', () => {
11451145
expect(dockerStubInput).not.toMatch(DockerStubCommand.MULTIPLE_FILES);
11461146
});
11471147

1148+
test('correctly skips bundling with stack under stage and nested stack', () => {
1149+
// GIVEN
1150+
const app = new App();
1151+
1152+
const stage = new Stage(app, 'Stage');
1153+
stage.node.setContext(cxapi.BUNDLING_STACKS, ['Stage/Stack1']);
1154+
1155+
const stack1 = new Stack(stage, 'Stack1', { stackName: 'unrelated-stack1-name' });
1156+
const stack1Nested = new NestedStack(stack1, 'Stack1Nest');
1157+
1158+
const stack2 = new Stack(stage, 'Stack2', { stackName: 'unrelated-stack2-name' });
1159+
const stack2Nested = new NestedStack(stack2, 'Stack2Nest');
1160+
1161+
const directory = path.join(__dirname, 'fs', 'fixtures', 'test1');
1162+
1163+
// WHEN
1164+
new AssetStaging(stack1Nested, 'Asset', {
1165+
sourcePath: directory,
1166+
assetHashType: AssetHashType.OUTPUT,
1167+
bundling: {
1168+
image: DockerImage.fromRegistry('alpine'),
1169+
command: [DockerStubCommand.SUCCESS],
1170+
},
1171+
});
1172+
1173+
new AssetStaging(stack2Nested, 'Asset', {
1174+
sourcePath: directory,
1175+
assetHashType: AssetHashType.OUTPUT,
1176+
bundling: {
1177+
image: DockerImage.fromRegistry('alpine'),
1178+
command: [DockerStubCommand.MULTIPLE_FILES],
1179+
},
1180+
});
1181+
1182+
// THEN
1183+
const dockerStubInput = readDockerStubInputConcat();
1184+
// Docker ran for the asset in Stack1
1185+
expect(dockerStubInput).toMatch(DockerStubCommand.SUCCESS);
1186+
// Docker did not run for the asset in Stack2
1187+
expect(dockerStubInput).not.toMatch(DockerStubCommand.MULTIPLE_FILES);
1188+
});
1189+
11481190
test('correctly bundles with stack under stage and the default stack pattern', () => {
11491191
// GIVEN
11501192
const app = new App();

0 commit comments

Comments
 (0)