Skip to content

Commit 55621ad

Browse files
fix(core): Add stage prefix to stack name shortening process (#24443)
There is an issue in the stack name generation process where the prefix generated from assembly's stage name is not taken into account when shortening a stack name to meet the requirement of being equal or less than 128 characters longs. This can lead to generating an invalid stack name greater than 128 characters: since the stack name is shortened to 128 characters, when the prefix is added, the limit is exceeded. Current solution: - Add the prefix into the first ID if any prefix is present. Alternative solution: - Add the prefix at the start of the ID list. Doing this would require to add more conditionals and consider more corner cases to keep the names consistent with the old algorithm. Fixes #23628 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 0f92c7a commit 55621ad

File tree

3 files changed

+34
-7
lines changed

3 files changed

+34
-7
lines changed

packages/aws-cdk-lib/core/lib/private/unique-resource-name.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ interface MakeUniqueResourceNameOptions {
2525
* @default - none
2626
*/
2727
readonly allowedSpecialCharacters?: string;
28+
29+
/**
30+
* Prefix to be added into the stack name
31+
*
32+
* @default - none
33+
*/
34+
readonly prefix?: string;
2835
}
2936

3037
/**
@@ -49,6 +56,7 @@ const HASH_LEN = 8;
4956
export function makeUniqueResourceName(components: string[], options: MakeUniqueResourceNameOptions) {
5057
const maxLength = options.maxLength ?? 256;
5158
const separator = options.separator ?? '';
59+
const prefix = options.prefix ?? '';
5260
components = components.filter(x => x !== HIDDEN_ID);
5361

5462
if (components.length === 0) {
@@ -59,7 +67,7 @@ export function makeUniqueResourceName(components: string[], options: MakeUnique
5967
// in order to support transparent migration of cloudformation templates to the CDK without the
6068
// need to rename all resources.
6169
if (components.length === 1) {
62-
const topLevelResource = removeNonAllowedSpecialCharacters(components[0], separator, options.allowedSpecialCharacters);
70+
const topLevelResource = prefix + removeNonAllowedSpecialCharacters(components[0], separator, options.allowedSpecialCharacters);
6371

6472
if (topLevelResource.length <= maxLength) {
6573
return topLevelResource;
@@ -68,6 +76,9 @@ export function makeUniqueResourceName(components: string[], options: MakeUnique
6876

6977
// Calculate the hash from the full path, included unresolved tokens so the hash value is always unique
7078
const hash = pathHash(components);
79+
if (prefix) {
80+
components.unshift(prefix);
81+
}
7182
const human = removeDupes(components)
7283
.filter(pathElement => pathElement !== HIDDEN_FROM_HUMAN_ID)
7384
.map(pathElement => removeNonAllowedSpecialCharacters(pathElement, separator, options.allowedSpecialCharacters))

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

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1424,7 +1424,7 @@ export class Stack extends Construct implements ITaggable {
14241424
private generateStackName() {
14251425
const assembly = Stage.of(this);
14261426
const prefix = (assembly && assembly.stageName) ? `${assembly.stageName}-` : '';
1427-
return `${prefix}${this.generateStackId(assembly)}`;
1427+
return `${this.generateStackId(assembly, prefix)}`;
14281428
}
14291429

14301430
/**
@@ -1439,7 +1439,7 @@ export class Stack extends Construct implements ITaggable {
14391439
/**
14401440
* Generate an ID with respect to the given container construct.
14411441
*/
1442-
private generateStackId(container: IConstruct | undefined) {
1442+
private generateStackId(container: IConstruct | undefined, prefix: string='') {
14431443
const rootPath = rootPathTo(this, container);
14441444
const ids = rootPath.map(c => Node.of(c).id);
14451445

@@ -1449,7 +1449,7 @@ export class Stack extends Construct implements ITaggable {
14491449
throw new Error('unexpected: stack id must always be defined');
14501450
}
14511451

1452-
return makeStackName(ids);
1452+
return makeStackName(ids, prefix);
14531453
}
14541454

14551455
private resolveExportedValue(exportedValue: any): ResolvedExport {
@@ -1635,9 +1635,14 @@ export function rootPathTo(construct: IConstruct, ancestor?: IConstruct): IConst
16351635
* has only one component. Otherwise we fall back to the regular "makeUniqueId"
16361636
* behavior.
16371637
*/
1638-
function makeStackName(components: string[]) {
1639-
if (components.length === 1) { return components[0]; }
1640-
return makeUniqueResourceName(components, { maxLength: 128 });
1638+
function makeStackName(components: string[], prefix: string='') {
1639+
if (components.length === 1) {
1640+
const stack_name = prefix + components[0];
1641+
if (stack_name.length <= 128) {
1642+
return stack_name;
1643+
}
1644+
}
1645+
return makeUniqueResourceName(components, { maxLength: 128, prefix: prefix });
16411646
}
16421647

16431648
function getCreateExportsScope(stack: Stack) {

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,17 @@ describe('stage', () => {
103103
expect(stack.stackName).toEqual('MyStage-MyStack');
104104
});
105105

106+
test('generated stack names will not exceed 128 characters', () => {
107+
// WHEN
108+
const app = new App();
109+
const stage = new Stage(app, 'ThisStageNameIsVeryLongButWillOnlyBeTooLongWhenCombinedWithTheStackName');
110+
const stack = new BogusStack(stage, 'ThisStackNameIsVeryLongButItWillOnlyBeTooLongWhenCombinedWithTheLongPrefix');
111+
112+
// THEN
113+
expect(stack.stackName.length).toEqual(128);
114+
expect(stack.stackName).toEqual('ThisStageNameIsVeryLongButWillOnlyBeTooLongWhenCombinedWithTsVeryLongButItWillOnlyBeTooLongWhenCombinedWithTheLongPrefix4CA9F65B');
115+
});
116+
106117
test('Can not have dependencies to stacks outside the nested asm', () => {
107118
// GIVEN
108119
const app = new App();

0 commit comments

Comments
 (0)