From 823a1e80b03a85a6f12612e488ff8633f0f81ed2 Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Wed, 6 Feb 2019 16:41:30 +0200 Subject: [PATCH] feat(core): overrideLogicalId: override IDs of CFN elements (#1670) Allow users to explicitly override the logical ID of a CloudFormation element (such as "Cfn" resources) by invoking `overrideLogicalId` on the resource object. For example: const bucket = new s3.CfnBucket(this, 'MyBucket'); bucket.overrideLogicalId('YourBucket'); The resulting template will use `YourBucket` as the logical ID. NOTE: the `logicalId` property will now return a stringified token instead of a concrete value. Fixes #1594 --- .../test/integ.cicd.expected.json | 6 +-- packages/@aws-cdk/assets/test/test.asset.ts | 29 ++++++++++++++- .../test/integ.restapi.books.expected.json | 8 ++-- .../test/integ.restapi.expected.json | 10 ++--- .../aws-apigateway/test/test.restapi.ts | 6 +-- .../test/test.lifecyclehooks.ts | 2 +- .../test/integ.docker-asset.lit.expected.json | 6 +-- .../integ.deployment-group.expected.json | 4 +- ...g.cfn-template-from-repo.lit.expected.json | 4 +- .../test/integ.lambda-pipeline.expected.json | 4 +- .../integ.pipeline-alexa-deploy.expected.json | 4 +- ...eg.pipeline-cfn-cross-region.expected.json | 4 +- ...ipeline-cfn-wtih-action-role.expected.json | 4 +- .../test/integ.pipeline-cfn.expected.json | 4 +- ...uild-multiple-inputs-outputs.expected.json | 4 +- ...g.pipeline-code-commit-build.expected.json | 4 +- .../integ.pipeline-code-commit.expected.json | 4 +- .../integ.pipeline-code-deploy.expected.json | 4 +- .../integ.pipeline-ecr-source.expected.json | 4 +- .../test/integ.pipeline-events.expected.json | 4 +- .../test/integ.pipeline-jenkins.expected.json | 4 +- ...teg.pipeline-manual-approval.expected.json | 4 +- .../integ.pipeline-s3-deploy.expected.json | 4 +- .../ec2/integ.event-task.lit.expected.json | 16 ++++---- .../test/ec2/integ.lb-awsvpc-nw.expected.json | 16 ++++---- .../test/ec2/integ.lb-bridge-nw.expected.json | 16 ++++---- .../fargate/integ.asset-image.expected.json | 8 ++-- .../test/fargate/integ.l3.expected.json | 4 +- .../fargate/integ.lb-awsvpc-nw.expected.json | 4 +- .../test/nlb/test.listener.ts | 4 +- .../test/integ.dynamodb.expected.json | 4 +- .../test/integ.kinesis.expected.json | 4 +- .../test/integ.sqs.expected.json | 4 +- .../test/integ.lambda.expected.json | 6 +-- .../@aws-cdk/aws-lambda/test/test.lambda.ts | 22 +++++------ .../integ.bucket-deployment.expected.json | 6 +-- packages/@aws-cdk/aws-sns/test/test.sns.ts | 3 +- .../cdk/lib/cloudformation/cfn-tokens.ts | 4 ++ .../cdk/lib/cloudformation/resource.ts | 26 ++++++++----- .../cdk/lib/cloudformation/stack-element.ts | 32 +++++++++++----- .../@aws-cdk/cdk/lib/cloudformation/stack.ts | 7 ++-- .../@aws-cdk/cdk/lib/core/tokens/encoding.ts | 7 +--- .../test/cloudformation/test.logical-id.ts | 10 ++--- .../cdk/test/cloudformation/test.output.ts | 4 +- .../cdk/test/cloudformation/test.parameter.ts | 4 +- .../cdk/test/cloudformation/test.resource.ts | 37 ++++++++++++++++++- .../cdk/test/cloudformation/test.stack.ts | 24 ++++++++++++ .../test/integ.rtv.lambda.expected.json | 4 +- 48 files changed, 255 insertions(+), 152 deletions(-) diff --git a/packages/@aws-cdk/app-delivery/test/integ.cicd.expected.json b/packages/@aws-cdk/app-delivery/test/integ.cicd.expected.json index e143489256939..3dfdea3ef5319 100644 --- a/packages/@aws-cdk/app-delivery/test/integ.cicd.expected.json +++ b/packages/@aws-cdk/app-delivery/test/integ.cicd.expected.json @@ -239,8 +239,8 @@ ] }, "DependsOn": [ - "CodePipelineRoleB3A660B4", - "CodePipelineRoleDefaultPolicy8D520A8D" + "CodePipelineRoleDefaultPolicy8D520A8D", + "CodePipelineRoleB3A660B4" ] }, "DeployStackChangeSetRole4923A126": { @@ -261,4 +261,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/assets/test/test.asset.ts b/packages/@aws-cdk/assets/test/test.asset.ts index 849063ffa7189..1c23f1b49f699 100644 --- a/packages/@aws-cdk/assets/test/test.asset.ts +++ b/packages/@aws-cdk/assets/test/test.asset.ts @@ -18,7 +18,10 @@ export = { // the correct information const entry = asset.node.metadata.find(m => m.type === 'aws:cdk:asset'); test.ok(entry, 'found metadata entry'); - test.deepEqual(entry!.data, { + + // console.error(JSON.stringify(stack.node.resolve(entry!.data))); + + test.deepEqual(stack.node.resolve(entry!.data), { path: dirPath, id: 'MyAsset', packaging: 'zip', @@ -34,13 +37,35 @@ export = { test.done(); }, + 'verify that the app resolves tokens in metadata'(test: Test) { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'my-stack'); + const dirPath = path.resolve(__dirname, 'sample-asset-directory'); + + new ZipDirectoryAsset(stack, 'MyAsset', { + path: dirPath + }); + + const synth = app.synthesizeStack(stack.name); + + test.deepEqual(synth.metadata['/my-stack/MyAsset'][0].data, { + path: dirPath, + id: "mystackMyAssetD6B1B593", + packaging: "zip", + s3BucketParameter: "MyAssetS3Bucket68C9B344", + s3KeyParameter: "MyAssetS3VersionKey68E1A45D" + }); + + test.done(); + }, + '"file" assets'(test: Test) { const stack = new cdk.Stack(); const filePath = path.join(__dirname, 'file-asset.txt'); const asset = new FileAsset(stack, 'MyAsset', { path: filePath }); const entry = asset.node.metadata.find(m => m.type === 'aws:cdk:asset'); test.ok(entry, 'found metadata entry'); - test.deepEqual(entry!.data, { + test.deepEqual(stack.node.resolve(entry!.data), { path: filePath, packaging: 'file', id: 'MyAsset', diff --git a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.books.expected.json b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.books.expected.json index 46ba00f170929..404b8eee1cf95 100644 --- a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.books.expected.json +++ b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.books.expected.json @@ -526,12 +526,12 @@ }, "DependsOn": [ "booksapiANYF4F0CDEB", - "booksapibooks97D84727", + "booksapibooksbookidDELETE214F4059", + "booksapibooksbookidGETCCE21986", + "booksapibooksbookid5264BCA2", "booksapibooksGETA776447A", "booksapibooksPOSTF6C6559D", - "booksapibooksbookid5264BCA2", - "booksapibooksbookidDELETE214F4059", - "booksapibooksbookidGETCCE21986" + "booksapibooks97D84727" ] }, "booksapiDeploymentStageprod55D8E03E": { diff --git a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.expected.json b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.expected.json index 664c3b5c6617c..bcd28e191f331 100644 --- a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.expected.json +++ b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.expected.json @@ -15,16 +15,16 @@ "Description": "Automatically created by the RestApi construct" }, "DependsOn": [ - "myapiv113487378", - "myapiv1appliances507FEFF4", "myapiv1appliancesGET8FE872EC", - "myapiv1books1D4BE6C1", + "myapiv1appliances507FEFF4", "myapiv1booksGETC6B996D0", "myapiv1booksPOST53E2832E", - "myapiv1toysA55FCBC4", + "myapiv1books1D4BE6C1", + "myapiv113487378", "myapiv1toysGET7348114D", "myapiv1toysPOST55128058", - "myapiv1toysPUT59AFBBC2" + "myapiv1toysPUT59AFBBC2", + "myapiv1toysA55FCBC4" ], "DeletionPolicy": "Retain" }, diff --git a/packages/@aws-cdk/aws-apigateway/test/test.restapi.ts b/packages/@aws-cdk/aws-apigateway/test/test.restapi.ts index ffe80668ca896..94d56d5150590 100644 --- a/packages/@aws-cdk/aws-apigateway/test/test.restapi.ts +++ b/packages/@aws-cdk/aws-apigateway/test/test.restapi.ts @@ -525,12 +525,12 @@ export = { // THEN expect(stack).to(haveResource('My::Resource', { DependsOn: [ - "myapiDeploymentB7EF8EB75c091a668064a3f3a1f6d68a3fb22cf9", - "myapi162F20B8", "myapiAccountC3A4750C", "myapiCloudWatchRoleEB425128", + "myapiDeploymentB7EF8EB75c091a668064a3f3a1f6d68a3fb22cf9", "myapiDeploymentStageprod329F21FF", - "myapiGET9B7CD29E" + "myapiGET9B7CD29E", + "myapi162F20B8" ] }, ResourcePart.CompleteDefinition)); diff --git a/packages/@aws-cdk/aws-autoscaling/test/test.lifecyclehooks.ts b/packages/@aws-cdk/aws-autoscaling/test/test.lifecyclehooks.ts index c01201c556194..df1d599d2f50f 100644 --- a/packages/@aws-cdk/aws-autoscaling/test/test.lifecyclehooks.ts +++ b/packages/@aws-cdk/aws-autoscaling/test/test.lifecyclehooks.ts @@ -34,8 +34,8 @@ export = { // Lifecycle Hook has a dependency on the policy object expect(stack).to(haveResource('AWS::AutoScaling::LifecycleHook', { DependsOn: [ + "ASGLifecycleHookTransitionRoleDefaultPolicy2E50C7DB", "ASGLifecycleHookTransitionRole3AAA6BB7", - "ASGLifecycleHookTransitionRoleDefaultPolicy2E50C7DB" ] }, ResourcePart.CompleteDefinition)); diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.docker-asset.lit.expected.json b/packages/@aws-cdk/aws-codebuild/test/integ.docker-asset.lit.expected.json index 887a952d0ef7c..14fcfec0b7098 100644 --- a/packages/@aws-cdk/aws-codebuild/test/integ.docker-asset.lit.expected.json +++ b/packages/@aws-cdk/aws-codebuild/test/integ.docker-asset.lit.expected.json @@ -197,8 +197,8 @@ "Timeout": 300 }, "DependsOn": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17", - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C" + "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", + "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" ] }, "MyProjectRole9BBE5233": { @@ -416,4 +416,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-group.expected.json b/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-group.expected.json index 6dfeb4061a356..d47c94ea745f4 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-group.expected.json +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-group.expected.json @@ -253,8 +253,8 @@ "Runtime": "nodejs8.10" }, "DependsOn": [ - "PreHookServiceRoleC724B9BA", - "PreHookServiceRoleDefaultPolicy65358F76" + "PreHookServiceRoleDefaultPolicy65358F76", + "PreHookServiceRoleC724B9BA" ] }, "PostHookServiceRoleE8A6AAC2": { diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.cfn-template-from-repo.lit.expected.json b/packages/@aws-cdk/aws-codepipeline/test/integ.cfn-template-from-repo.lit.expected.json index 552b0ee5903aa..313d1a5f5ac39 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/integ.cfn-template-from-repo.lit.expected.json +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.cfn-template-from-repo.lit.expected.json @@ -272,8 +272,8 @@ ] }, "DependsOn": [ - "PipelineRoleD68726F7", - "PipelineRoleDefaultPolicyC7A05455" + "PipelineRoleDefaultPolicyC7A05455", + "PipelineRoleD68726F7" ] }, "PipelineDeployPrepareChangesRoleD28C853C": { diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.lambda-pipeline.expected.json b/packages/@aws-cdk/aws-codepipeline/test/integ.lambda-pipeline.expected.json index 9ce5afc38218f..b33012fec4f5e 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/integ.lambda-pipeline.expected.json +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.lambda-pipeline.expected.json @@ -184,8 +184,8 @@ ] }, "DependsOn": [ - "PipelineRoleD68726F7", - "PipelineRoleDefaultPolicyC7A05455" + "PipelineRoleDefaultPolicyC7A05455", + "PipelineRoleD68726F7" ] }, "PipelineEventsRole46BEEA7C": { diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-alexa-deploy.expected.json b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-alexa-deploy.expected.json index f65ac6af5d5f4..d8c834a6bc4de 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-alexa-deploy.expected.json +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-alexa-deploy.expected.json @@ -173,8 +173,8 @@ } }, "DependsOn": [ - "PipelineRoleD68726F7", - "PipelineRoleDefaultPolicyC7A05455" + "PipelineRoleDefaultPolicyC7A05455", + "PipelineRoleD68726F7" ] }, "PipelineBucketB967BD35": { diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn-cross-region.expected.json b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn-cross-region.expected.json index 11d129412453c..c6c39076795ce 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn-cross-region.expected.json +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn-cross-region.expected.json @@ -222,8 +222,8 @@ ] }, "DependsOn": [ - "MyPipelineRoleC0D47CA4", - "MyPipelineRoleDefaultPolicy34F09EFA" + "MyPipelineRoleDefaultPolicy34F09EFA", + "MyPipelineRoleC0D47CA4" ] }, "CFNDeployRole68D5E8D3": { diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn-wtih-action-role.expected.json b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn-wtih-action-role.expected.json index 996cc24369ad7..6e3db81ef398c 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn-wtih-action-role.expected.json +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn-wtih-action-role.expected.json @@ -238,8 +238,8 @@ } }, "DependsOn": [ - "MyPipelineRoleC0D47CA4", - "MyPipelineRoleDefaultPolicy34F09EFA" + "MyPipelineRoleDefaultPolicy34F09EFA", + "MyPipelineRoleC0D47CA4" ] }, "ActionRole60B0EDF7": { diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn.expected.json b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn.expected.json index ce6601744b4fc..a41cc93d39df2 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn.expected.json +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn.expected.json @@ -224,8 +224,8 @@ ] }, "DependsOn": [ - "PipelineRoleD68726F7", - "PipelineRoleDefaultPolicyC7A05455" + "PipelineRoleDefaultPolicyC7A05455", + "PipelineRoleD68726F7" ] }, "PipelineBucketB967BD35": { diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-build-multiple-inputs-outputs.expected.json b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-build-multiple-inputs-outputs.expected.json index edf559a6831b7..e038fb3a5e13b 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-build-multiple-inputs-outputs.expected.json +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-build-multiple-inputs-outputs.expected.json @@ -358,8 +358,8 @@ ] }, "DependsOn": [ - "PipelineRoleD68726F7", - "PipelineRoleDefaultPolicyC7A05455" + "PipelineRoleDefaultPolicyC7A05455", + "PipelineRoleD68726F7" ] }, "PipelineEventsRole46BEEA7C": { diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit-build.expected.json b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit-build.expected.json index c0083e6923c60..5bdbf836e3627 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit-build.expected.json +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit-build.expected.json @@ -222,8 +222,8 @@ ] }, "DependsOn": [ - "PipelineRoleD68726F7", - "PipelineRoleDefaultPolicyC7A05455" + "PipelineRoleDefaultPolicyC7A05455", + "PipelineRoleD68726F7" ] }, "MyBuildProjectRole6B7E2258": { diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit.expected.json b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit.expected.json index b6e9ea9fa6c85..147696b2cb636 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit.expected.json +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit.expected.json @@ -225,8 +225,8 @@ ] }, "DependsOn": [ - "PipelineRoleD68726F7", - "PipelineRoleDefaultPolicyC7A05455" + "PipelineRoleDefaultPolicyC7A05455", + "PipelineRoleD68726F7" ] }, "PipelineEventsRole46BEEA7C": { diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-deploy.expected.json b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-deploy.expected.json index a1615f8e2905c..5b7f0e88fb948 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-deploy.expected.json +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-deploy.expected.json @@ -330,8 +330,8 @@ ] }, "DependsOn": [ - "PipelineRoleD68726F7", - "PipelineRoleDefaultPolicyC7A05455" + "PipelineRoleDefaultPolicyC7A05455", + "PipelineRoleD68726F7" ] } } diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-ecr-source.expected.json b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-ecr-source.expected.json index 6236aba51716e..d83bf9168554b 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-ecr-source.expected.json +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-ecr-source.expected.json @@ -141,8 +141,8 @@ } }, "DependsOn": [ - "MyPipelineRoleC0D47CA4", - "MyPipelineRoleDefaultPolicy34F09EFA" + "MyPipelineRoleDefaultPolicy34F09EFA", + "MyPipelineRoleC0D47CA4" ] }, "MyPipelineEventsRoleFAB99F32": { diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-events.expected.json b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-events.expected.json index b977353f76690..d5dbe738cd94e 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-events.expected.json +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-events.expected.json @@ -180,8 +180,8 @@ ] }, "DependsOn": [ - "MyPipelineRoleC0D47CA4", - "MyPipelineRoleDefaultPolicy34F09EFA" + "MyPipelineRoleDefaultPolicy34F09EFA", + "MyPipelineRoleC0D47CA4" ] }, "MyPipelineOnPipelineStateChangeA017E4B1": { diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-jenkins.expected.json b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-jenkins.expected.json index fcd2e2444f8f1..85bfa3a5b6ed1 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-jenkins.expected.json +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-jenkins.expected.json @@ -217,8 +217,8 @@ ] }, "DependsOn": [ - "PipelineRoleD68726F7", - "PipelineRoleDefaultPolicyC7A05455" + "PipelineRoleDefaultPolicyC7A05455", + "PipelineRoleD68726F7" ] }, "JenkinsProviderJenkinsBuildProviderResourceD9231CAC": { diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-manual-approval.expected.json b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-manual-approval.expected.json index d27e5e8587ea3..83a73e2128b66 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-manual-approval.expected.json +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-manual-approval.expected.json @@ -175,8 +175,8 @@ } }, "DependsOn": [ - "PipelineRoleD68726F7", - "PipelineRoleDefaultPolicyC7A05455" + "PipelineRoleDefaultPolicyC7A05455", + "PipelineRoleD68726F7" ] }, "ManualApprovalTopicResource300641E2": { diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-s3-deploy.expected.json b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-s3-deploy.expected.json index c490f7a2cd023..9165619069674 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-s3-deploy.expected.json +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-s3-deploy.expected.json @@ -203,8 +203,8 @@ } }, "DependsOn": [ - "PipelineRoleD68726F7", - "PipelineRoleDefaultPolicyC7A05455" + "PipelineRoleDefaultPolicyC7A05455", + "PipelineRoleD68726F7" ] }, "PipelineBucketB967BD35": { diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.event-task.lit.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.event-task.lit.expected.json index 364dfeb0633f4..d7925a2d328ec 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.event-task.lit.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.event-task.lit.expected.json @@ -307,8 +307,8 @@ } }, "DependsOn": [ - "EcsClusterDefaultAutoScalingGroupInstanceRole3C026863", - "EcsClusterDefaultAutoScalingGroupInstanceRoleDefaultPolicy04DC6C80" + "EcsClusterDefaultAutoScalingGroupInstanceRoleDefaultPolicy04DC6C80", + "EcsClusterDefaultAutoScalingGroupInstanceRole3C026863" ] }, "EcsClusterDefaultAutoScalingGroupASGC1A785DB": { @@ -461,8 +461,8 @@ "Timeout": 310 }, "DependsOn": [ - "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA", - "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRoleDefaultPolicyA45BF396" + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRoleDefaultPolicyA45BF396", + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA" ] }, "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionTopicE6B1EBA6": { @@ -538,8 +538,8 @@ } }, "DependsOn": [ - "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleA38EC83B", - "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleDefaultPolicy75002F88" + "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleDefaultPolicy75002F88", + "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleA38EC83B" ] }, "TaskDefTaskRole1EDB4A67": { @@ -1031,8 +1031,8 @@ "Timeout": 300 }, "DependsOn": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17", - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C" + "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", + "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" ] }, "TaskLoggingLogGroupC7E938D4": { diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-awsvpc-nw.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-awsvpc-nw.expected.json index e566dfadd397c..c70b4bdd68f30 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-awsvpc-nw.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-awsvpc-nw.expected.json @@ -460,8 +460,8 @@ } }, "DependsOn": [ - "EcsClusterDefaultAutoScalingGroupInstanceRole3C026863", - "EcsClusterDefaultAutoScalingGroupInstanceRoleDefaultPolicy04DC6C80" + "EcsClusterDefaultAutoScalingGroupInstanceRoleDefaultPolicy04DC6C80", + "EcsClusterDefaultAutoScalingGroupInstanceRole3C026863" ] }, "EcsClusterDefaultAutoScalingGroupASGC1A785DB": { @@ -617,8 +617,8 @@ "Timeout": 310 }, "DependsOn": [ - "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA", - "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRoleDefaultPolicyA45BF396" + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRoleDefaultPolicyA45BF396", + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA" ] }, "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionTopicE6B1EBA6": { @@ -694,8 +694,8 @@ } }, "DependsOn": [ - "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleA38EC83B", - "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleDefaultPolicy75002F88" + "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleDefaultPolicy75002F88", + "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleA38EC83B" ] }, "TaskDefTaskRole1EDB4A67": { @@ -809,8 +809,8 @@ "SchedulingStrategy": "REPLICA" }, "DependsOn": [ - "LBPublicListener6E1F3D94", - "LBPublicListenerECSGroupD6A32205" + "LBPublicListenerECSGroupD6A32205", + "LBPublicListener6E1F3D94" ] }, "ServiceSecurityGroupC96ED6A7": { diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-bridge-nw.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-bridge-nw.expected.json index 802bbbfff8f04..0d9d1497b6d86 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-bridge-nw.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-bridge-nw.expected.json @@ -481,8 +481,8 @@ } }, "DependsOn": [ - "EcsClusterDefaultAutoScalingGroupInstanceRole3C026863", - "EcsClusterDefaultAutoScalingGroupInstanceRoleDefaultPolicy04DC6C80" + "EcsClusterDefaultAutoScalingGroupInstanceRoleDefaultPolicy04DC6C80", + "EcsClusterDefaultAutoScalingGroupInstanceRole3C026863" ] }, "EcsClusterDefaultAutoScalingGroupASGC1A785DB": { @@ -638,8 +638,8 @@ "Timeout": 310 }, "DependsOn": [ - "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA", - "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRoleDefaultPolicyA45BF396" + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRoleDefaultPolicyA45BF396", + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA" ] }, "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionTopicE6B1EBA6": { @@ -715,8 +715,8 @@ } }, "DependsOn": [ - "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleA38EC83B", - "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleDefaultPolicy75002F88" + "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleDefaultPolicy75002F88", + "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleA38EC83B" ] }, "TaskDefTaskRole1EDB4A67": { @@ -810,8 +810,8 @@ "SchedulingStrategy": "REPLICA" }, "DependsOn": [ - "LBPublicListener6E1F3D94", - "LBPublicListenerECSGroupD6A32205" + "LBPublicListenerECSGroupD6A32205", + "LBPublicListener6E1F3D94" ] }, "LB8A12904C": { diff --git a/packages/@aws-cdk/aws-ecs/test/fargate/integ.asset-image.expected.json b/packages/@aws-cdk/aws-ecs/test/fargate/integ.asset-image.expected.json index ddfcc1ebe0501..d1d4f4a7ca4b9 100644 --- a/packages/@aws-cdk/aws-ecs/test/fargate/integ.asset-image.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/fargate/integ.asset-image.expected.json @@ -512,8 +512,8 @@ "Timeout": 300 }, "DependsOn": [ - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17", - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C" + "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", + "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" ] }, "FargateServiceTaskDefTaskRole8CDCF85E": { @@ -838,8 +838,8 @@ } }, "DependsOn": [ - "FargateServiceLBPublicListener4B4929CA", - "FargateServiceLBPublicListenerECSGroupBE57E081" + "FargateServiceLBPublicListenerECSGroupBE57E081", + "FargateServiceLBPublicListener4B4929CA" ] }, "FargateServiceSecurityGroup262B61DD": { diff --git a/packages/@aws-cdk/aws-ecs/test/fargate/integ.l3.expected.json b/packages/@aws-cdk/aws-ecs/test/fargate/integ.l3.expected.json index 2983132345d69..e5416641368ce 100644 --- a/packages/@aws-cdk/aws-ecs/test/fargate/integ.l3.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/fargate/integ.l3.expected.json @@ -525,8 +525,8 @@ } }, "DependsOn": [ - "L3LBPublicListener156FFC0F", - "L3LBPublicListenerECSGroup648EEA11" + "L3LBPublicListenerECSGroup648EEA11", + "L3LBPublicListener156FFC0F" ] }, "L3ServiceSecurityGroup677B0897": { diff --git a/packages/@aws-cdk/aws-ecs/test/fargate/integ.lb-awsvpc-nw.expected.json b/packages/@aws-cdk/aws-ecs/test/fargate/integ.lb-awsvpc-nw.expected.json index 3d277f445d568..39c3355a8e0ae 100644 --- a/packages/@aws-cdk/aws-ecs/test/fargate/integ.lb-awsvpc-nw.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/fargate/integ.lb-awsvpc-nw.expected.json @@ -454,8 +454,8 @@ } }, "DependsOn": [ - "LBPublicListener6E1F3D94", - "LBPublicListenerFargateGroup5EE2FBAF" + "LBPublicListenerFargateGroup5EE2FBAF", + "LBPublicListener6E1F3D94" ] }, "ServiceSecurityGroupC96ED6A7": { diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/test.listener.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/test.listener.ts index 6b08d52acbb51..b60d0db5801f3 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/test.listener.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/test.listener.ts @@ -133,10 +133,10 @@ export = { MyResource: { Type: "Test::Resource", DependsOn: [ - "LBListener49E825B4", // 2nd dependency is there because of the structure of the construct tree. // It does not harm. - "LBListenerGroupGroup79B304FF" + "LBListenerGroupGroup79B304FF", + "LBListener49E825B4", ] } } diff --git a/packages/@aws-cdk/aws-lambda-event-sources/test/integ.dynamodb.expected.json b/packages/@aws-cdk/aws-lambda-event-sources/test/integ.dynamodb.expected.json index 95f12af72cf00..cab31fbca8a38 100644 --- a/packages/@aws-cdk/aws-lambda-event-sources/test/integ.dynamodb.expected.json +++ b/packages/@aws-cdk/aws-lambda-event-sources/test/integ.dynamodb.expected.json @@ -82,8 +82,8 @@ "Runtime": "nodejs8.10" }, "DependsOn": [ - "FServiceRole3AC82EE1", - "FServiceRoleDefaultPolicy17A19BFA" + "FServiceRoleDefaultPolicy17A19BFA", + "FServiceRole3AC82EE1" ] }, "FDynamoDBEventSourcelambdaeventsourcedynamodbT7967476AE652DA48": { diff --git a/packages/@aws-cdk/aws-lambda-event-sources/test/integ.kinesis.expected.json b/packages/@aws-cdk/aws-lambda-event-sources/test/integ.kinesis.expected.json index fdd245f5a7cda..d811834a7f0b1 100644 --- a/packages/@aws-cdk/aws-lambda-event-sources/test/integ.kinesis.expected.json +++ b/packages/@aws-cdk/aws-lambda-event-sources/test/integ.kinesis.expected.json @@ -77,8 +77,8 @@ "Runtime": "nodejs8.10" }, "DependsOn": [ - "FServiceRole3AC82EE1", - "FServiceRoleDefaultPolicy17A19BFA" + "FServiceRoleDefaultPolicy17A19BFA", + "FServiceRole3AC82EE1" ] }, "FKinesisEventSourcelambdaeventsourcekinesisQ645CE7DB2D6BCCF5": { diff --git a/packages/@aws-cdk/aws-lambda-event-sources/test/integ.sqs.expected.json b/packages/@aws-cdk/aws-lambda-event-sources/test/integ.sqs.expected.json index e590deace266b..4975bf693e1fd 100644 --- a/packages/@aws-cdk/aws-lambda-event-sources/test/integ.sqs.expected.json +++ b/packages/@aws-cdk/aws-lambda-event-sources/test/integ.sqs.expected.json @@ -81,8 +81,8 @@ "Runtime": "nodejs8.10" }, "DependsOn": [ - "FServiceRole3AC82EE1", - "FServiceRoleDefaultPolicy17A19BFA" + "FServiceRoleDefaultPolicy17A19BFA", + "FServiceRole3AC82EE1" ] }, "FSqsEventSourcelambdaeventsourcesqsQ67DE9201754EC819": { diff --git a/packages/@aws-cdk/aws-lambda/test/integ.lambda.expected.json b/packages/@aws-cdk/aws-lambda/test/integ.lambda.expected.json index 72c4017892df1..57ec918f05d8f 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.lambda.expected.json +++ b/packages/@aws-cdk/aws-lambda/test/integ.lambda.expected.json @@ -68,8 +68,8 @@ "Runtime": "nodejs6.10" }, "DependsOn": [ - "MyLambdaServiceRole4539ECB6", - "MyLambdaServiceRoleDefaultPolicy5BBC6F68" + "MyLambdaServiceRoleDefaultPolicy5BBC6F68", + "MyLambdaServiceRole4539ECB6" ] }, "MyLambdaVersion16CDE3C40": { @@ -96,4 +96,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-lambda/test/test.lambda.ts b/packages/@aws-cdk/aws-lambda/test/test.lambda.ts index 06dde470520de..83350ce1aad4d 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.lambda.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.lambda.ts @@ -95,7 +95,7 @@ export = { Handler: 'index.handler', Role: { 'Fn::GetAtt': [ 'MyLambdaServiceRole4539ECB6', 'Arn' ] }, Runtime: 'nodejs6.10' }, - DependsOn: [ 'MyLambdaServiceRole4539ECB6', 'MyLambdaServiceRoleDefaultPolicy5BBC6F68' ] } } } ); + DependsOn: [ 'MyLambdaServiceRoleDefaultPolicy5BBC6F68', 'MyLambdaServiceRole4539ECB6' ] } } } ); test.done(); }, @@ -429,8 +429,8 @@ export = { "FunctionName": "OneFunctionToRuleThemAll" }, "DependsOn": [ - "MyLambdaServiceRole4539ECB6", - "MyLambdaServiceRoleDefaultPolicy5BBC6F68" + "MyLambdaServiceRoleDefaultPolicy5BBC6F68", + "MyLambdaServiceRole4539ECB6" ] } } @@ -539,8 +539,8 @@ export = { } }, "DependsOn": [ - "MyLambdaServiceRole4539ECB6", - "MyLambdaServiceRoleDefaultPolicy5BBC6F68" + "MyLambdaServiceRoleDefaultPolicy5BBC6F68", + "MyLambdaServiceRole4539ECB6" ] } } @@ -717,8 +717,8 @@ export = { } }, "DependsOn": [ - "MyLambdaServiceRole4539ECB6", - "MyLambdaServiceRoleDefaultPolicy5BBC6F68" + "MyLambdaServiceRoleDefaultPolicy5BBC6F68", + "MyLambdaServiceRole4539ECB6" ] } } @@ -827,8 +827,8 @@ export = { } }, "DependsOn": [ - "MyLambdaServiceRole4539ECB6", - "MyLambdaServiceRoleDefaultPolicy5BBC6F68" + "MyLambdaServiceRoleDefaultPolicy5BBC6F68", + "MyLambdaServiceRole4539ECB6", ] } } @@ -906,8 +906,8 @@ export = { } }, "DependsOn": [ + "MyLambdaServiceRoleDefaultPolicy5BBC6F68", "MyLambdaServiceRole4539ECB6", - "MyLambdaServiceRoleDefaultPolicy5BBC6F68" ] }, ResourcePart.CompleteDefinition)); @@ -964,8 +964,8 @@ export = { } }, "DependsOn": [ + "MyLambdaServiceRoleDefaultPolicy5BBC6F68", "MyLambdaServiceRole4539ECB6", - "MyLambdaServiceRoleDefaultPolicy5BBC6F68" ] }, ResourcePart.CompleteDefinition)); diff --git a/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment.expected.json b/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment.expected.json index 8a43d05aa3089..6fb73507aebf6 100644 --- a/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment.expected.json +++ b/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment.expected.json @@ -338,8 +338,8 @@ "Timeout": 900 }, "DependsOn": [ - "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265", - "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF" + "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF", + "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265" ] }, "Destination281A09BDF": { @@ -425,4 +425,4 @@ "Description": "S3 key for asset version \"test-bucket-deployments-1/DeployWithPrefix/Asset\"" } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-sns/test/test.sns.ts b/packages/@aws-cdk/aws-sns/test/test.sns.ts index ae9a5921296fe..6befe1dd706e4 100644 --- a/packages/@aws-cdk/aws-sns/test/test.sns.ts +++ b/packages/@aws-cdk/aws-sns/test/test.sns.ts @@ -718,7 +718,8 @@ export = { test.deepEqual(dest1.type, s3n.BucketNotificationDestinationType.Topic); const dep: cdk.Construct = dest1.dependencies![0] as any; - test.deepEqual((dep.node.children[0] as any).logicalId, 'MyTopicPolicy12A5EC17', 'verify topic policy is added as dependency'); + test.deepEqual(stack.node.resolve((dep.node.children[0] as any).logicalId), + 'MyTopicPolicy12A5EC17', 'verify topic policy is added as dependency'); // calling again on the same bucket yields is idempotent const dest2 = topic.asBucketNotificationDestination(bucketArn, bucketId); diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/cfn-tokens.ts b/packages/@aws-cdk/cdk/lib/cloudformation/cfn-tokens.ts index bdce6482edfb2..1646df2d46288 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/cfn-tokens.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/cfn-tokens.ts @@ -38,6 +38,10 @@ export class CfnReference extends Token { if (typeof(value) === 'function') { throw new Error('CfnReference can only hold CloudFormation intrinsics (not a function)'); } + // prepend scope path to display name + if (displayName && scope) { + displayName = `${scope.node.path}.${displayName}`; + } super(value, displayName); this.replacementTokens = new Map(); this.isReference = true; diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/resource.ts b/packages/@aws-cdk/cdk/lib/cloudformation/resource.ts index 79ef4aec3bd97..a30008236a3b5 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/resource.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/resource.ts @@ -98,7 +98,7 @@ export class Resource extends Referenceable { * * Is filled during prepare(). */ - private readonly dependsOn = new Set(); + private readonly dependsOn = new Set(); /** * Creates a resource construct. @@ -131,7 +131,7 @@ export class Resource extends Referenceable { * @param attributeName The name of the attribute. */ public getAtt(attributeName: string) { - return new CfnReference({ 'Fn::GetAtt': [this.logicalId, attributeName] }, `${this.logicalId}.${attributeName}`, this); + return new CfnReference({ 'Fn::GetAtt': [this.logicalId, attributeName] }, attributeName, this); } /** @@ -193,8 +193,12 @@ export class Resource extends Referenceable { this.addPropertyOverride(propertyPath, undefined); } + /** + * Indicates that this resource depends on another resource and cannot be provisioned + * unless the other resource has been successfully provisioned. + */ public addDependsOn(resource: Resource) { - this.dependsOn.add(resource.logicalId); + this.dependsOn.add(resource); } /** @@ -215,7 +219,7 @@ export class Resource extends Referenceable { Type: this.resourceType, Properties: ignoreEmpty(this, properties), // Return a sorted set of dependencies to be consistent across tests - DependsOn: ignoreEmpty(this, sortedSet(this.dependsOn)), + DependsOn: ignoreEmpty(this, renderDependsOn(this.dependsOn)), CreationPolicy: capitalizePropertyNames(this, this.options.creationPolicy), UpdatePolicy: capitalizePropertyNames(this, this.options.updatePolicy), UpdateReplacePolicy: capitalizePropertyNames(this, this.options.updateReplacePolicy), @@ -235,6 +239,15 @@ export class Resource extends Referenceable { // Re-throw throw e; } + + // returns the set of logical ID (tokens) this resource depends on + // sorted by construct paths to ensure test determinism + function renderDependsOn(dependsOn: Set) { + return Array + .from(dependsOn) + .sort((x, y) => x.node.path.localeCompare(y.node.path)) + .map(r => r.logicalId); + } } protected renderProperties(properties: any): { [key: string]: any } { @@ -330,8 +343,3 @@ export function deepMerge(target: any, source: any) { return target; } -function sortedSet(xs: Set): T[] { - const ret = Array.from(xs); - ret.sort(); - return ret; -} diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/stack-element.ts b/packages/@aws-cdk/cdk/lib/cloudformation/stack-element.ts index e4c1fc26daaec..563cd5cb050c7 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/stack-element.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/stack-element.ts @@ -1,4 +1,5 @@ import { Construct, IConstruct, PATH_SEP } from "../core/construct"; +import { Token } from '../core/tokens'; const LOGICAL_ID_MD = 'aws:cdk:logicalId'; @@ -15,16 +16,18 @@ export abstract class StackElement extends Construct { * * @returns The construct as a stack element or undefined if it is not a stack element. */ - public static _asStackElement(construct: IConstruct): StackElement | undefined { - if ('logicalId' in construct && 'toCloudFormation' in construct) { - return construct as StackElement; - } else { - return undefined; - } + public static isStackElement(construct: IConstruct): construct is StackElement { + return ('logicalId' in construct && 'toCloudFormation' in construct); } /** - * The logical ID for this CloudFormation stack element + * The logical ID for this CloudFormation stack element. The logical ID of the element + * is calculated from the path of the resource node in the construct tree. + * + * To override this value, use `overrideLogicalId(newLogicalId)`. + * + * @returns the logical ID as a stringified token. This value will only get + * resolved during synthesis. */ public readonly logicalId: string; @@ -33,6 +36,8 @@ export abstract class StackElement extends Construct { */ protected stack: Stack; + private _logicalId: string; + /** * Creates an entity and binds it to a tree. * Note that the root of the tree must be a Stack object (not just any Root). @@ -50,7 +55,16 @@ export abstract class StackElement extends Construct { this.node.addMetadata(LOGICAL_ID_MD, new (require("../core/tokens/token").Token)(() => this.logicalId), this.constructor); - this.logicalId = this.stack.logicalIds.getLogicalId(this); + this._logicalId = this.stack.logicalIds.getLogicalId(this); + this.logicalId = new Token(() => this._logicalId).toString(); + } + + /** + * Overrides the auto-generated logical ID with a specific ID. + * @param newLogicalId The new logical ID to use for this stack element. + */ + public overrideLogicalId(newLogicalId: string) { + this._logicalId = newLogicalId; } /** @@ -127,7 +141,7 @@ import { CfnReference } from "./cfn-tokens"; */ export class Ref extends CfnReference { constructor(element: StackElement) { - super({ Ref: element.logicalId }, `${element.logicalId}.Ref`, element); + super({ Ref: element.logicalId }, 'Ref', element); } } diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/stack.ts b/packages/@aws-cdk/cdk/lib/cloudformation/stack.ts index f8ed92b837c7d..82fc3e198fc98 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/stack.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/stack.ts @@ -169,7 +169,7 @@ export class Stack extends Construct { }; const elements = stackElements(this); - const fragments = elements.map(e => e.toCloudFormation()); + const fragments = elements.map(e => this.node.resolve(e.toCloudFormation())); // merge in all CloudFormation fragments collected from the tree for (const fragment of fragments) { @@ -521,9 +521,8 @@ export interface TemplateOptions { * @returns The same array as is being collected into */ function stackElements(node: IConstruct, into: StackElement[] = []): StackElement[] { - const element = StackElement._asStackElement(node); - if (element) { - into.push(element); + if (StackElement.isStackElement(node)) { + into.push(node); } for (const child of node.node.children) { diff --git a/packages/@aws-cdk/cdk/lib/core/tokens/encoding.ts b/packages/@aws-cdk/cdk/lib/core/tokens/encoding.ts index 301698214dbb9..29ba49bee36d2 100644 --- a/packages/@aws-cdk/cdk/lib/core/tokens/encoding.ts +++ b/packages/@aws-cdk/cdk/lib/core/tokens/encoding.ts @@ -95,13 +95,8 @@ export class TokenMap { private register(token: Token, representationHint?: string): string { const counter = Object.keys(this.tokenMap).length; - const representation = representationHint || `TOKEN`; - + const representation = (representationHint || `TOKEN`).replace(new RegExp(`[^${VALID_KEY_CHARS}]`, 'g'), '.'); const key = `${representation}.${counter}`; - if (new RegExp(`[^${VALID_KEY_CHARS}]`).exec(key)) { - throw new Error(`Invalid characters in token representation: ${key}`); - } - this.tokenMap[key] = token; return key; } diff --git a/packages/@aws-cdk/cdk/test/cloudformation/test.logical-id.ts b/packages/@aws-cdk/cdk/test/cloudformation/test.logical-id.ts index 7c63ac07ea106..9fd0ecba1ebe8 100644 --- a/packages/@aws-cdk/cdk/test/cloudformation/test.logical-id.ts +++ b/packages/@aws-cdk/cdk/test/cloudformation/test.logical-id.ts @@ -29,7 +29,7 @@ const uniqueTests = { const r = new Resource(stack, 'MyAwesomeness', { type: 'Resource' }); // THEN - test.equal(r.logicalId, 'MyAwesomeness'); + test.equal(stack.node.resolve(r.logicalId), 'MyAwesomeness'); test.done(); }, @@ -204,13 +204,13 @@ const allSchemesTests: {[name: string]: (scheme: IAddressingScheme, test: Test) stack.node.prepareTree(); test.deepEqual(stack.toCloudFormation(), { Resources: { - [c1.logicalId]: { + NewName: { Type: 'R1' }, - [c2.logicalId]: { + Construct2: { Type: 'R2', Properties: { - ReferenceToR1: { Ref: c1.logicalId } }, - DependsOn: [ c1.logicalId ] } } }); + ReferenceToR1: { Ref: 'NewName' } }, + DependsOn: [ 'NewName' ] } } }); test.done(); }, diff --git a/packages/@aws-cdk/cdk/test/cloudformation/test.output.ts b/packages/@aws-cdk/cdk/test/cloudformation/test.output.ts index 17a4136a1ea71..7ffbbb7c66014 100644 --- a/packages/@aws-cdk/cdk/test/cloudformation/test.output.ts +++ b/packages/@aws-cdk/cdk/test/cloudformation/test.output.ts @@ -43,7 +43,7 @@ export = { test.done(); }, - 'is stack name is undefined, we will only use the logical ID for the export name'(test: Test) { + 'if stack name is undefined, we will only use the logical ID for the export name'(test: Test) { const stack = new Stack(); const output = new Output(stack, 'MyOutput'); test.deepEqual(stack.node.resolve(output.makeImportValue()), { 'Fn::ImportValue': 'MyOutput' }); @@ -83,4 +83,4 @@ export = { test.done(); }, -}; \ No newline at end of file +}; diff --git a/packages/@aws-cdk/cdk/test/cloudformation/test.parameter.ts b/packages/@aws-cdk/cdk/test/cloudformation/test.parameter.ts index 2a5526c4255f2..2424939dccbac 100644 --- a/packages/@aws-cdk/cdk/test/cloudformation/test.parameter.ts +++ b/packages/@aws-cdk/cdk/test/cloudformation/test.parameter.ts @@ -16,14 +16,14 @@ export = { test.deepEqual(stack.toCloudFormation(), { Parameters: { - [param.logicalId]: { + ChildMyParam3161BF5D: { Default: 10, Type: 'Integer', Description: 'My first parameter' } }, Resources: { Resource: { Type: 'Type', - Properties: { ReferenceToParam: { Ref: param.logicalId } } } } }); + Properties: { ReferenceToParam: { Ref: 'ChildMyParam3161BF5D' } } } } }); test.done(); }, diff --git a/packages/@aws-cdk/cdk/test/cloudformation/test.resource.ts b/packages/@aws-cdk/cdk/test/cloudformation/test.resource.ts index 27b08e598343e..4160199108fdd 100644 --- a/packages/@aws-cdk/cdk/test/cloudformation/test.resource.ts +++ b/packages/@aws-cdk/cdk/test/cloudformation/test.resource.ts @@ -44,8 +44,8 @@ export = { const res1 = new Resource(level1, 'childoflevel1', { type: 'MyResourceType1' }); const res2 = new Resource(level3, 'childoflevel3', { type: 'MyResourceType2' }); - test.equal(withoutHash(res1.logicalId), 'level1childoflevel1'); - test.equal(withoutHash(res2.logicalId), 'level1level2level3childoflevel3'); + test.equal(withoutHash(stack.node.resolve(res1.logicalId)), 'level1childoflevel1'); + test.equal(withoutHash(stack.node.resolve(res2.logicalId)), 'level1level2level3childoflevel3'); test.done(); }, @@ -152,6 +152,39 @@ export = { test.done(); }, + 'if addDependency is called multiple times with the same resource, it will only appear once'(test: Test) { + // GIVEN + const stack = new Stack(); + const r1 = new Counter(stack, 'Counter1', { Count: 1 }); + const dependent = new Resource(stack, 'Dependent', { type: 'R' }); + + // WHEN + dependent.addDependsOn(r1); + dependent.addDependsOn(r1); + dependent.addDependsOn(r1); + dependent.addDependsOn(r1); + dependent.addDependsOn(r1); + + // THEN + test.deepEqual(stack.toCloudFormation(), { + Resources: { + Counter1: { + Type: "My::Counter", + Properties: { + Count: 1 + } + }, + Dependent: { + Type: "R", + DependsOn: [ + "Counter1" + ] + } + } + }); + test.done(); + }, + 'conditions can be attached to a resource'(test: Test) { const stack = new Stack(); const r1 = new Resource(stack, 'Resource', { type: 'Type' }); diff --git a/packages/@aws-cdk/cdk/test/cloudformation/test.stack.ts b/packages/@aws-cdk/cdk/test/cloudformation/test.stack.ts index 880068e92d589..d56bedb34896d 100644 --- a/packages/@aws-cdk/cdk/test/cloudformation/test.stack.ts +++ b/packages/@aws-cdk/cdk/test/cloudformation/test.stack.ts @@ -345,6 +345,30 @@ export = { test.done(); }, + + 'overrideLogicalId(id) can be used to override the logical ID of a resource'(test: Test) { + // GIVEN + const stack = new Stack(); + const bonjour = new Resource(stack, 'BonjourResource', { type: 'Resource::Type' }); + + // { Ref } and { GetAtt } + new Resource(stack, 'RefToBonjour', { type: 'Other::Resource', properties: { + RefToBonjour: bonjour.ref.toString(), + GetAttBonjour: bonjour.getAtt('TheAtt').toString() + }}); + + bonjour.overrideLogicalId('BOOM'); + + // THEN + test.deepEqual(stack.toCloudFormation(), { Resources: + { BOOM: { Type: 'Resource::Type' }, + RefToBonjour: + { Type: 'Other::Resource', + Properties: + { RefToBonjour: { Ref: 'BOOM' }, + GetAttBonjour: { 'Fn::GetAtt': [ 'BOOM', 'TheAtt' ] } } } } }); + test.done(); + } }; class StackWithPostProcessor extends Stack { diff --git a/packages/@aws-cdk/runtime-values/test/integ.rtv.lambda.expected.json b/packages/@aws-cdk/runtime-values/test/integ.rtv.lambda.expected.json index 850eb3247efed..cd55408e3416c 100644 --- a/packages/@aws-cdk/runtime-values/test/integ.rtv.lambda.expected.json +++ b/packages/@aws-cdk/runtime-values/test/integ.rtv.lambda.expected.json @@ -105,8 +105,8 @@ } }, "DependsOn": [ - "MyFunctionServiceRole3C357FF2", - "MyFunctionServiceRoleDefaultPolicyB705ABD4" + "MyFunctionServiceRoleDefaultPolicyB705ABD4", + "MyFunctionServiceRole3C357FF2" ] }, "MyQueueURLParameterA4918D6E": {