Skip to content

Commit 12459ca

Browse files
authored
fix(core): exportValue does not work on number attributes (#19818)
Number attributes go through two levels of encoding: - L1: because of lack of type information, all attributes are assumed to be `string`s, so we do `Token.asString(new CfnReference(...))`. - L2: we recast select attributes to numbers by doing `Token.asNumber()`. The end result is a Token that looks like: ```ts asNumber(Intrinsic(asString(CfnReference({ 'Fn::GetAtt': [...] })))) ``` When we do `Tokenization.reverse()` on the number, we only reverse the *first* encoding one layer, leaving us with an `Intrinsic` instead of the original `CfnReference`. `exportValue()` then rejects the value as not being the right type of token. Solution: before encoding, try to decode the given value so we always encode the innermost token, and not any of the inbetween ones. Fixes #19537. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 87786f4 commit 12459ca

File tree

5 files changed

+42
-3
lines changed

5 files changed

+42
-3
lines changed

packages/@aws-cdk/assertions/lib/private/outputs.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export function hasOutput(template: Template, logicalId: string, props: any): st
2020
}
2121

2222
if (result.closestResult === undefined) {
23-
return `No outputs named ${logicalId} found in the template.`;
23+
return `No outputs named ${logicalId} found in the template (found: ${Object.keys(section).join(', ')})`;
2424
}
2525

2626
return [

packages/@aws-cdk/aws-docdb/test/endpoint.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { Token } from '@aws-cdk/core';
22
import { Endpoint } from '../lib';
33

4-
// A numeric CDK token (see: https://docs.aws.amazon.com/cdk/latest/guide/tokens.html#tokens_number)
5-
const CDK_NUMERIC_TOKEN = -1.8881545897087626e+289;
4+
const CDK_NUMERIC_TOKEN = Token.asNumber({ Ref: 'abc' });
65

76
describe('Endpoint', () => {
87
test('accepts tokens for the port value', () => {

packages/@aws-cdk/aws-rds/test/serverless-cluster.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -862,6 +862,27 @@ describe('serverless cluster', () => {
862862
vpcSubnets,
863863
})).toThrow(/A VPC is required to use vpcSubnets in ServerlessCluster. Please add a VPC or remove vpcSubnets/);
864864
});
865+
866+
test('can call exportValue on endpoint.port', () => {
867+
// GIVEN
868+
const stack = new cdk.Stack();
869+
const vpc = new ec2.Vpc(stack, 'VPC');
870+
const cluster = new ServerlessCluster(stack, 'Database', {
871+
engine: DatabaseClusterEngine.AURORA_MYSQL,
872+
credentials: { username: 'admin' },
873+
vpc,
874+
});
875+
876+
// WHEN
877+
stack.exportValue(cluster.clusterEndpoint.port);
878+
879+
// THEN
880+
const template = Template.fromStack(stack);
881+
template.hasOutput('ExportsOutputFnGetAttDatabaseB269D8BBEndpointPort3ACB3F51', {
882+
Value: { 'Fn::GetAtt': ['DatabaseB269D8BB', 'Endpoint.Port'] },
883+
Export: { Name: 'Default:ExportsOutputFnGetAttDatabaseB269D8BBEndpointPort3ACB3F51' },
884+
});
885+
});
865886
});
866887

867888
function testStack(app?: cdk.App, id?: string): cdk.Stack {

packages/@aws-cdk/core/lib/token.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ export class Token {
9999
* Return a resolvable representation of the given value
100100
*/
101101
public static asAny(value: any): IResolvable {
102+
// First, reverse any encoding that was already done (so we end up with an IResolvable
103+
// if it was a token).
104+
value = Tokenization.reverse(value) ?? value;
105+
// Then, either return the IResolvable we resolved to, or wrap in an Intrinsic
102106
return isResolvableObject(value) ? value : new Intrinsic(value);
103107
}
104108

packages/@aws-cdk/core/test/tokens.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,21 @@ describe('tokens', () => {
523523
expect(resolved).toEqual({ value: 123 });
524524
});
525525

526+
test('Tokens are still reversible after having been encoded multiple times', () => {
527+
// GIVEN
528+
const original = new Intrinsic(123);
529+
530+
// WHEN
531+
let x: any = original;
532+
x = Token.asString(x);
533+
x = Token.asNumber(x);
534+
x = Token.asList(x);
535+
x = Token.asString(x);
536+
537+
// THEN
538+
expect(Tokenization.reverse(x)).toBe(original);
539+
});
540+
526541
test('regex detects all stringifications of encoded tokens', () => {
527542
expect(stringContainsNumberTokens(`${createTokenDouble(0)}`)).toBeTruthy();
528543
expect(stringContainsNumberTokens(`${createTokenDouble(Math.pow(2, 48) - 1)}`)).toBeTruthy(); // MAX_ENCODABLE_INTEGER

0 commit comments

Comments
 (0)