From a46cfd846fcb26a0eb25a26976a8c1ce7305e914 Mon Sep 17 00:00:00 2001 From: Romain Marcadier-Muller Date: Wed, 13 Feb 2019 17:03:55 +0100 Subject: [PATCH] feat(core): Add `construct.node.stack` attribute (#1753) Makes it easier to access some construct's `Stack`, avoiding having to sprinkle `Stack.find(this)` everywhere. BREAKING CHANGE: `Stack.find(c)` and `Stack.tryFind(c)` were replaced by `c.node.stack`. Fixes #798 --- .../lib/pipeline-deploy-stack-action.ts | 2 +- .../aws-apigateway/lib/integrations/aws.ts | 2 +- .../@aws-cdk/aws-apigateway/lib/method.ts | 2 +- .../@aws-cdk/aws-apigateway/lib/restapi.ts | 4 +- packages/@aws-cdk/aws-apigateway/lib/stage.ts | 4 +- .../lib/pipeline-actions.ts | 2 +- .../test/test.pipeline-actions.ts | 4 +- packages/@aws-cdk/aws-cloudtrail/lib/index.ts | 4 +- .../@aws-cdk/aws-cloudwatch/lib/dashboard.ts | 5 +- .../@aws-cdk/aws-codebuild/lib/project.ts | 4 +- .../@aws-cdk/aws-codecommit/lib/repository.ts | 7 ++- .../lib/server/deployment-group.ts | 8 ++- packages/@aws-cdk/aws-codedeploy/lib/utils.ts | 6 +-- .../@aws-cdk/aws-codepipeline/lib/pipeline.ts | 11 ++-- packages/@aws-cdk/aws-dynamodb/lib/table.ts | 2 +- .../@aws-cdk/aws-ec2/lib/machine-image.ts | 5 +- .../@aws-cdk/aws-ec2/lib/security-group.ts | 4 +- packages/@aws-cdk/aws-ec2/lib/vpc-ref.ts | 6 +-- .../@aws-cdk/aws-ecr/lib/repository-ref.ts | 4 +- .../@aws-cdk/aws-ecs/lib/base/base-service.ts | 2 +- packages/@aws-cdk/aws-ecs/lib/cluster.ts | 2 +- .../aws-ecs/lib/log-drivers/aws-log-driver.ts | 3 +- .../lib/alb/application-load-balancer.ts | 4 +- .../@aws-cdk/aws-iam/lib/managed-policy.ts | 2 +- .../@aws-cdk/aws-iam/lib/policy-document.ts | 5 +- packages/@aws-cdk/aws-iam/lib/role.ts | 4 +- packages/@aws-cdk/aws-kinesis/lib/stream.ts | 13 +++-- .../@aws-cdk/aws-lambda/lib/function-base.ts | 8 ++- packages/@aws-cdk/aws-lambda/lib/function.ts | 6 +-- packages/@aws-cdk/aws-lambda/lib/layers.ts | 5 +- .../aws-lambda/lib/singleton-lambda.ts | 5 +- .../aws-logs/lib/cross-account-destination.ts | 3 +- packages/@aws-cdk/aws-logs/lib/log-group.ts | 2 +- packages/@aws-cdk/aws-s3/lib/bucket.ts | 7 ++- .../notifications-resource-handler.ts | 4 +- packages/@aws-cdk/aws-s3/lib/util.ts | 4 +- .../@aws-cdk/aws-secretsmanager/lib/secret.ts | 2 +- packages/@aws-cdk/aws-sqs/lib/queue.ts | 2 +- packages/@aws-cdk/aws-sqs/package-lock.json | 2 +- packages/@aws-cdk/aws-ssm/lib/parameter.ts | 2 +- .../aws-stepfunctions/lib/state-machine.ts | 3 +- .../cdk/lib/cloudformation/cfn-tokens.ts | 4 +- .../@aws-cdk/cdk/lib/cloudformation/output.ts | 4 +- .../cdk/lib/cloudformation/stack-element.ts | 15 +----- .../@aws-cdk/cdk/lib/cloudformation/stack.ts | 29 +---------- packages/@aws-cdk/cdk/lib/context.ts | 20 +++----- packages/@aws-cdk/cdk/lib/core/construct.ts | 23 +++++++++ .../cdk/test/cloudformation/test.stack.ts | 23 +-------- .../@aws-cdk/cdk/test/core/test.construct.ts | 50 +++++++++++++++++-- packages/@aws-cdk/runtime-values/lib/rtv.ts | 8 ++- .../@aws-cdk/runtime-values/test/test.rtv.ts | 4 +- tools/cfn2ts/lib/codegen.ts | 6 +-- tools/decdk/package.json | 2 +- 53 files changed, 171 insertions(+), 193 deletions(-) diff --git a/packages/@aws-cdk/app-delivery/lib/pipeline-deploy-stack-action.ts b/packages/@aws-cdk/app-delivery/lib/pipeline-deploy-stack-action.ts index 15b7d823072a0..9e4632ae4c9c1 100644 --- a/packages/@aws-cdk/app-delivery/lib/pipeline-deploy-stack-action.ts +++ b/packages/@aws-cdk/app-delivery/lib/pipeline-deploy-stack-action.ts @@ -104,7 +104,7 @@ export class PipelineDeployStackAction extends cdk.Construct { constructor(scope: cdk.Construct, id: string, props: PipelineDeployStackActionProps) { super(scope, id); - if (!cdk.environmentEquals(props.stack.env, cdk.Stack.find(this).env)) { + if (!cdk.environmentEquals(props.stack.env, this.node.stack.env)) { // FIXME: Add the necessary to extend to stacks in a different account throw new Error(`Cross-environment deployment is not supported`); } diff --git a/packages/@aws-cdk/aws-apigateway/lib/integrations/aws.ts b/packages/@aws-cdk/aws-apigateway/lib/integrations/aws.ts index 341bbc88b40fb..a4fe394384033 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/integrations/aws.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/integrations/aws.ts @@ -73,7 +73,7 @@ export class AwsIntegration extends Integration { integrationHttpMethod: 'POST', uri: new cdk.Token(() => { if (!this.scope) { throw new Error('AwsIntegration must be used in API'); } - return cdk.Stack.find(this.scope).formatArn({ + return this.scope.node.stack.formatArn({ service: 'apigateway', account: backend, resource: apiType, diff --git a/packages/@aws-cdk/aws-apigateway/lib/method.ts b/packages/@aws-cdk/aws-apigateway/lib/method.ts index 844a160b77ca8..7375c8b51cf08 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/method.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/method.ts @@ -189,7 +189,7 @@ export class Method extends cdk.Construct { } else if (options.credentialsPassthrough) { // arn:aws:iam::*:user/* // tslint:disable-next-line:max-line-length - credentials = cdk.Stack.find(this).formatArn({ service: 'iam', region: '', account: '*', resource: 'user', sep: '/', resourceName: '*' }); + credentials = this.node.stack.formatArn({ service: 'iam', region: '', account: '*', resource: 'user', sep: '/', resourceName: '*' }); } return { diff --git a/packages/@aws-cdk/aws-apigateway/lib/restapi.ts b/packages/@aws-cdk/aws-apigateway/lib/restapi.ts index b6183cc2bc968..b964ca3bd2c9f 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/restapi.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/restapi.ts @@ -271,7 +271,7 @@ export class RestApi extends cdk.Construct implements IRestApi { method = '*'; } - return cdk.Stack.find(this).formatArn({ + return this.node.stack.formatArn({ service: 'execute-api', resource: this.restApiId, sep: '/', @@ -328,7 +328,7 @@ export class RestApi extends cdk.Construct implements IRestApi { private configureCloudWatchRole(apiResource: CfnRestApi) { const role = new iam.Role(this, 'CloudWatchRole', { assumedBy: new iam.ServicePrincipal('apigateway.amazonaws.com'), - managedPolicyArns: [ cdk.Stack.find(this).formatArn({ + managedPolicyArns: [ this.node.stack.formatArn({ service: 'iam', region: '', account: 'aws', diff --git a/packages/@aws-cdk/aws-apigateway/lib/stage.ts b/packages/@aws-cdk/aws-apigateway/lib/stage.ts index 5907d12f435f1..26a1cef90447d 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/stage.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/stage.ts @@ -1,5 +1,4 @@ import cdk = require('@aws-cdk/cdk'); -import { Stack } from '@aws-cdk/cdk'; import { CfnStage } from './apigateway.generated'; import { Deployment } from './deployment'; import { IRestApi } from './restapi'; @@ -179,8 +178,7 @@ export class Stage extends cdk.Construct { if (!path.startsWith('/')) { throw new Error(`Path must begin with "/": ${path}`); } - const stack = Stack.find(this); - return `https://${this.restApi.restApiId}.execute-api.${stack.region}.${stack.urlSuffix}/${this.stageName}${path}`; + return `https://${this.restApi.restApiId}.execute-api.${this.node.stack.region}.${this.node.stack.urlSuffix}/${this.stageName}${path}`; } private renderMethodSettings(props: StageProps): CfnStage.MethodSettingProperty[] | undefined { diff --git a/packages/@aws-cdk/aws-cloudformation/lib/pipeline-actions.ts b/packages/@aws-cdk/aws-cloudformation/lib/pipeline-actions.ts index ea5e9c91772b1..3f731b11d1d3d 100644 --- a/packages/@aws-cdk/aws-cloudformation/lib/pipeline-actions.ts +++ b/packages/@aws-cdk/aws-cloudformation/lib/pipeline-actions.ts @@ -544,7 +544,7 @@ class SingletonPolicy extends cdk.Construct { } private stackArnFromProps(props: { stackName: string, region?: string }): string { - return cdk.Stack.find(this).formatArn({ + return this.node.stack.formatArn({ region: props.region, service: 'cloudformation', resource: 'stack', diff --git a/packages/@aws-cdk/aws-cloudformation/test/test.pipeline-actions.ts b/packages/@aws-cdk/aws-cloudformation/test/test.pipeline-actions.ts index f22933e9ad784..9f0512875f118 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/test.pipeline-actions.ts +++ b/packages/@aws-cdk/aws-cloudformation/test/test.pipeline-actions.ts @@ -293,7 +293,7 @@ function _isOrContains(entity: string | string[], value: string): boolean { } function _stackArn(stackName: string, scope: cdk.IConstruct): string { - return cdk.Stack.find(scope).formatArn({ + return scope.node.stack.formatArn({ service: 'cloudformation', resource: 'stack', resourceName: `${stackName}/*`, @@ -308,7 +308,7 @@ class PipelineDouble extends cdk.Construct implements cpapi.IPipeline { constructor(scope: cdk.Construct, id: string, { pipelineName, role }: { pipelineName?: string, role: iam.Role }) { super(scope, id); this.pipelineName = pipelineName || 'TestPipeline'; - this.pipelineArn = cdk.Stack.find(this).formatArn({ service: 'codepipeline', resource: 'pipeline', resourceName: this.pipelineName }); + this.pipelineArn = this.node.stack.formatArn({ service: 'codepipeline', resource: 'pipeline', resourceName: this.pipelineName }); this.role = role; } diff --git a/packages/@aws-cdk/aws-cloudtrail/lib/index.ts b/packages/@aws-cdk/aws-cloudtrail/lib/index.ts index 1b7bedf020848..6a2cc83ae06c7 100644 --- a/packages/@aws-cdk/aws-cloudtrail/lib/index.ts +++ b/packages/@aws-cdk/aws-cloudtrail/lib/index.ts @@ -132,15 +132,13 @@ export class CloudTrail extends cdk.Construct { const s3bucket = new s3.Bucket(this, 'S3', {encryption: s3.BucketEncryption.Unencrypted}); const cloudTrailPrincipal = "cloudtrail.amazonaws.com"; - const stack = cdk.Stack.find(this); - s3bucket.addToResourcePolicy(new iam.PolicyStatement() .addResource(s3bucket.bucketArn) .addActions('s3:GetBucketAcl') .addServicePrincipal(cloudTrailPrincipal)); s3bucket.addToResourcePolicy(new iam.PolicyStatement() - .addResource(s3bucket.arnForObjects(`AWSLogs/${stack.accountId}/*`)) + .addResource(s3bucket.arnForObjects(`AWSLogs/${this.node.stack.accountId}/*`)) .addActions("s3:PutObject") .addServicePrincipal(cloudTrailPrincipal) .setCondition("StringEquals", {'s3:x-amz-acl': "bucket-owner-full-control"})); diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/dashboard.ts b/packages/@aws-cdk/aws-cloudwatch/lib/dashboard.ts index 3974af3f5caf2..195d1babf382a 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/dashboard.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/dashboard.ts @@ -1,4 +1,4 @@ -import { Construct, Stack, Token } from "@aws-cdk/cdk"; +import { Construct, Token } from "@aws-cdk/cdk"; import { CfnDashboard } from './cloudwatch.generated'; import { Column, Row } from "./layout"; import { IWidget } from "./widget"; @@ -61,7 +61,6 @@ export class Dashboard extends Construct { */ private generateDashboardName(): string { // Combination of stack name and LogicalID, which are guaranteed to be unique. - const stack = Stack.find(this); - return stack.name + '-' + this.dashboard.logicalId; + return this.node.stack.name + '-' + this.dashboard.logicalId; } } diff --git a/packages/@aws-cdk/aws-codebuild/lib/project.ts b/packages/@aws-cdk/aws-codebuild/lib/project.ts index 6ad303d4a84ff..d81ddc85843c2 100644 --- a/packages/@aws-cdk/aws-codebuild/lib/project.ts +++ b/packages/@aws-cdk/aws-codebuild/lib/project.ts @@ -409,7 +409,7 @@ class ImportedProject extends ProjectBase { constructor(scope: cdk.Construct, id: string, private readonly props: ProjectImportProps) { super(scope, id); - this.projectArn = cdk.Stack.find(this).formatArn({ + this.projectArn = this.node.stack.formatArn({ service: 'codebuild', resource: 'project', resourceName: props.projectName, @@ -744,7 +744,7 @@ export class Project extends ProjectBase { } private createLoggingPermission() { - const logGroupArn = cdk.Stack.find(this).formatArn({ + const logGroupArn = this.node.stack.formatArn({ service: 'logs', resource: 'log-group', sep: ':', diff --git a/packages/@aws-cdk/aws-codecommit/lib/repository.ts b/packages/@aws-cdk/aws-codecommit/lib/repository.ts index 1db388314c7bd..3092d5e4282b5 100644 --- a/packages/@aws-cdk/aws-codecommit/lib/repository.ts +++ b/packages/@aws-cdk/aws-codecommit/lib/repository.ts @@ -230,7 +230,7 @@ class ImportedRepository extends RepositoryBase { constructor(scope: cdk.Construct, id: string, private readonly props: RepositoryImportProps) { super(scope, id); - this.repositoryArn = cdk.Stack.find(this).formatArn({ + this.repositoryArn = this.node.stack.formatArn({ service: 'codecommit', resource: props.repositoryName, }); @@ -249,9 +249,8 @@ class ImportedRepository extends RepositoryBase { return this.repositoryCloneUrl('ssh'); } - private repositoryCloneUrl(protocol: 'https' | 'ssh'): string { - const stack = cdk.Stack.find(this); - return `${protocol}://git-codecommit.${stack.region}.${stack.urlSuffix}/v1/repos/${this.repositoryName}`; + private repositoryCloneUrl(protocol: 'https' | 'ssh'): string { + return `${protocol}://git-codecommit.${this.node.stack.region}.${this.node.stack.urlSuffix}/v1/repos/${this.repositoryName}`; } } diff --git a/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-group.ts b/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-group.ts index 6460a847bc125..a86328ec92762 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-group.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-group.ts @@ -282,9 +282,8 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupBase { this._autoScalingGroups = props.autoScalingGroups || []; this.installAgent = props.installAgent === undefined ? true : props.installAgent; - const stack = cdk.Stack.find(this); this.codeDeployBucket = s3.Bucket.import(this, 'CodeDeployBucket', { - bucketName: `aws-codedeploy-${stack.region}`, + bucketName: `aws-codedeploy-${this.node.stack.region}`, }); for (const asg of this._autoScalingGroups) { this.addCodeDeployAgentInstallUserData(asg); @@ -359,7 +358,6 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupBase { this.codeDeployBucket.grantRead(asg.role, 'latest/*'); - const stack = cdk.Stack.find(this); switch (asg.osType) { case ec2.OperatingSystemType.Linux: asg.addUserData( @@ -377,7 +375,7 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupBase { '$PKG_CMD install -y awscli', 'TMP_DIR=`mktemp -d`', 'cd $TMP_DIR', - `aws s3 cp s3://aws-codedeploy-${stack.region}/latest/install . --region ${stack.region}`, + `aws s3 cp s3://aws-codedeploy-${this.node.stack.region}/latest/install . --region ${this.node.stack.region}`, 'chmod +x ./install', './install auto', 'rm -fr $TMP_DIR', @@ -386,7 +384,7 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupBase { case ec2.OperatingSystemType.Windows: asg.addUserData( 'Set-Variable -Name TEMPDIR -Value (New-TemporaryFile).DirectoryName', - `aws s3 cp s3://aws-codedeploy-${stack.region}/latest/codedeploy-agent.msi $TEMPDIR\\codedeploy-agent.msi`, + `aws s3 cp s3://aws-codedeploy-${this.node.stack.region}/latest/codedeploy-agent.msi $TEMPDIR\\codedeploy-agent.msi`, '$TEMPDIR\\codedeploy-agent.msi /quiet /l c:\\temp\\host-agent-install-log.txt', ); break; diff --git a/packages/@aws-cdk/aws-codedeploy/lib/utils.ts b/packages/@aws-cdk/aws-codedeploy/lib/utils.ts index 6c54dad262fab..12fc92475c295 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/utils.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/utils.ts @@ -4,7 +4,7 @@ import { CfnDeploymentGroup } from './codedeploy.generated'; import { AutoRollbackConfig } from './rollback-config'; export function applicationNameToArn(applicationName: string, scope: cdk.IConstruct): string { - return cdk.Stack.find(scope).formatArn({ + return scope.node.stack.formatArn({ service: 'codedeploy', resource: 'application', resourceName: applicationName, @@ -13,7 +13,7 @@ export function applicationNameToArn(applicationName: string, scope: cdk.IConstr } export function deploymentGroupNameToArn(applicationName: string, deploymentGroupName: string, scope: cdk.IConstruct): string { - return cdk.Stack.find(scope).formatArn({ + return scope.node.stack.formatArn({ service: 'codedeploy', resource: 'deploymentgroup', resourceName: `${applicationName}/${deploymentGroupName}`, @@ -22,7 +22,7 @@ export function deploymentGroupNameToArn(applicationName: string, deploymentGrou } export function arnForDeploymentConfigName(name: string, scope: cdk.IConstruct): string { - return cdk.Stack.find(scope).formatArn({ + return scope.node.stack.formatArn({ service: 'codedeploy', resource: 'deploymentconfig', resourceName: name, diff --git a/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts b/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts index c439bd094fe89..add15cb903ab4 100644 --- a/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts +++ b/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts @@ -188,7 +188,7 @@ export class Pipeline extends cdk.Construct implements cpapi.IPipeline { this.artifactStores = {}; // Does not expose a Fn::GetAtt for the ARN so we'll have to make it ourselves - this.pipelineArn = cdk.Stack.find(this).formatArn({ + this.pipelineArn = this.node.stack.formatArn({ service: 'codepipeline', resource: this.pipelineName }); @@ -336,8 +336,7 @@ export class Pipeline extends cdk.Construct implements cpapi.IPipeline { } // get the region the Pipeline itself is in - const pipelineStack = cdk.Stack.find(this); - const pipelineRegion = pipelineStack.requireRegion( + const pipelineRegion = this.node.stack.requireRegion( "You need to specify an explicit region when using CodePipeline's cross-region support"); // if we already have an ArtifactStore generated for this region, or it's the Pipeline's region, nothing to do @@ -347,9 +346,9 @@ export class Pipeline extends cdk.Construct implements cpapi.IPipeline { let replicationBucketName = this.crossRegionReplicationBuckets[action.region]; if (!replicationBucketName) { - const pipelineAccount = pipelineStack.requireAccountId( + const pipelineAccount = this.node.stack.requireAccountId( "You need to specify an explicit account when using CodePipeline's cross-region support"); - const app = pipelineStack.parentApp(); + const app = this.node.stack.parentApp(); if (!app) { throw new Error(`Pipeline stack which uses cross region actions must be part of an application`); } @@ -477,7 +476,7 @@ export class Pipeline extends cdk.Construct implements cpapi.IPipeline { // add the Pipeline's artifact store const artifactStore = this.renderArtifactStore(); - this.artifactStores[cdk.Stack.find(this).requireRegion()] = { + this.artifactStores[this.node.stack.requireRegion()] = { Location: artifactStore.location, Type: artifactStore.type, EncryptionKey: artifactStore.encryptionKey, diff --git a/packages/@aws-cdk/aws-dynamodb/lib/table.ts b/packages/@aws-cdk/aws-dynamodb/lib/table.ts index af3876030f930..2279e3c24bff6 100644 --- a/packages/@aws-cdk/aws-dynamodb/lib/table.ts +++ b/packages/@aws-cdk/aws-dynamodb/lib/table.ts @@ -622,7 +622,7 @@ export class Table extends Construct { private makeScalingRole(): iam.IRole { // Use a Service Linked Role. return iam.Role.import(this, 'ScalingRole', { - roleArn: cdk.Stack.find(this).formatArn({ + roleArn: this.node.stack.formatArn({ // https://docs.aws.amazon.com/autoscaling/application/userguide/application-auto-scaling-service-linked-roles.html service: 'iam', resource: 'role/aws-service-role/dynamodb.application-autoscaling.amazonaws.com', diff --git a/packages/@aws-cdk/aws-ec2/lib/machine-image.ts b/packages/@aws-cdk/aws-ec2/lib/machine-image.ts index 2e457030d2682..549d4e523bbb6 100644 --- a/packages/@aws-cdk/aws-ec2/lib/machine-image.ts +++ b/packages/@aws-cdk/aws-ec2/lib/machine-image.ts @@ -1,4 +1,4 @@ -import { Construct, SSMParameterProvider, Stack } from '@aws-cdk/cdk'; +import { Construct, SSMParameterProvider } from '@aws-cdk/cdk'; /** * Interface for classes that can select an appropriate machine image to use @@ -188,8 +188,7 @@ export class GenericLinuxImage implements IMachineImageSource { } public getImage(scope: Construct): MachineImage { - const stack = Stack.find(scope); - const region = stack.requireRegion('AMI cannot be determined'); + const region = scope.node.stack.requireRegion('AMI cannot be determined'); const ami = region !== 'test-region' ? this.amiMap[region] : 'ami-12345'; if (!ami) { throw new Error(`Unable to find AMI in AMI map: no AMI specified for region '${region}'`); diff --git a/packages/@aws-cdk/aws-ec2/lib/security-group.ts b/packages/@aws-cdk/aws-ec2/lib/security-group.ts index 15fe5acd73fbf..8cfd3471df83e 100644 --- a/packages/@aws-cdk/aws-ec2/lib/security-group.ts +++ b/packages/@aws-cdk/aws-ec2/lib/security-group.ts @@ -1,4 +1,4 @@ -import { Construct, IConstruct, Output, Stack, Token } from '@aws-cdk/cdk'; +import { Construct, IConstruct, Output, Token } from '@aws-cdk/cdk'; import { Connections, IConnectable } from './connections'; import { CfnSecurityGroup, CfnSecurityGroupEgress, CfnSecurityGroupIngress } from './ec2.generated'; import { IPortRange, ISecurityGroupRule } from './security-group-rule'; @@ -193,7 +193,7 @@ function determineRuleScope( } function differentStacks(group1: SecurityGroupBase, group2: SecurityGroupBase) { - return Stack.find(group1) !== Stack.find(group2); + return group1.node.stack !== group2.node.stack; } export interface SecurityGroupProps { diff --git a/packages/@aws-cdk/aws-ec2/lib/vpc-ref.ts b/packages/@aws-cdk/aws-ec2/lib/vpc-ref.ts index d4c0f6b618d5b..0bab75866938e 100644 --- a/packages/@aws-cdk/aws-ec2/lib/vpc-ref.ts +++ b/packages/@aws-cdk/aws-ec2/lib/vpc-ref.ts @@ -1,4 +1,4 @@ -import { Construct, IConstruct, IDependable, Stack } from "@aws-cdk/cdk"; +import { Construct, IConstruct, IDependable } from "@aws-cdk/cdk"; import { subnetName } from './util'; export interface IVpcSubnet extends IConstruct { @@ -231,7 +231,7 @@ export abstract class VpcNetworkBase extends Construct implements IVpcNetwork { * The region where this VPC is defined */ public get vpcRegion(): string { - return Stack.find(this).region; + return this.node.stack.region; } } @@ -303,4 +303,4 @@ export interface VpcSubnetImportProps { * The subnetId for this particular subnet */ subnetId: string; -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-ecr/lib/repository-ref.ts b/packages/@aws-cdk/aws-ecr/lib/repository-ref.ts index b79c1de336f94..064ca083e180a 100644 --- a/packages/@aws-cdk/aws-ecr/lib/repository-ref.ts +++ b/packages/@aws-cdk/aws-ecr/lib/repository-ref.ts @@ -119,7 +119,7 @@ export abstract class RepositoryBase extends cdk.Construct implements IRepositor * as the current stack. */ public static arnForLocalRepository(repositoryName: string, scope: cdk.IConstruct): string { - return cdk.Stack.find(scope).formatArn({ + return scope.node.stack.formatArn({ service: 'ecr', resource: 'repository', resourceName: repositoryName @@ -160,7 +160,7 @@ export abstract class RepositoryBase extends cdk.Construct implements IRepositor */ public repositoryUriForTag(tag?: string): string { const tagSuffix = tag ? `:${tag}` : ''; - const parts = cdk.Stack.find(this).parseArn(this.repositoryArn); + const parts = this.node.stack.parseArn(this.repositoryArn); return `${parts.account}.dkr.ecr.${parts.region}.amazonaws.com/${this.repositoryName}${tagSuffix}`; } diff --git a/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts b/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts index 174e082770c5a..fa8365cc1d266 100644 --- a/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts +++ b/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts @@ -224,7 +224,7 @@ export abstract class BaseService extends cdk.Construct private makeAutoScalingRole(): iam.IRole { // Use a Service Linked Role. return iam.Role.import(this, 'ScalingRole', { - roleArn: cdk.Stack.find(this).formatArn({ + roleArn: this.node.stack.formatArn({ service: 'iam', resource: 'role/aws-service-role/ecs.application-autoscaling.amazonaws.com', resourceName: 'AWSServiceRoleForApplicationAutoScaling_ECSService', diff --git a/packages/@aws-cdk/aws-ecs/lib/cluster.ts b/packages/@aws-cdk/aws-ecs/lib/cluster.ts index c88bacd9895f8..a11c886a5e96f 100644 --- a/packages/@aws-cdk/aws-ecs/lib/cluster.ts +++ b/packages/@aws-cdk/aws-ecs/lib/cluster.ts @@ -327,7 +327,7 @@ class ImportedCluster extends cdk.Construct implements ICluster { this.vpc = ec2.VpcNetwork.import(this, "vpc", props.vpc); this.hasEc2Capacity = props.hasEc2Capacity !== false; - this.clusterArn = props.clusterArn !== undefined ? props.clusterArn : cdk.Stack.find(this).formatArn({ + this.clusterArn = props.clusterArn !== undefined ? props.clusterArn : this.node.stack.formatArn({ service: 'ecs', resource: 'cluster', resourceName: props.clusterName, diff --git a/packages/@aws-cdk/aws-ecs/lib/log-drivers/aws-log-driver.ts b/packages/@aws-cdk/aws-ecs/lib/log-drivers/aws-log-driver.ts index 0614626947f7f..fc2a83e4c8d53 100644 --- a/packages/@aws-cdk/aws-ecs/lib/log-drivers/aws-log-driver.ts +++ b/packages/@aws-cdk/aws-ecs/lib/log-drivers/aws-log-driver.ts @@ -73,13 +73,12 @@ export class AwsLogDriver extends LogDriver { * Return the log driver CloudFormation JSON */ public renderLogDriver(): CfnTaskDefinition.LogConfigurationProperty { - const stack = cdk.Stack.find(this); return { logDriver: 'awslogs', options: removeEmpty({ 'awslogs-group': this.logGroup.logGroupName, 'awslogs-stream-prefix': this.props.streamPrefix, - 'awslogs-region': stack.region, + 'awslogs-region': this.node.stack.region, 'awslogs-datetime-format': this.props.datetimeFormat, 'awslogs-multiline-pattern': this.props.multilinePattern, }), diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-load-balancer.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-load-balancer.ts index bddec5bac2bae..4c9cde7a9bef4 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-load-balancer.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-load-balancer.ts @@ -82,9 +82,7 @@ export class ApplicationLoadBalancer extends BaseLoadBalancer implements IApplic this.setAttribute('access_logs.s3.bucket', bucket.bucketName.toString()); this.setAttribute('access_logs.s3.prefix', prefix); - const stack = cdk.Stack.find(this); - - const region = stack.requireRegion('Enable ELBv2 access logging'); + const region = this.node.stack.requireRegion('Enable ELBv2 access logging'); const account = ELBV2_ACCOUNTS[region]; if (!account) { throw new Error(`Cannot enable access logging; don't know ELBv2 account for region ${region}`); diff --git a/packages/@aws-cdk/aws-iam/lib/managed-policy.ts b/packages/@aws-cdk/aws-iam/lib/managed-policy.ts index 531080b80ac4b..7699f6a8b222e 100644 --- a/packages/@aws-cdk/aws-iam/lib/managed-policy.ts +++ b/packages/@aws-cdk/aws-iam/lib/managed-policy.ts @@ -18,7 +18,7 @@ export class AwsManagedPolicy { */ public get policyArn(): string { // the arn is in the form of - arn:aws:iam::aws:policy/ - return cdk.Stack.find(this.scope).formatArn({ + return this.scope.node.stack.formatArn({ service: "iam", region: "", // no region for managed policy account: "aws", // the account for a managed policy is 'aws' diff --git a/packages/@aws-cdk/aws-iam/lib/policy-document.ts b/packages/@aws-cdk/aws-iam/lib/policy-document.ts index 3a30676b18a59..b1618945e0c0c 100644 --- a/packages/@aws-cdk/aws-iam/lib/policy-document.ts +++ b/packages/@aws-cdk/aws-iam/lib/policy-document.ts @@ -459,7 +459,6 @@ class StackDependentToken extends cdk.Token { } public resolve(context: cdk.ResolveContext) { - const stack = cdk.Stack.find(context.scope); - return this.fn(stack); + return this.fn(context.scope.node.stack); } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-iam/lib/role.ts b/packages/@aws-cdk/aws-iam/lib/role.ts index 528f3fc5db761..d6b64ed3d143e 100644 --- a/packages/@aws-cdk/aws-iam/lib/role.ts +++ b/packages/@aws-cdk/aws-iam/lib/role.ts @@ -1,4 +1,4 @@ -import { Construct, IConstruct, Output, Stack } from '@aws-cdk/cdk'; +import { Construct, IConstruct, Output } from '@aws-cdk/cdk'; import { CfnRole } from './iam.generated'; import { IPrincipal, Policy } from './policy'; import { ArnPrincipal, PolicyDocument, PolicyPrincipal, PolicyStatement } from './policy-document'; @@ -313,7 +313,7 @@ class ImportedRole extends Construct implements IRole { } public get roleName() { - return Stack.find(this).parseArn(this.roleArn).resourceName!; + return this.node.stack.parseArn(this.roleArn).resourceName!; } public export() { diff --git a/packages/@aws-cdk/aws-kinesis/lib/stream.ts b/packages/@aws-cdk/aws-kinesis/lib/stream.ts index 0b569ccf07227..872dedd16a55b 100644 --- a/packages/@aws-cdk/aws-kinesis/lib/stream.ts +++ b/packages/@aws-cdk/aws-kinesis/lib/stream.ts @@ -197,10 +197,9 @@ export abstract class StreamBase extends cdk.Construct implements IStream { public logSubscriptionDestination(sourceLogGroup: logs.ILogGroup): logs.LogSubscriptionDestination { // Following example from https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/SubscriptionFilters.html#DestinationKinesisExample if (!this.cloudWatchLogsRole) { - const stack = cdk.Stack.find(this); // Create a role to be assumed by CWL that can write to this stream and pass itself. this.cloudWatchLogsRole = new iam.Role(this, 'CloudWatchLogsCanPutRecords', { - assumedBy: new iam.ServicePrincipal(`logs.${stack.region}.amazonaws.com`) + assumedBy: new iam.ServicePrincipal(`logs.${this.node.stack.region}.amazonaws.com`) }); this.cloudWatchLogsRole.addToPolicy(new iam.PolicyStatement().addAction('kinesis:PutRecord').addResource(this.streamArn)); this.cloudWatchLogsRole.addToPolicy(new iam.PolicyStatement().addAction('iam:PassRole').addResource(this.cloudWatchLogsRole.roleArn)); @@ -208,8 +207,8 @@ export abstract class StreamBase extends cdk.Construct implements IStream { // We've now made it possible for CloudWatch events to write to us. In case the LogGroup is in a // different account, we must add a Destination in between as well. - const sourceStack = cdk.Stack.find(sourceLogGroup as any); - const thisStack = cdk.Stack.find(this); + const sourceStack = sourceLogGroup.node.stack; + const thisStack = this.node.stack; // Case considered: if both accounts are undefined, we can't make any assumptions. Better // to assume we don't need to do anything special. @@ -227,8 +226,8 @@ export abstract class StreamBase extends cdk.Construct implements IStream { */ private crossAccountLogSubscriptionDestination(sourceLogGroup: logs.ILogGroup): logs.LogSubscriptionDestination { const sourceLogGroupConstruct: cdk.Construct = sourceLogGroup as any; - const sourceStack = cdk.Stack.find(sourceLogGroupConstruct); - const thisStack = cdk.Stack.find(this); + const sourceStack = sourceLogGroupConstruct.node.stack; + const thisStack = this.node.stack; if (!sourceStack.env.account || !thisStack.env.account) { throw new Error('SubscriptionFilter stack and Destination stack must either both have accounts defined, or both not have accounts'); @@ -429,7 +428,7 @@ class ImportedStream extends StreamBase { this.streamArn = props.streamArn; // Get the name from the ARN - this.streamName = cdk.Stack.find(this).parseArn(props.streamArn).resourceName!; + this.streamName = this.node.stack.parseArn(props.streamArn).resourceName!; if (props.encryptionKey) { // TODO: import "scope" should be changed to "this" diff --git a/packages/@aws-cdk/aws-lambda/lib/function-base.ts b/packages/@aws-cdk/aws-lambda/lib/function-base.ts index 0690d64df156c..c2f9edb762ee6 100644 --- a/packages/@aws-cdk/aws-lambda/lib/function-base.ts +++ b/packages/@aws-cdk/aws-lambda/lib/function-base.ts @@ -258,13 +258,12 @@ export abstract class FunctionBase extends cdk.Construct implements IFunction { const arn = sourceLogGroup.logGroupArn; if (this.logSubscriptionDestinationPolicyAddedFor.indexOf(arn) === -1) { - const stack = cdk.Stack.find(this); // NOTE: the use of {AWS::Region} limits this to the same region, which shouldn't really be an issue, // since the Lambda must be in the same region as the SubscriptionFilter anyway. // // (Wildcards in principals are unfortunately not supported. this.addPermission('InvokedByCloudWatchLogs', { - principal: new iam.ServicePrincipal(`logs.${stack.region}.amazonaws.com`), + principal: new iam.ServicePrincipal(`logs.${this.node.stack.region}.amazonaws.com`), sourceArn: arn }); this.logSubscriptionDestinationPolicyAddedFor.push(arn); @@ -283,10 +282,9 @@ export abstract class FunctionBase extends cdk.Construct implements IFunction { */ public asBucketNotificationDestination(bucketArn: string, bucketId: string): s3n.BucketNotificationDestinationProps { const permissionId = `AllowBucketNotificationsFrom${bucketId}`; - const stack = cdk.Stack.find(this); if (!this.node.tryFindChild(permissionId)) { this.addPermission(permissionId, { - sourceAccount: stack.accountId, + sourceAccount: this.node.stack.accountId, principal: new iam.ServicePrincipal('s3.amazonaws.com'), sourceArn: bucketArn, }); @@ -350,4 +348,4 @@ export abstract class FunctionBase extends cdk.Construct implements IFunction { throw new Error(`Invalid principal type for Lambda permission statement: ${JSON.stringify(this.node.resolve(principal))}. ` + 'Supported: AccountPrincipal, ServicePrincipal'); } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-lambda/lib/function.ts b/packages/@aws-cdk/aws-lambda/lib/function.ts index dfa04648cd51e..9fc1de92d6f2a 100644 --- a/packages/@aws-cdk/aws-lambda/lib/function.ts +++ b/packages/@aws-cdk/aws-lambda/lib/function.ts @@ -355,10 +355,10 @@ export class Function extends FunctionBase { this.role.addToPolicy(statement); } - const stack = cdk.Stack.find(this); - const isChina = stack.env.region && stack.env.region.startsWith('cn-'); + const isChina = this.node.stack.env.region && this.node.stack.env.region.startsWith('cn-'); if (isChina && props.environment && Object.keys(props.environment).length > 0) { - throw new Error(`Environment variables are not supported in this region (${stack.env.region}); consider using tags or SSM parameters instead`); + // tslint:disable-next-line:max-line-length + throw new Error(`Environment variables are not supported in this region (${this.node.stack.env.region}); consider using tags or SSM parameters instead`); } const resource = new CfnFunction(this, 'Resource', { diff --git a/packages/@aws-cdk/aws-lambda/lib/layers.ts b/packages/@aws-cdk/aws-lambda/lib/layers.ts index 2a4d91387e9ad..d37b7de1cb614 100644 --- a/packages/@aws-cdk/aws-lambda/lib/layers.ts +++ b/packages/@aws-cdk/aws-lambda/lib/layers.ts @@ -233,11 +233,10 @@ export class SingletonLayerVersion extends cdk.Construct implements ILayerVersio private ensureLayerVersion(props: SingletonLayerVersionProps): ILayerVersion { const singletonId = `SingletonLayer-${props.uuid}`; - const stack = cdk.Stack.find(this); - const existing = stack.node.tryFindChild(singletonId); + const existing = this.node.stack.node.tryFindChild(singletonId); if (existing) { return existing as unknown as ILayerVersion; } - return new LayerVersion(stack, singletonId, props); + return new LayerVersion(this.node.stack, singletonId, props); } } diff --git a/packages/@aws-cdk/aws-lambda/lib/singleton-lambda.ts b/packages/@aws-cdk/aws-lambda/lib/singleton-lambda.ts index 7f8aac34cfb32..9b00fbda319f9 100644 --- a/packages/@aws-cdk/aws-lambda/lib/singleton-lambda.ts +++ b/packages/@aws-cdk/aws-lambda/lib/singleton-lambda.ts @@ -63,14 +63,13 @@ export class SingletonFunction extends FunctionBase { private ensureLambda(props: SingletonFunctionProps): IFunction { const constructName = (props.lambdaPurpose || 'SingletonLambda') + slugify(props.uuid); - const stack = cdk.Stack.find(this); - const existing = stack.node.tryFindChild(constructName); + const existing = this.node.stack.node.tryFindChild(constructName); if (existing) { // Just assume this is true return existing as FunctionBase; } - return new LambdaFunction(stack, constructName, props); + return new LambdaFunction(this.node.stack, constructName, props); } } diff --git a/packages/@aws-cdk/aws-logs/lib/cross-account-destination.ts b/packages/@aws-cdk/aws-logs/lib/cross-account-destination.ts index dccec000fa360..c67852c02a381 100644 --- a/packages/@aws-cdk/aws-logs/lib/cross-account-destination.ts +++ b/packages/@aws-cdk/aws-logs/lib/cross-account-destination.ts @@ -87,8 +87,7 @@ export class CrossAccountDestination extends cdk.Construct implements ILogSubscr */ private generateUniqueName(): string { // Combination of stack name and LogicalID, which are guaranteed to be unique. - const stack = cdk.Stack.find(this); - return stack.name + '-' + this.resource.logicalId; + return this.node.stack.name + '-' + this.resource.logicalId; } /** diff --git a/packages/@aws-cdk/aws-logs/lib/log-group.ts b/packages/@aws-cdk/aws-logs/lib/log-group.ts index 4f548fd295bdc..a3215ceec2fc4 100644 --- a/packages/@aws-cdk/aws-logs/lib/log-group.ts +++ b/packages/@aws-cdk/aws-logs/lib/log-group.ts @@ -295,7 +295,7 @@ class ImportedLogGroup extends LogGroupBase { super(scope, id); this.logGroupArn = props.logGroupArn; - this.logGroupName = cdk.Stack.find(this).parseArn(props.logGroupArn, ':').resourceName!; + this.logGroupName = this.node.stack.parseArn(props.logGroupArn, ':').resourceName!; } /** diff --git a/packages/@aws-cdk/aws-s3/lib/bucket.ts b/packages/@aws-cdk/aws-s3/lib/bucket.ts index c69d907c11141..44e1bb517ad01 100644 --- a/packages/@aws-cdk/aws-s3/lib/bucket.ts +++ b/packages/@aws-cdk/aws-s3/lib/bucket.ts @@ -373,8 +373,7 @@ export abstract class BucketBase extends cdk.Construct implements IBucket { * @returns an ObjectS3Url token */ public urlForObject(key?: string): string { - const stack = cdk.Stack.find(this); - const components = [ `https://s3.${stack.region}.${stack.urlSuffix}/${this.bucketName}` ]; + const components = [ `https://s3.${this.node.stack.region}.${this.node.stack.urlSuffix}/${this.bucketName}` ]; if (key) { // trim prepending '/' if (typeof key === 'string' && key.startsWith('/')) { @@ -1134,7 +1133,7 @@ class ImportedBucket extends BucketBase { private generateBucketWebsiteUrl() { return this.bucketWebsiteNewUrlFormat - ? `${this.bucketName}.s3-website.${cdk.Stack.find(this).region}.${cdk.Stack.find(this).urlSuffix}` - : `${this.bucketName}.s3-website-${cdk.Stack.find(this).region}.${cdk.Stack.find(this).urlSuffix}`; + ? `${this.bucketName}.s3-website.${this.node.stack.region}.${this.node.stack.urlSuffix}` + : `${this.bucketName}.s3-website-${this.node.stack.region}.${this.node.stack.urlSuffix}`; } } diff --git a/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource-handler.ts b/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource-handler.ts index 44ae63c32a0b3..9318aa023c8ba 100644 --- a/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource-handler.ts +++ b/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource-handler.ts @@ -26,7 +26,7 @@ export class NotificationsResourceHandler extends cdk.Construct { * @returns The ARN of the custom resource lambda function. */ public static singleton(context: cdk.Construct) { - const root = cdk.Stack.find(context); + const root = context.node.stack; // well-known logical id to ensure stack singletonity const logicalId = 'BucketNotificationsHandler050a0587b7544547bf325f094a3db834'; @@ -50,7 +50,7 @@ export class NotificationsResourceHandler extends cdk.Construct { const role = new iam.Role(this, 'Role', { assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'), managedPolicyArns: [ - cdk.Stack.find(this).formatArn({ + this.node.stack.formatArn({ service: 'iam', region: '', // no region for managed policy account: 'aws', // the account for a managed policy is 'aws' diff --git a/packages/@aws-cdk/aws-s3/lib/util.ts b/packages/@aws-cdk/aws-s3/lib/util.ts index e241db77cd5a0..1af6468e8fc25 100644 --- a/packages/@aws-cdk/aws-s3/lib/util.ts +++ b/packages/@aws-cdk/aws-s3/lib/util.ts @@ -9,7 +9,7 @@ export function parseBucketArn(construct: cdk.IConstruct, props: BucketImportPro } if (props.bucketName) { - return cdk.Stack.find(construct).formatArn({ + return construct.node.stack.formatArn({ // S3 Bucket names are globally unique in a partition, // and so their ARNs have empty region and account components region: '', @@ -34,7 +34,7 @@ export function parseBucketName(construct: cdk.IConstruct, props: BucketImportPr const resolved = construct.node.resolve(props.bucketArn); if (typeof(resolved) === 'string') { - const components = cdk.Stack.find(construct).parseArn(resolved); + const components = construct.node.stack.parseArn(resolved); if (components.service !== 's3') { throw new Error('Invalid ARN. Expecting "s3" service:' + resolved); } diff --git a/packages/@aws-cdk/aws-secretsmanager/lib/secret.ts b/packages/@aws-cdk/aws-secretsmanager/lib/secret.ts index 5d9ade85f9193..2eda7901c5d42 100644 --- a/packages/@aws-cdk/aws-secretsmanager/lib/secret.ts +++ b/packages/@aws-cdk/aws-secretsmanager/lib/secret.ts @@ -122,7 +122,7 @@ export abstract class SecretBase extends cdk.Construct implements ISecret { .addAction('kms:Decrypt') .addAllResources() .addCondition('StringEquals', { - 'kms:ViaService': `secretsmanager.${cdk.Stack.find(this).region}.amazonaws.com` + 'kms:ViaService': `secretsmanager.${this.node.stack.region}.amazonaws.com` })); } } diff --git a/packages/@aws-cdk/aws-sqs/lib/queue.ts b/packages/@aws-cdk/aws-sqs/lib/queue.ts index 162acbadfbe24..9afbf6d97a710 100644 --- a/packages/@aws-cdk/aws-sqs/lib/queue.ts +++ b/packages/@aws-cdk/aws-sqs/lib/queue.ts @@ -350,7 +350,7 @@ class ImportedQueue extends QueueBase { super(scope, id); this.queueArn = props.queueArn; this.queueUrl = props.queueUrl; - this.queueName = cdk.Stack.find(this).parseArn(props.queueArn).resource; + this.queueName = this.node.stack.parseArn(props.queueArn).resource; if (props.keyArn) { this.encryptionMasterKey = kms.EncryptionKey.import(this, 'Key', { diff --git a/packages/@aws-cdk/aws-sqs/package-lock.json b/packages/@aws-cdk/aws-sqs/package-lock.json index 2864c17a1143b..37948f25919b7 100644 --- a/packages/@aws-cdk/aws-sqs/package-lock.json +++ b/packages/@aws-cdk/aws-sqs/package-lock.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-sqs", - "version": "0.23.0", + "version": "0.24.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/@aws-cdk/aws-ssm/lib/parameter.ts b/packages/@aws-cdk/aws-ssm/lib/parameter.ts index f228ac84a5ab0..4dfabdf0460a4 100644 --- a/packages/@aws-cdk/aws-ssm/lib/parameter.ts +++ b/packages/@aws-cdk/aws-ssm/lib/parameter.ts @@ -116,7 +116,7 @@ export abstract class ParameterBase extends cdk.Construct implements IParameter } public get parameterArn(): string { - return cdk.Stack.find(this).formatArn({ + return this.node.stack.formatArn({ service: 'ssm', resource: 'parameter', sep: '', // Sep is empty because this.parameterName starts with a / already! diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts b/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts index 7744cc7e2e914..0e51318d98f36 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts @@ -71,9 +71,8 @@ export class StateMachine extends cdk.Construct implements IStateMachine, events constructor(scope: cdk.Construct, id: string, props: StateMachineProps) { super(scope, id); - const stack = cdk.Stack.find(this); this.role = props.role || new iam.Role(this, 'Role', { - assumedBy: new iam.ServicePrincipal(`states.${stack.region}.amazonaws.com`), + assumedBy: new iam.ServicePrincipal(`states.${this.node.stack.region}.amazonaws.com`), }); const graph = new StateGraph(props.definition.startState, `State Machine ${id} definition`); diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/cfn-tokens.ts b/packages/@aws-cdk/cdk/lib/cloudformation/cfn-tokens.ts index 1646df2d46288..8a4ab68ed574f 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/cfn-tokens.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/cfn-tokens.ts @@ -47,14 +47,14 @@ export class CfnReference extends Token { this.isReference = true; if (scope !== undefined) { - this.producingStack = Stack.find(scope); + this.producingStack = scope.node.stack; } } public resolve(context: ResolveContext): any { // If we have a special token for this consuming stack, resolve that. Otherwise resolve as if // we are in the same stack. - const token = this.replacementTokens.get(Stack.find(context.scope)); + const token = this.replacementTokens.get(context.scope.node.stack); if (token) { return token.resolve(context); } else { diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/output.ts b/packages/@aws-cdk/cdk/lib/cloudformation/output.ts index 339a497d30253..c098f4dfc09ab 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/output.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/output.ts @@ -150,7 +150,7 @@ export class Output extends StackElement { */ private uniqueOutputName() { // prefix export name with stack name since exports are global within account + region. - const stackName = require('./stack').Stack.find(this).name; + const stackName = this.node.stack.name; return (stackName ? stackName + ':' : '') + this.logicalId; } } @@ -261,4 +261,4 @@ function fn() { return require('./fn').Fn; } -import { Condition } from './condition'; \ No newline at end of file +import { Condition } from './condition'; diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/stack-element.ts b/packages/@aws-cdk/cdk/lib/cloudformation/stack-element.ts index 563cd5cb050c7..044bf504848a4 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/stack-element.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/stack-element.ts @@ -31,11 +31,6 @@ export abstract class StackElement extends Construct { */ public readonly logicalId: string; - /** - * The stack this Construct has been made a part of - */ - protected stack: Stack; - private _logicalId: string; /** @@ -47,15 +42,10 @@ export abstract class StackElement extends Construct { */ constructor(scope: Construct, id: string) { super(scope, id); - const s = Stack.find(this); - if (!s) { - throw new Error('The tree root must be derived from "Stack"'); - } - this.stack = s; 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.node.stack.logicalIds.getLogicalId(this); this.logicalId = new Token(() => this._logicalId).toString(); } @@ -93,7 +83,7 @@ export abstract class StackElement extends Construct { * Return the path with respect to the stack */ public get stackPath(): string { - return this.node.ancestors(this.stack).map(c => c.node.id).join(PATH_SEP); + return this.node.ancestors(this.node.stack).map(c => c.node.id).join(PATH_SEP); } /** @@ -165,4 +155,3 @@ export abstract class Referenceable extends StackElement { } import { findTokens } from "../core/tokens/resolve"; -import { Stack } from "./stack"; diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/stack.ts b/packages/@aws-cdk/cdk/lib/cloudformation/stack.ts index 82fc3e198fc98..fd8b4b272a59c 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/stack.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/stack.ts @@ -26,33 +26,6 @@ export interface StackProps { * A root construct which represents a single CloudFormation stack. */ export class Stack extends Construct { - /** - * Traverses the tree and looks up for the Stack root. - * @param scope A construct in the tree - * @returns The Stack object (throws if the node is not part of a Stack-rooted tree) - */ - public static find(scope: IConstruct): Stack { - const curr = Stack.tryFind(scope); - if (curr == null) { - throw new Error(`Cannot find a Stack parent for '${scope.toString()}'`); - } - return curr; - } - - /** - * Traverses the tree and looks up for the Stack root. - * - * @param scope A construct in the tree - * @returns The Stack object, or undefined if no stack was found. - */ - public static tryFind(scope: IConstruct): Stack | undefined { - let curr: IConstruct | undefined = scope; - while (curr != null && !Stack.isStack(curr)) { - curr = curr.node.scope; - } - return curr; - } - /** * Adds a metadata annotation "aws:cdk:physical-name" to the construct if physicalName * is non-null. This can be used later by tools and aspects to determine if resources @@ -422,7 +395,7 @@ export class Stack extends Construct { // Resource dependencies for (const dependency of this.node.findDependencies()) { - const theirStack = Stack.tryFind(dependency.target); + const theirStack = dependency.target.node.stack; if (theirStack !== undefined && theirStack !== this) { this.addDependency(theirStack); } else { diff --git a/packages/@aws-cdk/cdk/lib/context.ts b/packages/@aws-cdk/cdk/lib/context.ts index 45e7b8bcfc79b..c31ae244f9cc2 100644 --- a/packages/@aws-cdk/cdk/lib/context.ts +++ b/packages/@aws-cdk/cdk/lib/context.ts @@ -1,5 +1,4 @@ import cxapi = require('@aws-cdk/cx-api'); -import { Stack } from './cloudformation/stack'; import { Construct } from './core/construct'; type ContextProviderProps = {[key: string]: any}; @@ -14,17 +13,14 @@ type ContextProviderProps = {[key: string]: any}; */ export class ContextProvider { - private readonly stack: Stack; private readonly props: ContextProviderProps; - constructor( - private readonly context: Construct, - private readonly provider: string, - props: ContextProviderProps = {}) { - this.stack = Stack.find(context); + constructor(private readonly context: Construct, + private readonly provider: string, + props: ContextProviderProps = {}) { this.props = { - account: this.stack.env.account, - region: this.stack.env.region, + account: context.node.stack.env.account, + region: context.node.stack.env.region, ...props, }; } @@ -51,7 +47,7 @@ export class ContextProvider { return value; } - this.stack.reportMissingContext(this.key, { + this.context.node.stack.reportMissingContext(this.key, { provider: this.provider, props: this.props, }); @@ -78,7 +74,7 @@ export class ContextProvider { return value; } - this.stack.reportMissingContext(this.key, { + this.context.node.stack.reportMissingContext(this.key, { provider: this.provider, props: this.props, }); @@ -108,7 +104,7 @@ export class ContextProvider { return value; } - this.stack.reportMissingContext(this.key, { + this.context.node.stack.reportMissingContext(this.key, { provider: this.provider, props: this.props, }); diff --git a/packages/@aws-cdk/cdk/lib/core/construct.ts b/packages/@aws-cdk/cdk/lib/core/construct.ts index 8cc232b3af582..0a4a12cf7fef5 100644 --- a/packages/@aws-cdk/cdk/lib/core/construct.ts +++ b/packages/@aws-cdk/cdk/lib/core/construct.ts @@ -47,6 +47,9 @@ export class ConstructNode { private readonly references = new Set(); private readonly dependencies = new Set(); + /** Will be used to cache the value of ``this.stack``. */ + private _stack?: Stack; + /** * If this is set to 'true'. addChild() calls for this construct and any child * will fail. This is used to prevent tree mutations during synthesis. @@ -83,6 +86,23 @@ export class ConstructNode { } } + /** + * The stack the construct is a part of. + */ + public get stack(): Stack { + return this._stack || (this._stack = _lookStackUp(this)); + + function _lookStackUp(_this: ConstructNode) { + if (Stack.isStack(_this.host)) { + return _this.host; + } + if (!_this.scope) { + throw new Error(`No stack could be identified for the construct at path ${_this.path}`); + } + return _this.scope.node.stack; + } + } + /** * The full path of this construct in the tree. * Components are separated by '/'. @@ -708,3 +728,6 @@ export interface Dependency { */ target: IConstruct; } + +// Import this _after_ everything else to help node work the classes out in the correct order... +import { Stack } from '../cloudformation/stack'; diff --git a/packages/@aws-cdk/cdk/test/cloudformation/test.stack.ts b/packages/@aws-cdk/cdk/test/cloudformation/test.stack.ts index d56bedb34896d..bcf2ca10432b4 100644 --- a/packages/@aws-cdk/cdk/test/cloudformation/test.stack.ts +++ b/packages/@aws-cdk/cdk/test/cloudformation/test.stack.ts @@ -1,6 +1,6 @@ import cxapi = require('@aws-cdk/cx-api'); import { Test } from 'nodeunit'; -import { App, Aws, Condition, Construct, Include, Output, Parameter, Resource, Root, Stack, Token } from '../../lib'; +import { App, Aws, Condition, Construct, Include, Output, Parameter, Resource, Stack, Token } from '../../lib'; export = { 'a stack can be serialized into a CloudFormation template, initially it\'s empty'(test: Test) { @@ -22,27 +22,6 @@ export = { test.done(); }, - 'Stack.find(c) can be used to find the stack from any point in the tree'(test: Test) { - const stack = new Stack(); - const level1 = new Construct(stack, 'level1'); - const level2 = new Construct(level1, 'level2'); - const level3 = new Construct(level2, 'level3'); - const res1 = new Resource(level1, 'childoflevel1', { type: 'MyResourceType1' }); - const res2 = new Resource(level3, 'childoflevel3', { type: 'MyResourceType2' }); - - test.equal(Stack.find(res1), stack); - test.equal(Stack.find(res2), stack); - test.equal(Stack.find(level2), stack); - - const root = new Root(); - const child = new Construct(root, 'child'); - - test.throws(() => Stack.find(child)); - test.throws(() => Stack.find(root)); - - test.done(); - }, - 'Stack.isStack indicates that a construct is a stack'(test: Test) { const stack = new Stack(); const c = new Construct(stack, 'Construct'); diff --git a/packages/@aws-cdk/cdk/test/core/test.construct.ts b/packages/@aws-cdk/cdk/test/core/test.construct.ts index 4479c4709140d..851d179a53200 100644 --- a/packages/@aws-cdk/cdk/test/core/test.construct.ts +++ b/packages/@aws-cdk/cdk/test/core/test.construct.ts @@ -1,6 +1,6 @@ import cxapi = require('@aws-cdk/cx-api'); import { Test } from 'nodeunit'; -import { Construct, Root, Token } from '../../lib'; +import { ArnComponents, Construct, Root, Stack, Token } from '../../lib'; // tslint:disable:variable-name // tslint:disable:max-line-length @@ -95,6 +95,50 @@ export = { test.done(); }, + 'construct.node.stack returns the correct stack'(test: Test) { + const stack = new Stack(); + test.same(stack.node.stack, stack); + const parent = new Construct(stack, 'Parent'); + const construct = new Construct(parent, 'Construct'); + test.same(construct.node.stack, stack); + test.done(); + }, + + 'construct.node.stack throws when there is no parent Stack'(test: Test) { + const root = new Root(); + const construct = new Construct(root, 'Construct'); + test.throws(() => construct.node.stack, /No stack could be identified for the construct at path/); + test.done(); + }, + + 'construct.node.stack.formatArn forwards to the Stack'(test: Test) { + const stack = new Stack(); + const components: ArnComponents = { service: 'test', resource: 'test' }; + const dummyArn = 'arn:::dummy'; + stack.formatArn = (args) => { + test.same(args, components); + return dummyArn; + }; + + const construct = new Construct(stack, 'Construct'); + test.same(construct.node.stack.formatArn(components), dummyArn); + test.done(); + }, + + 'construct.node.stack.parseArn forwards to the Stack'(test: Test) { + const stack = new Stack(); + const components: ArnComponents = { service: 'test', resource: 'test' }; + const dummyArn = 'arn:::dummy'; + stack.parseArn = (arn) => { + test.same(arn, dummyArn); + return components; + }; + + const construct = new Construct(stack, 'Construct'); + test.same(construct.node.stack.parseArn(dummyArn), components); + test.done(); + }, + 'construct.getChildren() returns an array of all children'(test: Test) { const root = new Root(); const child = new Construct(root, 'Child1'); @@ -339,7 +383,7 @@ export = { } } - class Stack extends Root { + class TestStack extends Root { constructor() { super(); @@ -352,7 +396,7 @@ export = { } } - const stack = new Stack(); + const stack = new TestStack(); const errors = (stack.node.validateTree()).map(v => ({ path: v.source.node.path, message: v.message })); diff --git a/packages/@aws-cdk/runtime-values/lib/rtv.ts b/packages/@aws-cdk/runtime-values/lib/rtv.ts index 3cf7222e78c0b..8407430b8698b 100644 --- a/packages/@aws-cdk/runtime-values/lib/rtv.ts +++ b/packages/@aws-cdk/runtime-values/lib/rtv.ts @@ -54,10 +54,8 @@ export class RuntimeValue extends cdk.Construct { constructor(scope: cdk.Construct, id: string, props: RuntimeValueProps) { super(scope, id); - const stack = cdk.Stack.find(this); - - this.parameterName = `/rtv/${stack.stackName}/${props.package}/${id}`; - this.envValue = stack.stackName; + this.parameterName = `/rtv/${this.node.stack.stackName}/${props.package}/${id}`; + this.envValue = this.node.stack.stackName; new ssm.CfnParameter(this, 'Parameter', { name: this.parameterName, @@ -65,7 +63,7 @@ export class RuntimeValue extends cdk.Construct { value: props.value, }); - this.parameterArn = cdk.Stack.find(this).formatArn({ + this.parameterArn = this.node.stack.formatArn({ service: 'ssm', resource: 'parameter', resourceName: this.parameterName diff --git a/packages/@aws-cdk/runtime-values/test/test.rtv.ts b/packages/@aws-cdk/runtime-values/test/test.rtv.ts index 5bb331617dd87..e05f9feea1dca 100644 --- a/packages/@aws-cdk/runtime-values/test/test.rtv.ts +++ b/packages/@aws-cdk/runtime-values/test/test.rtv.ts @@ -23,8 +23,6 @@ class RuntimeValueTest extends cdk.Construct { constructor(scope: cdk.Construct, id: string) { super(scope, id); - const stack = cdk.Stack.find(this); - const queue = new sqs.CfnQueue(this, 'Queue', {}); const role = new iam.Role(this, 'Role', { @@ -44,7 +42,7 @@ class RuntimeValueTest extends cdk.Construct { role: role.roleArn, environment: { variables: { - [RuntimeValue.ENV_NAME]: stack.stackName, + [RuntimeValue.ENV_NAME]: this.node.stack.stackName, } } }); diff --git a/tools/cfn2ts/lib/codegen.ts b/tools/cfn2ts/lib/codegen.ts index 045b2e635ce5f..1c9fbd0b61810 100644 --- a/tools/cfn2ts/lib/codegen.ts +++ b/tools/cfn2ts/lib/codegen.ts @@ -277,12 +277,12 @@ export default class CodeGenerator { if (spec.RequiredTransform) { const transformField = `${resourceName.className}.requiredTransform`; this.code.line('// If a different transform than the required one is in use, this resource cannot be used'); - this.code.openBlock(`if (this.stack.templateOptions.transform && this.stack.templateOptions.transform !== ${transformField})`); + this.code.openBlock(`if (this.node.stack.templateOptions.transform && this.node.stack.templateOptions.transform !== ${transformField})`); // tslint:disable-next-line:max-line-length - this.code.line(`throw new Error(\`The \${JSON.stringify(${transformField})} transform is required when using ${resourceName.className}, but the \${JSON.stringify(this.stack.templateOptions.transform)} is used.\`);`); + this.code.line(`throw new Error(\`The \${JSON.stringify(${transformField})} transform is required when using ${resourceName.className}, but the \${JSON.stringify(this.node.stack.templateOptions.transform)} is used.\`);`); this.code.closeBlock(); this.code.line('// Automatically configure the required transform'); - this.code.line(`this.stack.templateOptions.transform = ${resourceName.className}.requiredTransform;`); + this.code.line(`this.node.stack.templateOptions.transform = ${resourceName.className}.requiredTransform;`); } // initialize all attribute properties diff --git a/tools/decdk/package.json b/tools/decdk/package.json index 5a53c523a4477..8877946dcf440 100644 --- a/tools/decdk/package.json +++ b/tools/decdk/package.json @@ -139,4 +139,4 @@ "engines": { "node": ">= 8.10.0" } -} +} \ No newline at end of file