Skip to content

Commit 6e74e5e

Browse files
authored
feat(core): recommend the use of weak references if no choice has been made (#38070)
### Reason for this change For backward compatibility, the default value of the context parameter `@aws-cdk/core:defaultCrossStackReferences` is 'strong'. But we want to nudge users toward 'weak', so they can avoid deadly embraces. On the other hand, we also want to respect the user's choices and not annoy them unnecessarily. So, if they set the value to 'strong' or 'weak', explicitly, we don't want to emit a warning, assuming they know what they are doing. ### Description of changes Set 'weak' as the recommended value for `@aws-cdk/core:defaultCrossStackReferences` context parameter. The CLI will automatically pick this up on synthesis and print: > N feature flags are not configured. Run 'cdk flags --unstable=flags' to learn more. And, by running `cdk flags`, the user can see the details, update the value etc. In addition, to guide the user in doing the migration, emit the following warnings: - Flag unset: warns to set it to "both" and deploy everywhere, with a link to docs. - Flag set to "both": warns that after deploying everywhere, they should set it to "weak", with the same docs link. - Flag set to "strong" or "weak": no warning (user made an explicit choice). ### Checklist - [x] 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 fa1462b commit 6e74e5e

5 files changed

Lines changed: 105 additions & 4 deletions

File tree

packages/aws-cdk-lib/core/lib/private/refs.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ function crossStackReferenceStrength(scope: IConstruct): ReferenceStrength | und
4949
);
5050
}
5151

52+
const WEAK_REFS_WARNING_EMITTED = Symbol.for('@aws-cdk/core.WeakRefsWarningEmitted');
53+
5254
const OVERRIDDEN_REFERENCE_SYMBOL = Symbol.for('@aws-cdk/core.CustomCoupledReference');
5355

5456
/**
@@ -134,6 +136,27 @@ function resolveValue(consumer: Stack, reference: CfnReference, strengthOverride
134136
return reference;
135137
}
136138

139+
// Emit a once-per-app warning nudging users toward weak references
140+
const appRoot = consumer.node.root;
141+
if (!(appRoot as any)[WEAK_REFS_WARNING_EMITTED]) {
142+
const contextStrength = crossStackReferenceStrength(consumer);
143+
if (contextStrength === undefined) {
144+
(appRoot as any)[WEAK_REFS_WARNING_EMITTED] = true;
145+
Annotations.of(consumer).addWarningV2(
146+
'@aws-cdk/core:crossStackReferencesDefaultStrong',
147+
`No cross-stack-reference strength configured, defaulting to "strong". We recommend you set feature flag "${cxapi.DEFAULT_CROSS_STACK_REFERENCES}" to "both", then deploy everywhere, then set it to "weak". Alternatively, set it to "strong" explicitly to lock in the current producer-protecting behavior. ` +
148+
'(See: https://github.com/aws/aws-cdk/blob/main/packages/aws-cdk-lib/README.md#reference-strength)',
149+
);
150+
} else if (contextStrength === 'both') {
151+
(appRoot as any)[WEAK_REFS_WARNING_EMITTED] = true;
152+
Annotations.of(consumer).addWarningV2(
153+
'@aws-cdk/core:crossStackReferencesBothTransitional',
154+
`Feature flag "${cxapi.DEFAULT_CROSS_STACK_REFERENCES}" currently set to "both". This is a transitory state. After you have finished deploying this application everywhere, set it to "weak". ` +
155+
'(See: https://github.com/aws/aws-cdk/blob/main/packages/aws-cdk-lib/README.md#reference-strength)',
156+
);
157+
}
158+
}
159+
137160
// unsupported: stacks from different apps
138161
if (producer.node.root !== consumer.node.root) {
139162
throw new UnscopedValidationError(lit`CannotReferenceAcrossApps`, 'Cannot reference across apps. Consuming and producing stacks must be defined within the same CDK app.');

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

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as fs from 'fs';
22
import { testDeprecated } from '@aws-cdk/cdk-build-tools';
33
import { Construct, Node } from 'constructs';
4-
import { flattenMeta, toCloudFormation } from './util';
4+
import { flattenMeta, getWarnings, toCloudFormation } from './util';
55
import * as cxapi from '../../cx-api';
66
import { Fact, RegionInfo } from '../../region-info';
77
import type { ITaggableV2 } from '../lib';
@@ -1074,6 +1074,82 @@ describe('stack', () => {
10741074
expect(customResources2.length).toBeGreaterThan(0);
10751075
});
10761076

1077+
test('emits a single warning per app when cross-stack reference flag is unset', () => {
1078+
// GIVEN - no context flag set, multiple consumer stacks
1079+
const app = new App();
1080+
const stack1 = new Stack(app, 'Stack1');
1081+
const resource1 = new CfnResource(stack1, 'Resource1', { type: 'AWS::S3::Bucket' });
1082+
const resource2 = new CfnResource(stack1, 'Resource2', { type: 'AWS::SNS::Topic' });
1083+
const stack2 = new Stack(app, 'Stack2');
1084+
const stack3 = new Stack(app, 'Stack3');
1085+
1086+
// WHEN - cross-stack references from multiple consumers
1087+
new CfnResource(stack2, 'Consumer1', {
1088+
type: 'AWS::S3::Bucket',
1089+
properties: { Prop1: resource1.getAtt('Arn') },
1090+
});
1091+
new CfnResource(stack3, 'Consumer2', {
1092+
type: 'AWS::S3::Bucket',
1093+
properties: { Prop2: resource2.getAtt('Arn') },
1094+
});
1095+
1096+
const assembly = app.synth();
1097+
const warnings = getWarnings(assembly);
1098+
1099+
// THEN - only one warning in the entire app
1100+
const relevantWarnings = warnings.filter(w =>
1101+
w.message.includes('@aws-cdk/core:crossStackReferencesDefaultStrong'),
1102+
);
1103+
expect(relevantWarnings).toHaveLength(1);
1104+
});
1105+
1106+
test('no warning when cross-stack reference flag is explicitly set', () => {
1107+
// GIVEN - context flag explicitly set to 'strong'
1108+
const app = new App({ context: { [cxapi.DEFAULT_CROSS_STACK_REFERENCES]: 'strong' } });
1109+
const stack1 = new Stack(app, 'Stack1');
1110+
const resource1 = new CfnResource(stack1, 'Resource1', { type: 'AWS::S3::Bucket' });
1111+
const stack2 = new Stack(app, 'Stack2');
1112+
1113+
// WHEN
1114+
new CfnResource(stack2, 'Consumer1', {
1115+
type: 'AWS::S3::Bucket',
1116+
properties: { Prop1: resource1.getAtt('Arn') },
1117+
});
1118+
1119+
const assembly = app.synth();
1120+
const warnings = getWarnings(assembly);
1121+
1122+
// THEN - no warning because the flag is explicitly set
1123+
const relevantWarnings = warnings.filter(w =>
1124+
w.message.includes('@aws-cdk/core:crossStackReferencesDefaultStrong'),
1125+
);
1126+
expect(relevantWarnings).toHaveLength(0);
1127+
});
1128+
1129+
test('emits transitional warning when cross-stack reference flag is set to both', () => {
1130+
// GIVEN - context flag set to 'both'
1131+
const app = new App({ context: { [cxapi.DEFAULT_CROSS_STACK_REFERENCES]: 'both' } });
1132+
const stack1 = new Stack(app, 'Stack1');
1133+
const resource1 = new CfnResource(stack1, 'Resource1', { type: 'AWS::S3::Bucket' });
1134+
const stack2 = new Stack(app, 'Stack2');
1135+
1136+
// WHEN
1137+
new CfnResource(stack2, 'Consumer1', {
1138+
type: 'AWS::S3::Bucket',
1139+
properties: { Prop1: resource1.getAtt('Arn') },
1140+
});
1141+
1142+
const assembly = app.synth();
1143+
const warnings = getWarnings(assembly);
1144+
1145+
// THEN - transitional warning telling user to move to 'weak'
1146+
const relevantWarnings = warnings.filter(w =>
1147+
w.message.includes('@aws-cdk/core:crossStackReferencesBothTransitional'),
1148+
);
1149+
expect(relevantWarnings).toHaveLength(1);
1150+
expect(relevantWarnings[0].path).toContain('Stack2');
1151+
});
1152+
10771153
test('cross-region strong references use ExportWriter/ExportReader', () => {
10781154
// GIVEN - strength is explicitly 'strong'
10791155
const app = new App({ context: { [cxapi.DEFAULT_CROSS_STACK_REFERENCES]: 'strong' } });

packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ The following json shows the current recommended set of flags, as `cdk init` wou
201201
"@aws-cdk/core:aspectPrioritiesMutating": true,
202202
"@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": true,
203203
"@aws-cdk/core:checkSecretUsage": true,
204+
"@aws-cdk/core:defaultCrossStackReferences": "weak",
204205
"@aws-cdk/core:enableAdditionalMetadataCollection": true,
205206
"@aws-cdk/core:enablePartitionLiterals": true,
206207
"@aws-cdk/core:explicitStackTags": true,
@@ -2497,7 +2498,7 @@ The flag is read from the **consumer** stack's context, not the producer's.
24972498
| Since | Unset behaves like | Recommended value |
24982499
| ----- | ----- | ----- |
24992500
| (not in v1) | | |
2500-
| 2.254.0 | `"strong"` | `"strong"` |
2501+
| 2.254.0 | `"strong"` | `"weak"` |
25012502

25022503

25032504
<!-- END details -->

packages/aws-cdk-lib/cx-api/lib/features.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1888,7 +1888,7 @@ export const FLAGS: Record<string, FlagInfo> = {
18881888
18891889
**Migration from weak to strong**: set directly to \`"strong"\` (single deployment).`,
18901890
introducedIn: { v2: '2.254.0' },
1891-
recommendedValue: 'strong',
1891+
recommendedValue: 'weak',
18921892
unconfiguredBehavesLike: { v2: 'strong' },
18931893
},
18941894
};

packages/aws-cdk-lib/recommended-feature-flags.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,5 +84,6 @@
8484
"@aws-cdk/aws-cloudfront:defaultFunctionRuntimeV2_0": true,
8585
"@aws-cdk/aws-elasticloadbalancingv2:usePostQuantumTlsPolicy": true,
8686
"@aws-cdk/aws-batch:defaultToAL2023": true,
87-
"@aws-cdk/core:annotationsInValidationReport": true
87+
"@aws-cdk/core:annotationsInValidationReport": true,
88+
"@aws-cdk/core:defaultCrossStackReferences": "weak"
8889
}

0 commit comments

Comments
 (0)