From 9a8c82bdd95df52cb845c5ff68315f08cda880bb Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Thu, 18 Oct 2018 11:11:59 -0700 Subject: [PATCH 01/38] feat(cdk): Add support for Aspects (#1451) * The base Aspect class is added and is an idempotent propagating visitor, that recurses during the visit action * The first aspects are for tags * TagManager has been re-implemented to only handle setting removing and formatting tags * Tags can be included or excluded based on Resource Type, this replaces the the need for all previous tag properties * VPC, Security Group, KMS Keys, and AutoScaling are updated to use tags * Tag Aspects overwrite tags passed into the L1 during initialization --- .../aws-autoscaling/lib/auto-scaling-group.ts | 28 +- .../test/test.auto-scaling-group.ts | 31 +- .../test/test.scheduled-action.ts | 14 +- .../@aws-cdk/aws-ec2/lib/security-group.ts | 16 +- packages/@aws-cdk/aws-ec2/lib/vpc.ts | 49 +-- packages/@aws-cdk/aws-ec2/test/test.vpc.ts | 112 +++---- packages/@aws-cdk/aws-kms/lib/key.ts | 15 +- packages/@aws-cdk/aws-kms/test/test.key.ts | 280 ++++++++--------- packages/@aws-cdk/cdk/lib/app.ts | 5 +- packages/@aws-cdk/cdk/lib/aspects/aspect.ts | 38 +++ .../@aws-cdk/cdk/lib/aspects/tag-aspect.ts | 86 ++++++ .../cdk/lib/cloudformation/resource.ts | 39 ++- .../@aws-cdk/cdk/lib/cloudformation/tag.ts | 2 +- packages/@aws-cdk/cdk/lib/core/construct.ts | 26 ++ packages/@aws-cdk/cdk/lib/core/tag-manager.ts | 282 +++--------------- packages/@aws-cdk/cdk/lib/index.ts | 3 + packages/@aws-cdk/cdk/lib/runtime.ts | 4 +- .../cdk/test/aspects/test.tag-aspect.ts | 180 +++++++++++ .../cdk/test/cloudformation/test.resource.ts | 60 ++-- .../cdk/test/core/test.tag-manager.ts | 202 ++----------- .../@aws-cdk/cfnspec/lib/schema/property.ts | 50 +++- .../cfnspec/lib/schema/resource-type.ts | 25 +- tools/cfn2ts/lib/codegen.ts | 34 ++- tools/cfn2ts/lib/genspec.ts | 2 +- 24 files changed, 833 insertions(+), 750 deletions(-) create mode 100644 packages/@aws-cdk/cdk/lib/aspects/aspect.ts create mode 100644 packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts create mode 100644 packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts diff --git a/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts b/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts index dbd85436c8000..0b091505e01f4 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts @@ -137,11 +137,6 @@ export interface AutoScalingGroupProps { */ resourceSignalTimeoutSec?: number; - /** - * The AWS resource tags to associate with the ASG. - */ - tags?: cdk.Tags; - /** * Default scaling cooldown for this AutoScalingGroup * @@ -161,7 +156,7 @@ export interface AutoScalingGroupProps { * * The ASG spans all availability zones. */ -export class AutoScalingGroup extends cdk.Construct implements IAutoScalingGroup, cdk.ITaggable, elb.ILoadBalancerTarget, ec2.IConnectable, +export class AutoScalingGroup extends cdk.Construct implements IAutoScalingGroup, elb.ILoadBalancerTarget, ec2.IConnectable, elbv2.IApplicationLoadBalancerTarget, elbv2.INetworkLoadBalancerTarget { /** * The type of OS instances of this fleet are running. @@ -178,11 +173,6 @@ export class AutoScalingGroup extends cdk.Construct implements IAutoScalingGroup */ public readonly role: iam.Role; - /** - * Manage tags for this construct and children - */ - public readonly tags: cdk.TagManager; - /** * Name of the AutoScalingGroup */ @@ -209,8 +199,7 @@ export class AutoScalingGroup extends cdk.Construct implements IAutoScalingGroup }); this.connections = new ec2.Connections({ securityGroups: [this.securityGroup] }); this.securityGroups.push(this.securityGroup); - this.tags = new TagManager(this, {initialTags: props.tags}); - this.tags.setTag(NAME_TAG, this.node.path, { overwrite: false }); + this.apply(new cdk.Tag(NAME_TAG, this.node.path, { applyToLaunchInstances: true })); this.role = new iam.Role(this, 'InstanceRole', { assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com') @@ -255,7 +244,6 @@ export class AutoScalingGroup extends cdk.Construct implements IAutoScalingGroup launchConfigurationName: launchConfig.ref, loadBalancerNames: new cdk.Token(() => this.loadBalancerNames.length > 0 ? this.loadBalancerNames : undefined), targetGroupArns: new cdk.Token(() => this.targetGroupArns.length > 0 ? this.targetGroupArns : undefined), - tags: this.tags, }; if (props.notificationsTopic) { @@ -616,16 +604,6 @@ function renderRollingUpdateConfig(config: RollingUpdateConfiguration = {}): cdk }; } -class TagManager extends cdk.TagManager { - protected tagFormatResolve(tagGroups: cdk.TagGroups): any { - const tags = {...tagGroups.nonStickyTags, ...tagGroups.ancestorTags, ...tagGroups.stickyTags}; - return Object.keys(tags).map( (key) => { - const propagateAtLaunch = !!tagGroups.propagateTags[key] || !!tagGroups.ancestorTags[key]; - return {key, value: tags[key], propagateAtLaunch}; - }); - } -} - /** * Render a number of seconds to a PTnX string. */ @@ -748,4 +726,4 @@ export interface MetricTargetTrackingProps extends BaseTargetTrackingProps { * Value to keep the metric around */ targetValue: number; -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-autoscaling/test/test.auto-scaling-group.ts b/packages/@aws-cdk/aws-autoscaling/test/test.auto-scaling-group.ts index a4dbb1bf20aed..710446ae53f88 100644 --- a/packages/@aws-cdk/aws-autoscaling/test/test.auto-scaling-group.ts +++ b/packages/@aws-cdk/aws-autoscaling/test/test.auto-scaling-group.ts @@ -9,7 +9,7 @@ import autoscaling = require('../lib'); export = { 'default fleet'(test: Test) { - const stack = new cdk.Stack(undefined, 'MyStack', { env: { region: 'us-east-1', account: '1234' }}); + const stack = getTestStack(); const vpc = mockVpc(stack); new autoscaling.AutoScalingGroup(stack, 'MyFleet', { @@ -18,6 +18,8 @@ export = { vpc }); + stack.testInvokeAspects(); + expect(stack).toMatch({ "Resources": { "MyFleetInstanceSecurityGroup774E8234": { @@ -365,7 +367,8 @@ export = { }, 'can set tags'(test: Test) { // GIVEN - const stack = new cdk.Stack(undefined, 'MyStack', { env: { region: 'us-east-1', account: '1234' }}); + const stack = getTestStack(); + // new cdk.Stack(undefined, 'MyStack', { env: { region: 'us-east-1', account: '1234' }}); const vpc = mockVpc(stack); // WHEN @@ -378,27 +381,28 @@ export = { minSuccessfulInstancesPercent: 50, pauseTimeSec: 345 }, - tags: {superfood: 'acai'}, }); - asg.tags.setTag('notsuper', 'caramel', {propagate: false}); + asg.apply( new cdk.Tag('superfood', 'acai')); + asg.apply( new cdk.Tag('notsuper', 'caramel', { applyToLaunchInstances: false })); + stack.testInvokeAspects(); // THEN expect(stack).to(haveResource("AWS::AutoScaling::AutoScalingGroup", { Tags: [ { - Key: 'superfood', - Value: 'acai', + Key: 'Name', PropagateAtLaunch: true, + Value: 'MyFleet', }, { - Key: 'Name', - Value: 'MyFleet', + Key: 'superfood', PropagateAtLaunch: true, + Value: 'acai', }, { Key: 'notsuper', - Value: 'caramel', PropagateAtLaunch: false, + Value: 'caramel', }, ] })); @@ -421,3 +425,12 @@ function mockSecurityGroup(stack: cdk.Stack) { securityGroupId: 'most-secure', }); } + +class TestStack extends cdk.Stack { + public testInvokeAspects(): void { + this.invokeAspects(); + } +} +function getTestStack(): TestStack { + return new TestStack(undefined, 'TestStack', { env: { account: '1234', region: 'us-east-1' } }); +} diff --git a/packages/@aws-cdk/aws-autoscaling/test/test.scheduled-action.ts b/packages/@aws-cdk/aws-autoscaling/test/test.scheduled-action.ts index 8d7433e33a866..770c57cc29c6a 100644 --- a/packages/@aws-cdk/aws-autoscaling/test/test.scheduled-action.ts +++ b/packages/@aws-cdk/aws-autoscaling/test/test.scheduled-action.ts @@ -47,7 +47,7 @@ export = { 'autoscaling group has recommended updatepolicy for scheduled actions'(test: Test) { // GIVEN - const stack = new cdk.Stack(); + const stack = getTestStack(); const asg = makeAutoScalingGroup(stack); // WHEN @@ -56,6 +56,7 @@ export = { minCapacity: 10, }); + stack.testInvokeAspects(); // THEN expect(stack).toMatch({ Resources: { @@ -111,4 +112,13 @@ function makeAutoScalingGroup(scope: cdk.Construct) { machineImage: new ec2.AmazonLinuxImage(), updateType: autoscaling.UpdateType.RollingUpdate, }); -} \ No newline at end of file +} + +class TestStack extends cdk.Stack { + public testInvokeAspects(): void { + this.invokeAspects(); + } +} +function getTestStack(): TestStack { + return new TestStack(undefined, 'TestStack', { env: { account: '1234', region: 'us-east-1' } }); +} diff --git a/packages/@aws-cdk/aws-ec2/lib/security-group.ts b/packages/@aws-cdk/aws-ec2/lib/security-group.ts index 0a22448141753..66371dd9a722d 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, ITaggable, Output, TagManager, Tags, 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'; @@ -105,11 +105,6 @@ export interface SecurityGroupProps { */ description?: string; - /** - * The AWS resource tags to associate with the security group. - */ - tags?: Tags; - /** * The VPC in which to create the security group. */ @@ -134,7 +129,7 @@ export interface SecurityGroupProps { * inline ingress and egress rule (which saves on the total number of resources inside * the template). */ -export class SecurityGroup extends SecurityGroupBase implements ITaggable { +export class SecurityGroup extends SecurityGroupBase { /** * Import an existing SecurityGroup */ @@ -157,11 +152,6 @@ export class SecurityGroup extends SecurityGroupBase implements ITaggable { */ public readonly securityGroupId: string; - /** - * Manage tags for this construct and children - */ - public readonly tags: TagManager; - private readonly securityGroup: CfnSecurityGroup; private readonly directIngressRules: CfnSecurityGroup.IngressProperty[] = []; private readonly directEgressRules: CfnSecurityGroup.EgressProperty[] = []; @@ -171,7 +161,6 @@ export class SecurityGroup extends SecurityGroupBase implements ITaggable { constructor(scope: Construct, id: string, props: SecurityGroupProps) { super(scope, id); - this.tags = new TagManager(this, { initialTags: props.tags}); const groupDescription = props.description || this.node.path; this.allowAllOutbound = props.allowAllOutbound !== false; @@ -182,7 +171,6 @@ export class SecurityGroup extends SecurityGroupBase implements ITaggable { securityGroupIngress: new Token(() => this.directIngressRules), securityGroupEgress: new Token(() => this.directEgressRules), vpcId: props.vpc.vpcId, - tags: this.tags, }); this.securityGroupId = this.securityGroup.securityGroupId; diff --git a/packages/@aws-cdk/aws-ec2/lib/vpc.ts b/packages/@aws-cdk/aws-ec2/lib/vpc.ts index 93af5eb8ed121..cccde9be67550 100644 --- a/packages/@aws-cdk/aws-ec2/lib/vpc.ts +++ b/packages/@aws-cdk/aws-ec2/lib/vpc.ts @@ -47,11 +47,6 @@ export interface VpcNetworkProps { */ defaultInstanceTenancy?: DefaultInstanceTenancy; - /** - * The AWS resource tags to associate with the VPC. - */ - tags?: cdk.Tags; - /** * Define the maximum number of AZs to use in this region * @@ -162,11 +157,6 @@ export interface SubnetConfiguration { * availability zone. */ name: string; - - /** - * The AWS resource tags to associate with the resource. - */ - tags?: cdk.Tags; } /** @@ -189,7 +179,7 @@ export interface SubnetConfiguration { * * } */ -export class VpcNetwork extends VpcNetworkBase implements cdk.ITaggable { +export class VpcNetwork extends VpcNetworkBase { /** * @returns The IPv4 CidrBlock as returned by the VPC */ @@ -259,11 +249,6 @@ export class VpcNetwork extends VpcNetworkBase implements cdk.ITaggable { */ public readonly availabilityZones: string[]; - /** - * Manage tags for this construct and children - */ - public readonly tags: cdk.TagManager; - /** * The VPC resource */ @@ -298,9 +283,6 @@ export class VpcNetwork extends VpcNetworkBase implements cdk.ITaggable { throw new Error('To use DNS Hostnames, DNS Support must be enabled, however, it was explicitly disabled.'); } - this.tags = new cdk.TagManager(this, { initialTags: props.tags}); - this.tags.setTag(NAME_TAG, this.node.path, { overwrite: false }); - const cidrBlock = ifUndefined(props.cidr, VpcNetwork.DEFAULT_CIDR_RANGE); this.networkBuilder = new NetworkBuilder(cidrBlock); @@ -314,9 +296,10 @@ export class VpcNetwork extends VpcNetworkBase implements cdk.ITaggable { enableDnsHostnames, enableDnsSupport, instanceTenancy, - tags: this.tags, }); + this.apply(new cdk.Tag(NAME_TAG, this.node.path)); + this.availabilityZones = new cdk.AvailabilityZoneProvider(this).availabilityZones; this.availabilityZones.sort(); @@ -336,7 +319,6 @@ export class VpcNetwork extends VpcNetworkBase implements cdk.ITaggable { // Create an Internet Gateway and attach it if necessary if (allowOutbound) { const igw = new CfnInternetGateway(this, 'IGW', { - tags: new cdk.TagManager(this), }); this.internetDependencies.push(igw); const att = new CfnVPCGatewayAttachment(this, 'VPCGW', { @@ -444,7 +426,6 @@ export class VpcNetwork extends VpcNetworkBase implements cdk.ITaggable { vpcId: this.vpcId, cidrBlock: this.networkBuilder.addSubnet(cidrMask), mapPublicIpOnLaunch: (subnetConfig.subnetType === SubnetType.Public), - tags: subnetConfig.tags, }; let subnet: VpcSubnet; @@ -461,7 +442,6 @@ export class VpcNetwork extends VpcNetworkBase implements cdk.ITaggable { break; case SubnetType.Isolated: const isolatedSubnet = new VpcPrivateSubnet(this, name, subnetProps); - isolatedSubnet.tags.setTag(SUBNETTYPE_TAG, subnetTypeTagValue(subnetConfig.subnetType)); this.isolatedSubnets.push(isolatedSubnet); subnet = isolatedSubnet; break; @@ -470,8 +450,9 @@ export class VpcNetwork extends VpcNetworkBase implements cdk.ITaggable { } // These values will be used to recover the config upon provider import - subnet.tags.setTag(SUBNETNAME_TAG, subnetConfig.name, { propagate: false }); - subnet.tags.setTag(SUBNETTYPE_TAG, subnetTypeTagValue(subnetConfig.subnetType), { propagate: false }); + const include = [CfnSubnet.resourceTypeName]; + subnet.apply(new cdk.Tag(SUBNETNAME_TAG, subnetConfig.name, {include})); + subnet.apply(new cdk.Tag(SUBNETTYPE_TAG, subnetTypeTagValue(subnetConfig.subnetType), {include})); }); } } @@ -513,17 +494,12 @@ export interface VpcSubnetProps { * Defaults to true in Subnet.Public, false in Subnet.Private or Subnet.Isolated. */ mapPublicIpOnLaunch?: boolean; - - /** - * The AWS resource tags to associate with the Subnet - */ - tags?: cdk.Tags; } /** * Represents a new VPC subnet resource */ -export class VpcSubnet extends cdk.Construct implements IVpcSubnet, cdk.ITaggable, cdk.IDependable { +export class VpcSubnet extends cdk.Construct implements IVpcSubnet, cdk.IDependable { public static import(scope: cdk.Construct, id: string, props: VpcSubnetImportProps): IVpcSubnet { return new ImportedVpcSubnet(scope, id, props); } @@ -538,11 +514,6 @@ export class VpcSubnet extends cdk.Construct implements IVpcSubnet, cdk.ITaggabl */ public readonly subnetId: string; - /** - * Manage tags for Construct and propagate to children - */ - public readonly tags: cdk.TagManager; - /** * Parts of this VPC subnet */ @@ -555,8 +526,7 @@ export class VpcSubnet extends cdk.Construct implements IVpcSubnet, cdk.ITaggabl constructor(scope: cdk.Construct, id: string, props: VpcSubnetProps) { super(scope, id); - this.tags = new cdk.TagManager(this, {initialTags: props.tags}); - this.tags.setTag(NAME_TAG, this.node.path, {overwrite: false}); + this.apply(new cdk.Tag(NAME_TAG, this.node.path)); this.availabilityZone = props.availabilityZone; const subnet = new CfnSubnet(this, 'Subnet', { @@ -564,12 +534,10 @@ export class VpcSubnet extends cdk.Construct implements IVpcSubnet, cdk.ITaggabl cidrBlock: props.cidrBlock, availabilityZone: props.availabilityZone, mapPublicIpOnLaunch: props.mapPublicIpOnLaunch, - tags: this.tags, }); this.subnetId = subnet.subnetId; const table = new CfnRouteTable(this, 'RouteTable', { vpcId: props.vpcId, - tags: new cdk.TagManager(this), }); this.routeTableId = table.ref; @@ -643,7 +611,6 @@ export class VpcPublicSubnet extends VpcSubnet { allocationId: new CfnEIP(this, `EIP`, { domain: 'vpc' }).eipAllocationId, - tags: new cdk.TagManager(this), }); return ngw.natGatewayId; } diff --git a/packages/@aws-cdk/aws-ec2/test/test.vpc.ts b/packages/@aws-cdk/aws-ec2/test/test.vpc.ts index ec8aa87d1403e..4c9767d332a87 100644 --- a/packages/@aws-cdk/aws-ec2/test/test.vpc.ts +++ b/packages/@aws-cdk/aws-ec2/test/test.vpc.ts @@ -1,7 +1,7 @@ import { countResources, expect, haveResource, haveResourceLike, isSuperObject } from '@aws-cdk/assert'; -import { AvailabilityZoneProvider, Construct, Stack, Tags } from '@aws-cdk/cdk'; +import { AvailabilityZoneProvider, Construct, Stack, Tag } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; -import { DefaultInstanceTenancy, IVpcNetwork, SubnetType, VpcNetwork } from '../lib'; +import { CfnVPC, DefaultInstanceTenancy, IVpcNetwork, SubnetType, VpcNetwork } from '../lib'; export = { "When creating a VPC": { @@ -28,8 +28,15 @@ export = { 'the Name tag is defaulted to path'(test: Test) { const stack = getTestStack(); new VpcNetwork(stack, 'TheVPC'); - expect(stack).to(haveResource('AWS::EC2::VPC', - hasTags( [ {Key: 'Name', Value: 'TheVPC'} ]))); + stack.testInvokeAspects(); + expect(stack).to( + haveResource('AWS::EC2::VPC', + hasTags( [ {Key: 'Name', Value: 'TheVPC'} ])) + ); + expect(stack).to( + haveResource('AWS::EC2::InternetGateway', + hasTags( [ {Key: 'Name', Value: 'TheVPC'} ])) + ); test.done(); }, @@ -37,28 +44,19 @@ export = { "with all of the properties set, it successfully sets the correct VPC properties"(test: Test) { const stack = getTestStack(); - const tags = { - first: 'foo', - second: 'bar', - third: 'barz', - - }; new VpcNetwork(stack, 'TheVPC', { cidr: "192.168.0.0/16", enableDnsHostnames: false, enableDnsSupport: false, defaultInstanceTenancy: DefaultInstanceTenancy.Dedicated, - tags, }); - const cfnTags = toCfnTags(tags); expect(stack).to(haveResource('AWS::EC2::VPC', { CidrBlock: '192.168.0.0/16', EnableDnsHostnames: false, EnableDnsSupport: false, InstanceTenancy: DefaultInstanceTenancy.Dedicated, })); - expect(stack).to(haveResource('AWS::EC2::VPC', hasTags(cfnTags))); test.done(); }, @@ -124,25 +122,21 @@ export = { new VpcNetwork(stack, 'TheVPC', { cidr: '10.0.0.0/21', subnetConfiguration: [ - { - cidrMask: 24, - name: 'ingress', - subnetType: SubnetType.Public, - tags: { - type: 'Public', - init: 'No', + { + cidrMask: 24, + name: 'ingress', + subnetType: SubnetType.Public, }, - }, - { - cidrMask: 24, - name: 'application', - subnetType: SubnetType.Private, - }, - { - cidrMask: 28, - name: 'rds', - subnetType: SubnetType.Isolated, - } + { + cidrMask: 24, + name: 'application', + subnetType: SubnetType.Private, + }, + { + cidrMask: 28, + name: 'rds', + subnetType: SubnetType.Isolated, + } ], maxAZs: 3 }); @@ -151,20 +145,14 @@ export = { expect(stack).to(countResources("AWS::EC2::Subnet", 9)); for (let i = 0; i < 6; i++) { expect(stack).to(haveResource("AWS::EC2::Subnet", { - CidrBlock: `10.0.${i}.0/24` + CidrBlock: `10.0.${i}.0/24` })); } for (let i = 0; i < 3; i++) { expect(stack).to(haveResource("AWS::EC2::Subnet", { - CidrBlock: `10.0.6.${i * 16}/28` + CidrBlock: `10.0.6.${i * 16}/28` })); } - expect(stack).to(haveResource("AWS::EC2::Subnet", hasTags( - [ - { Key: 'type', Value: 'Public'}, - { Key: 'init', Value: 'No'}, - ], - ))); test.done(); }, "with custom subents and natGateways = 2 there should be only two NATGW"(test: Test) { @@ -306,16 +294,13 @@ export = { subnetName: 'egress' }, }); + stack.testInvokeAspects(); expect(stack).to(countResources("AWS::EC2::NatGateway", 3)); for (let i = 1; i < 4; i++) { - expect(stack).to(haveResource("AWS::EC2::NatGateway", { - Tags: [ - { - Key: 'Name', - Value: `VPC/egressSubnet${i}`, - } - ] - })); + expect(stack).to(haveResource('AWS::EC2::Subnet', hasTags([{ + Key: 'Name', + Value: `VPC/egressSubnet${i}`, + }]))); } test.done(); }, @@ -362,11 +347,13 @@ export = { const noPropTags = { BusinessUnit: 'Marketing', }; - const allTags: Tags = {...tags, ...noPropTags}; + const allTags = {...tags, ...noPropTags}; - const vpc = new VpcNetwork(stack, 'TheVPC', { tags: allTags }); + const vpc = new VpcNetwork(stack, 'TheVPC'); // overwrite to set propagate - vpc.tags.setTag('BusinessUnit', 'Marketing', {propagate: false}); + vpc.apply(new Tag('BusinessUnit', 'Marketing', {include: [CfnVPC.resourceTypeName]})); + vpc.apply(new Tag('VpcType', 'Good')); + stack.testInvokeAspects(); expect(stack).to(haveResource("AWS::EC2::VPC", hasTags(toCfnTags(allTags)))); const taggables = ['Subnet', 'InternetGateway', 'NatGateway', 'RouteTable']; const propTags = toCfnTags(tags); @@ -380,6 +367,7 @@ export = { 'Subnet Name will propagate to route tables and NATGW'(test: Test) { const stack = getTestStack(); const vpc = new VpcNetwork(stack, 'TheVPC'); + stack.testInvokeAspects(); for (const subnet of vpc.publicSubnets) { const tag = {Key: 'Name', Value: subnet.node.path}; expect(stack).to(haveResource('AWS::EC2::NatGateway', hasTags([tag]))); @@ -393,10 +381,13 @@ export = { }, 'Tags can be added after the Vpc is created with `vpc.tags.setTag(...)`'(test: Test) { const stack = getTestStack(); + const vpc = new VpcNetwork(stack, 'TheVPC'); const tag = {Key: 'Late', Value: 'Adder'}; + stack.testInvokeAspects(); expect(stack).notTo(haveResource('AWS::EC2::VPC', hasTags([tag]))); - vpc.tags.setTag(tag.Key, tag.Value); + vpc.apply(new Tag(tag.Key, tag.Value)); + stack.testInvokeAspects(); expect(stack).to(haveResource('AWS::EC2::VPC', hasTags([tag]))); test.done(); }, @@ -545,8 +536,14 @@ export = { }, }; -function getTestStack(): Stack { - return new Stack(undefined, 'TestStack', { env: { account: '123456789012', region: 'us-east-1' } }); +class TestStack extends Stack { + public testInvokeAspects(): void { + this.invokeAspects(); + } +} + +function getTestStack(): TestStack { + return new TestStack(undefined, 'TestStack', { env: { account: '123456789012', region: 'us-east-1' } }); } /** @@ -563,7 +560,7 @@ function doImportExportTest(constructFn: (scope: Construct) => VpcNetwork): IVpc return VpcNetwork.import(stack2, 'VPC2', vpc1.export()); } -function toCfnTags(tags: Tags): Array<{Key: string, Value: string}> { +function toCfnTags(tags: any): Array<{Key: string, Value: string}> { return Object.keys(tags).map( key => { return {Key: key, Value: tags[key]}; }); @@ -586,8 +583,11 @@ function hasTags(expectedTags: Array<{Key: string, Value: string}>): (props: any }); return actualTags.length === expectedTags.length; } catch (e) { - // tslint:disable-next-line:no-console - console.error('Invalid Tags array in ', props); + // tslint:disable:no-console + console.error('Tags are incorrect'); + console.error('found tags ', props.Tags); + console.error('expected tags ', expectedTags); + // tslint:enable:no-console throw e; } }; diff --git a/packages/@aws-cdk/aws-kms/lib/key.ts b/packages/@aws-cdk/aws-kms/lib/key.ts index 7b259bfb8fc25..20c8f4506a77b 100644 --- a/packages/@aws-cdk/aws-kms/lib/key.ts +++ b/packages/@aws-cdk/aws-kms/lib/key.ts @@ -1,5 +1,5 @@ import { PolicyDocument, PolicyStatement } from '@aws-cdk/aws-iam'; -import { Construct, DeletionPolicy, IConstruct, Output, TagManager, Tags } from '@aws-cdk/cdk'; +import { Construct, DeletionPolicy, IConstruct, Output } from '@aws-cdk/cdk'; import { EncryptionKeyAlias } from './alias'; import { CfnKey } from './kms.generated'; @@ -106,11 +106,6 @@ export interface EncryptionKeyProps { * administer the key will be created. */ policy?: PolicyDocument; - - /** - * The AWS resource tags to associate with the KMS key. - */ - tags?: Tags; } /** @@ -139,11 +134,6 @@ export class EncryptionKey extends EncryptionKeyBase { return new ImportedEncryptionKey(scope, id, props); } - /** - * Manage tags for this construct and children - */ - public readonly tags: TagManager; - public readonly keyArn: string; protected readonly policy?: PolicyDocument; @@ -157,14 +147,11 @@ export class EncryptionKey extends EncryptionKeyBase { this.allowAccountToAdmin(); } - this.tags = new TagManager(this, { initialTags: props.tags }); - const resource = new CfnKey(this, 'Resource', { description: props.description, enableKeyRotation: props.enableKeyRotation, enabled: props.enabled, keyPolicy: this.policy, - tags: this.tags }); this.keyArn = resource.keyArn; diff --git a/packages/@aws-cdk/aws-kms/test/test.key.ts b/packages/@aws-cdk/aws-kms/test/test.key.ts index 155347aa7a225..7ba07a37e171e 100644 --- a/packages/@aws-cdk/aws-kms/test/test.key.ts +++ b/packages/@aws-cdk/aws-kms/test/test.key.ts @@ -1,6 +1,6 @@ import { exactlyMatchTemplate, expect } from '@aws-cdk/assert'; import { PolicyDocument, PolicyStatement } from '@aws-cdk/aws-iam'; -import { App, Stack } from '@aws-cdk/cdk'; +import { App, Stack, Tag } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import { EncryptionKey } from '../lib'; @@ -138,97 +138,99 @@ export = { }, 'key with some options'(test: Test) { + // const app = getTestApp(); const app = new App(); - const stack = new Stack(app, 'Test'); + // const stack = new Stack(app, 'Test'); + const stack = new TestStack(app, 'Test'); const key = new EncryptionKey(stack, 'MyKey', { enableKeyRotation: true, enabled: false, - tags: { - tag1: 'value1', - tag2: 'value2', - tag3: '' - } }); const p = new PolicyStatement().addAllResources().addAction('kms:encrypt'); p.addAwsPrincipal('arn'); key.addToResourcePolicy(p); + key.apply(new Tag('tag1', 'value1')); + key.apply(new Tag('tag2', 'value2')); + key.apply(new Tag('tag3', '')); + // app.testInvokeAspects(); + stack.testInvokeAspects(); expect(app.synthesizeStack(stack.name)).to(exactlyMatchTemplate({ Resources: { MyKey6AB29FA6: { - Type: "AWS::KMS::Key", - Properties: { - Enabled: false, - EnableKeyRotation: true, - KeyPolicy: { - Statement: [ - { - Action: [ - "kms:Create*", - "kms:Describe*", - "kms:Enable*", - "kms:List*", - "kms:Put*", - "kms:Update*", - "kms:Revoke*", - "kms:Disable*", - "kms:Get*", - "kms:Delete*", - "kms:ScheduleKeyDeletion", - "kms:CancelKeyDeletion" - ], - Effect: "Allow", - Principal: { - AWS: { - "Fn::Join": [ - "", - [ - "arn:", + Type: "AWS::KMS::Key", + Properties: { + KeyPolicy: { + Statement: [ { - Ref: "AWS::Partition" + Action: [ + "kms:Create*", + "kms:Describe*", + "kms:Enable*", + "kms:List*", + "kms:Put*", + "kms:Update*", + "kms:Revoke*", + "kms:Disable*", + "kms:Get*", + "kms:Delete*", + "kms:ScheduleKeyDeletion", + "kms:CancelKeyDeletion" + ], + Effect: "Allow", + Principal: { + AWS: { + "Fn::Join": [ + "", + [ + "arn:", + { + Ref: "AWS::Partition" + }, + ":iam::", + { + Ref: "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + Resource: '*' }, - ":iam::", { - Ref: "AWS::AccountId" - }, - ":root" - ] - ] - } - }, - Resource: '*' - }, - { - Action: "kms:encrypt", - Effect: "Allow", - Principal: { - AWS: "arn" + Action: "kms:encrypt", + Effect: "Allow", + Principal: { + AWS: "arn" + }, + Resource: "*" + } + ], + Version: "2012-10-17" }, - Resource: "*" - } - ], - Version: "2012-10-17" + Enabled: false, + EnableKeyRotation: true, + Tags: [ + { + Key: "tag1", + Value: "value1" + }, + { + Key: "tag2", + Value: "value2" + }, + { + Key: "tag3", + Value: "" + } + ] }, - Tags: [ - { - Key: "tag1", - Value: "value1" - }, - { - Key: "tag2", - Value: "value2" - }, - { - Key: "tag3", - Value: "" - } - ] - }, - DeletionPolicy: "Retain" + DeletionPolicy: "Retain" } } - })); + })); test.done(); }, @@ -247,67 +249,67 @@ export = { test.deepEqual(app.synthesizeStack(stack.name).template, { Resources: { - MyKey6AB29FA6: { - Type: "AWS::KMS::Key", - Properties: { - EnableKeyRotation: true, - Enabled: false, - KeyPolicy: { - Statement: [ - { - Action: [ - "kms:Create*", - "kms:Describe*", - "kms:Enable*", - "kms:List*", - "kms:Put*", - "kms:Update*", - "kms:Revoke*", - "kms:Disable*", - "kms:Get*", - "kms:Delete*", - "kms:ScheduleKeyDeletion", - "kms:CancelKeyDeletion" - ], - Effect: "Allow", - Principal: { - AWS: { - "Fn::Join": [ - "", - [ - "arn:", - { - Ref: "AWS::Partition" - }, - ":iam::", + MyKey6AB29FA6: { + Type: "AWS::KMS::Key", + Properties: { + EnableKeyRotation: true, + Enabled: false, + KeyPolicy: { + Statement: [ { - Ref: "AWS::AccountId" - }, - ":root" - ] + Action: [ + "kms:Create*", + "kms:Describe*", + "kms:Enable*", + "kms:List*", + "kms:Put*", + "kms:Update*", + "kms:Revoke*", + "kms:Disable*", + "kms:Get*", + "kms:Delete*", + "kms:ScheduleKeyDeletion", + "kms:CancelKeyDeletion" + ], + Effect: "Allow", + Principal: { + AWS: { + "Fn::Join": [ + "", + [ + "arn:", + { + Ref: "AWS::Partition" + }, + ":iam::", + { + Ref: "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + Resource: "*" + } + ], + Version: "2012-10-17" + } + }, + DeletionPolicy: "Retain" + }, + MyKeyAlias1B45D9DA: { + Type: "AWS::KMS::Alias", + Properties: { + AliasName: "alias/xoo", + TargetKeyId: { + "Fn::GetAtt": [ + "MyKey6AB29FA6", + "Arn" ] } - }, - Resource: "*" } - ], - Version: "2012-10-17" } - }, - DeletionPolicy: "Retain" - }, - MyKeyAlias1B45D9DA: { - Type: "AWS::KMS::Alias", - Properties: { - AliasName: "alias/xoo", - TargetKeyId: { - "Fn::GetAtt": [ - "MyKey6AB29FA6", - "Arn" - ] - } - } - } } }); @@ -362,16 +364,16 @@ export = { expect(stack2).toMatch({ Resources: { - MyKeyImportedAliasB1C5269F: { - Type: "AWS::KMS::Alias", - Properties: { - AliasName: "alias/hello", - TargetKeyId: { - "Fn::ImportValue": "MyKeyKeyArn317F1332" - } + MyKeyImportedAliasB1C5269F: { + Type: "AWS::KMS::Alias", + Properties: { + AliasName: "alias/hello", + TargetKeyId: { + "Fn::ImportValue": "MyKeyKeyArn317F1332" + } + } } } - } }); test.done(); @@ -403,3 +405,9 @@ export = { } } }; + +class TestStack extends Stack { + public testInvokeAspects(): void { + this.invokeAspects(); + } +} diff --git a/packages/@aws-cdk/cdk/lib/app.ts b/packages/@aws-cdk/cdk/lib/app.ts index dc38817aafba8..2d985ab006c2d 100644 --- a/packages/@aws-cdk/cdk/lib/app.ts +++ b/packages/@aws-cdk/cdk/lib/app.ts @@ -41,6 +41,9 @@ export class App extends Root { return; } + // invoke all aspects before rendering + this.invokeAspects(); + const result: cxapi.SynthesizeResponse = { version: cxapi.PROTO_RESPONSE_VERSION, stacks: this.synthesizeStacks(Object.keys(this.stacks)), @@ -232,4 +235,4 @@ function getJsiiAgentVersion() { function noEmptyArray(xs: T[]): T[] | undefined { return xs.length > 0 ? xs : undefined; -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/cdk/lib/aspects/aspect.ts b/packages/@aws-cdk/cdk/lib/aspects/aspect.ts new file mode 100644 index 0000000000000..b54e87303da9b --- /dev/null +++ b/packages/@aws-cdk/cdk/lib/aspects/aspect.ts @@ -0,0 +1,38 @@ +import { IConstruct } from '../core/construct'; + +/** + * Represents an Aspect + */ +export interface IAspect { + /** + * The type of Aspect + */ + readonly type: string; + + /** + * All aspects can visit by IConstructs + */ + visit(node: IConstruct): void; +} + +/** + * TODO: description + */ +export abstract class Aspect implements IAspect { + public abstract readonly type: string; + private readonly visitedBy: {[id: string]: boolean} = {}; + + public visit(construct: IConstruct): void { + if (this.visitedBy[construct.node.uniqueId] === true) { + return; + } + this.visitedBy[construct.node.uniqueId] = true; + this.visitAction(construct); + for (const child of construct.node.children) { + // recurse through all children + this.visit(child); + } + } + + protected abstract visitAction(construct: IConstruct): void; +} diff --git a/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts b/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts new file mode 100644 index 0000000000000..fdbc572251266 --- /dev/null +++ b/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts @@ -0,0 +1,86 @@ +import { ITaggable, Resource } from '../cloudformation/resource'; +import { IConstruct } from '../core/construct'; +import { Aspect } from './aspect'; + +export interface TagBaseProps extends TagProperties { + // TODO docs + value?: string; +} + +export interface TagProperties { + // TODO docs + applyToLaunchInstances?: boolean; + include?: string[]; + exclude?: string[]; +} + +export abstract class TagBase extends Aspect { + + public static isResource(resource: any): resource is Resource { + return resource.resourceType !== undefined; + } + + public readonly type: string = 'taggable'; + + public readonly key: string; + public readonly value?: string; + + private readonly include: string[]; + private readonly exclude: string[]; + + constructor(key: string, props: TagBaseProps = {}) { + super(); + this.key = key; + + this.value = props.value; + this.include = props.include || []; + this.exclude = props.exclude || []; + } + + protected visitAction(construct: IConstruct): void { + if (!TagBase.isResource(construct)) { + return; + } + const resource = construct as Resource; + if (Resource.isTaggable(resource)) { + if (this.exclude.length !== 0 && this.exclude.indexOf(resource.resourceType) !== -1) { + return; + } + if (this.include.length !== 0 && this.include.indexOf(resource.resourceType) === -1) { + return; + } + this.applyTag(resource); + } + } + + protected abstract applyTag(resource: ITaggable): void; +} + +export class Tag extends TagBase { + + private readonly applyToLaunchInstances: boolean; + + constructor(key: string, value: string, props: TagProperties = {}) { + super(key, {value, ...props}); + this.applyToLaunchInstances = props.applyToLaunchInstances !== false; + if (this.value === undefined) { + throw new Error('Tag must have a value'); + } + } + + protected applyTag(resource: ITaggable) { + resource.tags.setTag(this.key, this.value!, {applyToLaunchInstances: this.applyToLaunchInstances}); + } +} + +export class RemoveTag extends TagBase { + + constructor(key: string, props: TagProperties = {}) { + super(key, props); + } + + protected applyTag(resource: ITaggable): void { + resource.tags.removeTag(this.key); + return; + } +} diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/resource.ts b/packages/@aws-cdk/cdk/lib/cloudformation/resource.ts index a2a4243dde2ac..390b1dcd5175e 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/resource.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/resource.ts @@ -1,5 +1,6 @@ import cxapi = require('@aws-cdk/cx-api'); import { Construct } from '../core/construct'; +import { TagManager } from '../core/tag-manager'; import { capitalizePropertyNames, ignoreEmpty } from '../core/util'; import { CfnReference } from './cfn-tokens'; import { Condition } from './condition'; @@ -18,6 +19,12 @@ export interface ResourceProps { properties?: any; } +export interface ITaggable { + /** + * TagManager to set, remove and format tags + */ + readonly tags: TagManager; +} /** * Represents a CloudFormation resource. */ @@ -38,6 +45,13 @@ export class Resource extends Referenceable { }; } + /** + * determines if the reosurce is taggable + */ + public static isTaggable(resource: any): resource is ITaggable { + return resource.tags !== undefined; + } + /** * Options for this resource, such as condition, update policy etc. */ @@ -48,6 +62,13 @@ export class Resource extends Referenceable { */ public readonly resourceType: string; + /** + * AWS resource properties. + * + * This object is rendered via a call to "renderProperties(this.properties)". + */ + public readonly properties: any; + /** * AWS resource property overrides. * @@ -60,13 +81,6 @@ export class Resource extends Referenceable { */ protected readonly untypedPropertyOverrides: any = { }; - /** - * AWS resource properties. - * - * This object is rendered via a call to "renderProperties(this.properties)". - */ - protected readonly properties: any; - /** * An object to be merged on top of the entire resource definition. */ @@ -180,6 +194,10 @@ export class Resource extends Referenceable { */ public toCloudFormation(): object { try { + if (Resource.isTaggable(this)) { + const tags = this.tags.renderTags(); + this.properties.tags = tags === undefined ? this.properties.tags : tags; + } // merge property overrides onto properties and then render (and validate). const properties = this.renderProperties(deepMerge(this.properties || { }, this.untypedPropertyOverrides)); @@ -239,6 +257,13 @@ export class Resource extends Referenceable { } } +export enum TagType { + Standard = 'StandardTag', + AutoScalingGroup = 'AutoScalingGroupTag', + Map = 'StringToStringMap', + NotTaggable = 'NotTaggable', +} + export interface ResourceOptions { /** * A condition to associate with this resource. This means that only if the condition evaluates to 'true' when the stack diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/tag.ts b/packages/@aws-cdk/cdk/lib/cloudformation/tag.ts index 26b13b0b41d28..a05aa282335b9 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/tag.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/tag.ts @@ -1,7 +1,7 @@ /** * @link http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resource-tags.html */ -export interface Tag { +export interface CfnTag { /** * @link http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resource-tags.html#cfn-resource-tags-key */ diff --git a/packages/@aws-cdk/cdk/lib/core/construct.ts b/packages/@aws-cdk/cdk/lib/core/construct.ts index e6d2c76e67ba9..844d7740c2a7d 100644 --- a/packages/@aws-cdk/cdk/lib/core/construct.ts +++ b/packages/@aws-cdk/cdk/lib/core/construct.ts @@ -1,4 +1,5 @@ import cxapi = require('@aws-cdk/cx-api'); +import { IAspect } from '../aspects/aspect'; import { CloudFormationJSON } from '../cloudformation/cloudformation-json'; import { makeUniqueId } from '../util/uniqueid'; import { Token, unresolved } from './tokens'; @@ -31,6 +32,11 @@ export class ConstructNode { */ public readonly id: string; + /** + * An array of aspects applied to this node + */ + public readonly aspects: IAspect[] = []; + /** * List of children and their names */ @@ -504,6 +510,14 @@ export class Construct implements IConstruct { return this.node.typename + (path.length > 0 ? ` [${path}]` : ''); } + /** + * Applies the aspect to this Constructs node + */ + public apply(aspect: IAspect): void { + this.node.aspects.push(aspect); + return; + } + /** * Validate the current construct. * @@ -529,6 +543,18 @@ export class Construct implements IConstruct { protected prepare(): void { // Intentionally left blank } + + /** + * Triggers each aspect to invoke visit + */ + protected invokeAspects(): void { + for (const aspect of this.node.aspects) { + aspect.visit(this); + } + for (const child of this.node.children) { + (child as Construct).invokeAspects(); + } + } } /** diff --git a/packages/@aws-cdk/cdk/lib/core/tag-manager.ts b/packages/@aws-cdk/cdk/lib/core/tag-manager.ts index 7f5f6c57d3814..a0d01b668d341 100644 --- a/packages/@aws-cdk/cdk/lib/core/tag-manager.ts +++ b/packages/@aws-cdk/cdk/lib/core/tag-manager.ts @@ -1,273 +1,85 @@ -import { Construct, IConstruct } from './construct'; -import { ResolveContext, Token } from './tokens'; - -/** - * ITaggable indicates a entity manages tags via the `tags` property - */ -export interface ITaggable { - readonly tags: TagManager, -} +import { TagType } from '../cloudformation/resource'; /** * Properties Tags is a dictionary of tags as strings */ -export type Tags = { [key: string]: string }; - -/** - * An object of tags with value and properties - * - * This is used internally but not exported - */ -interface FullTags { - [key: string]: {value: string, props?: TagProps}; -} +type Tags = { [key: string]: {value: string, props: TagProps }}; /** * Properties for a tag */ export interface TagProps { /** - * If true all child taggable `Constructs` will receive this tag - * - * @default true - */ - propagate?: boolean; - - /** - * If set propagated tags from parents will not overwrite the tag - * - * @default true - */ - sticky?: boolean; - - /** - * If set this tag will overwrite existing tags - * - * @default true - */ - overwrite?: boolean; -} - -/** - * This is the interface for arguments to `tagFormatResolve` to enable extensions - */ -export interface TagGroups { - /** - * Tags that overwrite ancestor tags - */ - stickyTags: Tags; - - /** - * Tags that are overwritten by ancestor tags - */ - nonStickyTags: Tags; - - /** - * Tags with propagate true not from an ancestor - */ - propagateTags: Tags; - - /** - * Tags that are propagated from ancestors - */ - ancestorTags: Tags; -} - -/** - * Properties for removing tags - */ -export interface RemoveProps { - /** - * If true prevent this tag form being set via propagation - * - * @default true - */ - blockPropagate?: boolean; -} - -/** - * Properties for Tag Manager - */ -export interface TagManagerProps { - /** - * Initial tags to set on the tag manager using TAG_DEFAULTS + * Handles AutoScalingGroup PropagateAtLaunch property */ - initialTags?: Tags; + applyToLaunchInstances: boolean; } /** * TagManager facilitates a common implementation of tagging for Constructs. - * - * Each construct that wants to support tags should implement the `ITaggable` - * interface and properly pass tags to the `Resources` (Cloudformation) elements - * the `Construct` creates. The `TagManager` extends `Token` the object can be - * passed directly to `Resources` that support tag properties. - * - * There are a few standard use cases the `TagManager` supports for managing - * tags across the resources in your stack. - * - * Propagation: If you tag a resource and it has children, by default those tags - * will be propagated to the children. This is controlled by - * `TagProps.propagate`. - * - * Default a tag unless an ancestor has a value: There are situations where a - * construct author might want to set a tag value, but choose to take a parents - * value. For example, you might default `{Key: "Compliance", Value: "None"}`, - * but if a parent has `{Key: "Compliance", Value: "PCI"}` allow that parent to - * override your tag. This is can be done by setting `TagProps.sticky` to false. - * The default behavior is that child tags have precedence and `TagProps.sticky` - * defaults to true to reflect this. - * - * Overwrite: Construct authors have the need to set a tag, but only if one was - * not provided by the consumer. The most common example is the `Name` tag. - * Overwrite is for this purpose and is controlled by `TagProps.overwrite`. The - * default is `true`. - * - * Removing Tags: Tags can be removed from the local manager via `removeTag`. If - * a parent also has a tag with the same name then it can be propagated to the - * child (after removal). The user can control this `RemoveProps.blockPropagate`. By default - * this is `true` and prevents a parent tag from propagating to the child after - * the `removeTag` is invoked. However, if user wants the parent tag to - * propagate, if it is provided by a parent this can be set to `false`. */ -export class TagManager extends Token { +export class TagManager { - /** - * Checks if the object implements the `ITaggable` interface - */ - public static isTaggable(taggable: ITaggable | any): taggable is ITaggable { - return ((taggable as ITaggable).tags !== undefined); - } - - private static readonly DEFAULT_TAG_PROPS: TagProps = { - propagate: true, - sticky: true, - overwrite: true - }; + private readonly tags: Tags = {}; - /* - * Internally tags will have properties set - */ - private readonly _tags: FullTags = {}; - - /* - * Tags that will be removed during `tags` method - */ - private readonly blockedTags: string[] = []; - - constructor(private readonly scope: Construct, props: TagManagerProps = {}) { - super(); - - const initialTags = props.initialTags || {}; - for (const key of Object.keys(initialTags)) { - const tag = { - value: initialTags[key], - props: TagManager.DEFAULT_TAG_PROPS, - }; - this._tags[key] = tag; - } - } - - /** - * Converts the `tags` to a Token for use in lazy evaluation - */ - public resolve(_context: ResolveContext): any { - // need this for scoping - const blockedTags = this.blockedTags; - function filterTags(_tags: FullTags, filter: TagProps = {}): Tags { - const filteredTags: Tags = {}; - Object.keys(_tags).map( key => { - let filterResult = true; - const props: TagProps = _tags[key].props || {}; - if (filter.propagate !== undefined) { - filterResult = filterResult && (filter.propagate === props.propagate); - } - if (filter.sticky !== undefined) { - filterResult = filterResult && - (filter.sticky === props.sticky); - } - if (filter.overwrite !== undefined) { - filterResult = filterResult && (filter.overwrite === props.overwrite); - } - if (filterResult) { - filteredTags[key] = _tags[key].value; - } - }); - for (const key of blockedTags) { delete filteredTags[key]; } - return filteredTags; - } - - function propagatedTags(tagProviders: IConstruct[]): Tags { - const parentTags: Tags = {}; - for (const ancestor of tagProviders) { - if (TagManager.isTaggable(ancestor)) { - const tagsFrom = filterTags(ancestor.tags._tags, {propagate: true}); - Object.assign(parentTags, tagsFrom); - } - } - for (const key of blockedTags) { delete parentTags[key]; } - return parentTags; - } - - const nonStickyTags = filterTags(this._tags, {sticky: false}); - const stickyTags = filterTags(this._tags, {sticky: true}); - const ancestors = this.scope.node.ancestors(); - const ancestorTags = propagatedTags(ancestors); - const propagateTags = filterTags(this._tags, {propagate: true}); - return this.tagFormatResolve( { - ancestorTags, - nonStickyTags, - stickyTags, - propagateTags, - }); - } + constructor(private readonly tagType: TagType) { } /** * Adds the specified tag to the array of tags * * @param key The key value of the tag * @param value The value value of the tag - * @param props A `TagProps` object for the tag @default `TagManager.DEFAULT_TAG_PROPS` + * @param props A `TagProps` defaulted to applyToLaunchInstances true */ - public setTag(key: string, value: string, tagProps: TagProps = {}): void { - const props = {...TagManager.DEFAULT_TAG_PROPS, ...tagProps}; - if (!props.overwrite) { - this._tags[key] = this._tags[key] || {value, props}; - } else { - this._tags[key] = {value, props}; - } - const index = this.blockedTags.indexOf(key); - if (index > -1) { - this.blockedTags.splice(index, 1); - } + public setTag(key: string, value: string, props: TagProps = {applyToLaunchInstances: true}): void { + this.tags[key] = { value, props }; } /** * Removes the specified tag from the array if it exists * * @param key The key of the tag to remove - * @param props The `RemoveProps` for the tag */ - public removeTag(key: string, props: RemoveProps = {blockPropagate: true}): void { - if (props.blockPropagate) { - this.blockedTags.push(key); - } - delete this._tags[key]; + public removeTag(key: string): void { + // tslint:disable-next-line:no-console + console.log(`removing ${key}`); + delete this.tags[key]; } /** - * Handles returning the tags in the desired format - * - * This function can be overridden to support another tag format. This was - * specifically designed to enable AutoScalingGroup Tags that have an - * additional CloudFormation key for `PropagateAtLaunch` - */ - protected tagFormatResolve(tagGroups: TagGroups): any { - const tags = {...tagGroups.nonStickyTags, ...tagGroups.ancestorTags, ...tagGroups.stickyTags}; - for (const key of this.blockedTags) { delete tags[key]; } - if (Object.keys(tags).length === 0) { - return undefined; + * Renders tags into the proper format based on TagType + */ + public renderTags(): any { + const keys = Object.keys(this.tags); + switch (this.tagType) { + case TagType.Standard: { + const tags: Array<{key: string, value: string}> = []; + for (const key of keys) { + tags.push({key, value: this.tags[key].value}); + } + return tags.length === 0 ? undefined : tags; + } + case TagType.AutoScalingGroup: { + const tags: Array<{key: string, value: string, propagateAtLaunch: boolean}> = []; + for (const key of keys) { + tags.push({ + key, + value: this.tags[key].value, + propagateAtLaunch: this.tags[key].props.applyToLaunchInstances} + ); + } + return tags.length === 0 ? undefined : tags; + } + case TagType.Map: { + const tags: {[key: string]: string} = {}; + for (const key of keys) { + tags[key] = this.tags[key].value; + } + return Object.keys(tags).length === 0 ? undefined : tags; + } + case TagType.NotTaggable: { + return undefined; + } } - return Object.keys(tags).map( key => ({key, value: tags[key]})); } } diff --git a/packages/@aws-cdk/cdk/lib/index.ts b/packages/@aws-cdk/cdk/lib/index.ts index 72e4e00592b2d..c542da89697e6 100644 --- a/packages/@aws-cdk/cdk/lib/index.ts +++ b/packages/@aws-cdk/cdk/lib/index.ts @@ -1,3 +1,6 @@ +export * from './aspects/aspect'; +export * from './aspects/tag-aspect'; + export * from './core/construct'; export * from './core/tokens'; export * from './core/tag-manager'; diff --git a/packages/@aws-cdk/cdk/lib/runtime.ts b/packages/@aws-cdk/cdk/lib/runtime.ts index e8fb8ba893064..00d55a2cdcfbe 100644 --- a/packages/@aws-cdk/cdk/lib/runtime.ts +++ b/packages/@aws-cdk/cdk/lib/runtime.ts @@ -48,7 +48,7 @@ function pad(x: number) { /** * Turn a tag object into the proper CloudFormation representation */ -export function tagToCloudFormation(x: any): any { +export function cfnTagToCloudFormation(x: any): any { return { Key: x.key, Value: x.value @@ -249,7 +249,7 @@ export function validateObject(x: any): ValidationResult { return VALIDATION_SUCCESS; } -export function validateTag(x: any): ValidationResult { +export function validateCfnTag(x: any): ValidationResult { if (!canInspect(x)) {Β return VALIDATION_SUCCESS; } if (x.key == null || x.value == null) { diff --git a/packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts b/packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts new file mode 100644 index 0000000000000..e4b2909c6faf4 --- /dev/null +++ b/packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts @@ -0,0 +1,180 @@ +import { Test } from 'nodeunit'; +import { RemoveTag, Resource, Stack, Tag, TagManager, TagType } from '../../lib'; + +class TaggableResource extends Resource { + public readonly tags = new TagManager(TagType.Standard); +} + +class AsgTaggableResource extends Resource { + public readonly tags = new TagManager(TagType.AutoScalingGroup); +} + +class MapTaggableResource extends Resource { + public readonly tags = new TagManager(TagType.Map); +} + +class TestRoot extends Stack { + constructor() { + super(undefined, 'TestStack', { env: { account: '123456789012', region: 'us-east-1' } }); + } + public testInvokeAspects() { + this.invokeAspects(); + } +} + +export = { + 'Tag visit all children of the applied node'(test: Test) { + const root = new TestRoot(); + const res = new TaggableResource(root, 'FakeResource', { + type: 'AWS::Fake::Thing', + }); + const res2 = new TaggableResource(res, 'FakeResource', { + type: 'AWS::Fake::Thing', + }); + const asg = new AsgTaggableResource(res, 'AsgFakeResource', { + type: 'AWS::Fake::Thing', + }); + + const map = new MapTaggableResource(res, 'MapFakeResource', { + type: 'AWS::Fake::Thing', + }); + res.apply(new Tag('foo', 'bar')); + test.deepEqual(res.node.aspects.length, 1); + root.testInvokeAspects(); + test.deepEqual(res.tags.renderTags(), [{key: 'foo', value: 'bar'}]); + test.deepEqual(res2.tags.renderTags(), [{key: 'foo', value: 'bar'}]); + test.deepEqual(map.tags.renderTags(), {foo: 'bar'}); + test.deepEqual(asg.tags.renderTags(), [{key: 'foo', value: 'bar', propagateAtLaunch: true}]); + test.done(); + }, + 'The last aspect applied takes precedence'(test: Test) { + const root = new TestRoot(); + const res = new TaggableResource(root, 'FakeResource', { + type: 'AWS::Fake::Thing', + }); + const res2 = new TaggableResource(res, 'FakeResource', { + type: 'AWS::Fake::Thing', + }); + res.apply(new Tag('foo', 'bar')); + res.apply(new Tag('foo', 'foobar')); + res.apply(new Tag('foo', 'baz')); + res2.apply(new Tag('foo', 'good')); + root.testInvokeAspects(); + test.deepEqual(res.tags.renderTags(), [{key: 'foo', value: 'baz'}]); + test.deepEqual(res2.tags.renderTags(), [{key: 'foo', value: 'good'}]); + test.done(); + }, + 'RemoveTag will remove a tag if it exists'(test: Test) { + const root = new TestRoot(); + const res = new TaggableResource(root, 'FakeResource', { + type: 'AWS::Fake::Thing', + }); + const res2 = new TaggableResource(res, 'FakeResource', { + type: 'AWS::Fake::Thing', + }); + const asg = new AsgTaggableResource(res, 'AsgFakeResource', { + type: 'AWS::Fake::Thing', + }); + + const map = new MapTaggableResource(res, 'MapFakeResource', { + type: 'AWS::Fake::Thing', + }); + root.apply(new Tag('root', 'was here')); + res.apply(new Tag('first', 'there is only 1')); + res.apply(new RemoveTag('root')); + res.apply(new RemoveTag('doesnotexist')); + root.testInvokeAspects(); + + test.deepEqual(res.tags.renderTags(), [{key: 'first', value: 'there is only 1'}]); + test.deepEqual(map.tags.renderTags(), {first: 'there is only 1'}); + test.deepEqual(asg.tags.renderTags(), [{key: 'first', value: 'there is only 1', propagateAtLaunch: true}]); + test.deepEqual(res2.tags.renderTags(), [{key: 'first', value: 'there is only 1'}]); + test.done(); + }, + 'the #visit function is idempotent'(test: Test) { + const root = new TestRoot(); + const res = new TaggableResource(root, 'FakeResource', { + type: 'AWS::Fake::Thing', + }); + + res.apply(new Tag('foo', 'bar')); + root.testInvokeAspects(); + test.deepEqual(res.tags.renderTags(), [{key: 'foo', value: 'bar'}]); + root.testInvokeAspects(); + test.deepEqual(res.tags.renderTags(), [{key: 'foo', value: 'bar'}]); + root.testInvokeAspects(); + test.deepEqual(res.tags.renderTags(), [{key: 'foo', value: 'bar'}]); + test.done(); + }, + 'include restricts tag application to resources types in the list'(test: Test) { + const root = new TestRoot(); + const res = new TaggableResource(root, 'FakeResource', { + type: 'AWS::Fake::Thing', + }); + const res2 = new TaggableResource(res, 'FakeResource', { + type: 'AWS::Fake::Thing', + }); + const asg = new AsgTaggableResource(res, 'AsgFakeResource', { + type: 'AWS::Fake::Asg', + }); + + const map = new MapTaggableResource(res, 'MapFakeResource', { + type: 'AWS::Fake::Map', + }); + res.apply(new Tag('foo', 'bar', {include: ['AWS::Fake::Asg']})); + root.testInvokeAspects(); + test.deepEqual(res.tags.renderTags(), undefined); + test.deepEqual(map.tags.renderTags(), undefined); + test.deepEqual(res2.tags.renderTags(), undefined); + test.deepEqual(asg.tags.renderTags(), [{key: 'foo', value: 'bar', propagateAtLaunch: true}]); + test.deepEqual(map.properties.tags, undefined); + test.done(); + }, + 'exclude prevents tag application to resource types in the list'(test: Test) { + const root = new TestRoot(); + const res = new TaggableResource(root, 'FakeResource', { + type: 'AWS::Fake::Thing', + }); + const res2 = new TaggableResource(res, 'FakeResource', { + type: 'AWS::Fake::Thing', + }); + const asg = new AsgTaggableResource(res, 'AsgFakeResource', { + type: 'AWS::Fake::Asg', + }); + + const map = new MapTaggableResource(res, 'MapFakeResource', { + type: 'AWS::Fake::Map', + }); + res.apply(new Tag('foo', 'bar', {exclude: ['AWS::Fake::Asg']})); + root.testInvokeAspects(); + test.deepEqual(res.tags.renderTags(), [{key: 'foo', value: 'bar'}]); + test.deepEqual(res2.tags.renderTags(), [{key: 'foo', value: 'bar'}]); + test.deepEqual(asg.tags.renderTags(), undefined); + test.deepEqual(map.tags.renderTags(), {foo: 'bar'}); + test.done(); + }, + 'Aspects are mutually exclusive with tags created by L1 Constructor'(test: Test) { + const root = new TestRoot(); + const aspectBranch = new TaggableResource(root, 'FakeBranchA', { + type: 'AWS::Fake::Thing', + properties: { + tags: [ + {key: 'cfn', value: 'is cool'}, + ], + }, + }); + const cfnBranch = new TaggableResource(root, 'FakeBranchB', { + type: 'AWS::Fake::Thing', + properties: { + tags: [ + {key: 'cfn', value: 'is cool'}, + ], + }, + }); + aspectBranch.apply(new Tag('aspects', 'rule')); + root.testInvokeAspects(); + test.deepEqual(aspectBranch.tags.renderTags(), [{key: 'aspects', value: 'rule'}]); + test.deepEqual(cfnBranch.properties.tags, [{key: 'cfn', value: 'is cool'}]); + 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 3b0ee595aa35a..6897e7996edfc 100644 --- a/packages/@aws-cdk/cdk/test/cloudformation/test.resource.ts +++ b/packages/@aws-cdk/cdk/test/cloudformation/test.resource.ts @@ -346,12 +346,12 @@ export = { MyC3C2R2F213BD26: { Type: 'T2' }, MyC3C2R38CE6F9F7: { Type: 'T3' }, MyResource: - { Type: 'R', - DependsOn: + { Type: 'R', + DependsOn: [ 'MyC1R1FB2A562F', - 'MyC1R2AE2B5066', - 'MyC2R3809EEAD6', - 'MyC3C2R38CE6F9F7' ] } } }); + 'MyC1R2AE2B5066', + 'MyC2R3809EEAD6', + 'MyC3C2R38CE6F9F7' ] } } }); test.done(); }, @@ -377,9 +377,9 @@ export = { // THEN test.deepEqual(stack.toCloudFormation(), { Resources: { MyResource: - { Type: 'YouCanEvenOverrideTheType', - Use: { Dot: { Notation: 'To create subtrees' } }, - Metadata: { Key: 12 } } } }); + { Type: 'YouCanEvenOverrideTheType', + Use: { Dot: { Notation: 'To create subtrees' } }, + Metadata: { Key: 12 } } } }); test.done(); }, @@ -406,8 +406,8 @@ export = { // THEN test.deepEqual(stack.toCloudFormation(), { Resources: { MyResource: - { Type: 'AWS::Resource::Type', - Properties: { Hello: { World: { Value1: 'Hello', Value2: null } } } } } }); + { Type: 'AWS::Resource::Type', + Properties: { Hello: { World: { Value1: 'Hello', Value2: null } } } } } }); test.done(); }, @@ -434,8 +434,8 @@ export = { // THEN test.deepEqual(stack.toCloudFormation(), { Resources: { MyResource: - { Type: 'AWS::Resource::Type', - Properties: { Hello: { World: { Value1: 'Hello' } } } } } }); + { Type: 'AWS::Resource::Type', + Properties: { Hello: { World: { Value1: 'Hello' } } } } } }); test.done(); }, @@ -453,8 +453,8 @@ export = { // THEN test.deepEqual(stack.toCloudFormation(), { Resources: { MyResource: - { Type: 'AWS::Resource::Type', - Properties: { Tree: { Exists: 42 } } } } }); + { Type: 'AWS::Resource::Type', + Properties: { Tree: { Exists: 42 } } } } }); test.done(); }, @@ -483,8 +483,8 @@ export = { // THEN test.deepEqual(stack.toCloudFormation(), { Resources: { MyResource: - { Type: 'AWS::Resource::Type', - Properties: { Hello: { World: { Value1: 'Hello' } } } } } }); + { Type: 'AWS::Resource::Type', + Properties: { Hello: { World: { Value1: 'Hello' } } } } } }); test.done(); }, @@ -509,12 +509,12 @@ export = { // THEN test.deepEqual(stack.toCloudFormation(), { Resources: { MyResource: - { Type: 'AWS::Resource::Type', - Properties: + { Type: 'AWS::Resource::Type', + Properties: { Hello: { World: { Foo: { Bar: 42 } } }, - Override1: { - Override2: { Heyy: [ 1] } - } } } } }); + Override1: { + Override2: { Heyy: [ 1] } + } } } } }); test.done(); }, @@ -532,8 +532,8 @@ export = { // THEN test.deepEqual(stack.toCloudFormation(), { Resources: { MyResource: - { Type: 'AWS::Resource::Type', - Properties: { Hello: { World: { Hey: 'Jude' } } } } } }); + { Type: 'AWS::Resource::Type', + Properties: { Hello: { World: { Hey: 'Jude' } } } } } }); test.done(); }, @@ -550,8 +550,8 @@ export = { test.deepEqual(stack.toCloudFormation(), { Resources: { MyResource: - { Type: 'MyResourceType', - Properties: { PROP1: 'foo', PROP2: 'bar' } } } }); + { Type: 'MyResourceType', + Properties: { PROP1: 'foo', PROP2: 'bar' } } } }); test.done(); }, @@ -564,8 +564,8 @@ export = { test.deepEqual(stack.toCloudFormation(), { Resources: { MyResource: - { Type: 'MyResourceType', - Properties: { PROP3: 'zoo' } } } }); + { Type: 'MyResourceType', + Properties: { PROP3: 'zoo' } } } }); test.done(); }, @@ -579,10 +579,10 @@ export = { test.deepEqual(stack.toCloudFormation(), { Resources: { MyResource: - { Type: 'MyResourceType', - Properties: { PROP2: 'hey', PROP3: 'zoo' } } } }); + { Type: 'MyResourceType', + Properties: { PROP2: 'hey', PROP3: 'zoo' } } } }); test.done(); - } + }, } }, diff --git a/packages/@aws-cdk/cdk/test/core/test.tag-manager.ts b/packages/@aws-cdk/cdk/test/core/test.tag-manager.ts index 71af1326f6b7b..8997c8a6e2641 100644 --- a/packages/@aws-cdk/cdk/test/core/test.tag-manager.ts +++ b/packages/@aws-cdk/cdk/test/core/test.tag-manager.ts @@ -1,180 +1,38 @@ import { Test } from 'nodeunit'; -import { Construct, ITaggable, Root, TagManager } from '../../lib'; - -class ChildTagger extends Construct implements ITaggable { - public readonly tags: TagManager; - constructor(scope: Construct, id: string) { - super(scope, id); - this.tags = new TagManager(scope); - } -} - -class Child extends Construct { - constructor(scope: Construct, id: string) { - super(scope, id); - } -} +import { TagType } from '../../lib/cloudformation/resource'; +import { TagManager } from '../../lib/core/tag-manager'; + +// class TaggableResource extends Resource { +// public readonly tagType = TagType.Standard; +// } +// +// class AsgTaggableResource extends Resource { +// public readonly tagType = TagType.AutoScalingGroup; +// } +// +// class MapTaggableResource extends Resource { +// public readonly tagType = TagType.Map; +// } export = { - 'TagManger handles tags for a Contruct Tree': { - 'setTag by default propagates to children'(test: Test) { - const root = new Root(); - const ctagger = new ChildTagger(root, 'one'); - const ctagger1 = new ChildTagger(ctagger, 'two'); - const ctagger2 = new ChildTagger(root, 'three'); - - // not taggable at all - new Child(ctagger, 'notag'); - - const tag = {key: 'Name', value: 'TheCakeIsALie'}; - ctagger.tags.setTag(tag.key, tag.value); - - const tagArray = [tag]; - for (const construct of [ctagger, ctagger1]) { - test.deepEqual(root.node.resolve(construct.tags), tagArray); - } - - test.deepEqual(root.node.resolve(ctagger2.tags), undefined); - test.done(); - }, - 'setTag with propagate false tags do not propagate'(test: Test) { - const root = new Root(); - const ctagger = new ChildTagger(root, 'one'); - const ctagger1 = new ChildTagger(ctagger, 'two'); - const ctagger2 = new ChildTagger(root, 'three'); - - // not taggable at all - new Child(ctagger, 'notag'); - - const tag = {key: 'Name', value: 'TheCakeIsALie'}; - ctagger.tags.setTag(tag.key, tag.value, {propagate: false}); - - for (const construct of [ctagger1, ctagger2]) { - test.deepEqual(root.node.resolve(construct.tags), undefined); - } - test.deepEqual(root.node.resolve(ctagger.tags)[0].key, 'Name'); - test.deepEqual(root.node.resolve(ctagger.tags)[0].value, 'TheCakeIsALie'); - test.done(); - }, - 'setTag with overwrite false does not overwrite a tag'(test: Test) { - const root = new Root(); - const ctagger = new ChildTagger(root, 'one'); - ctagger.tags.setTag('Env', 'Dev'); - ctagger.tags.setTag('Env', 'Prod', {overwrite: false}); - const result = root.node.resolve(ctagger.tags); - test.deepEqual(result, [{key: 'Env', value: 'Dev'}]); - test.done(); - }, - 'setTag with sticky false enables propagations to overwrite child tags'(test: Test) { - const root = new Root(); - const ctagger = new ChildTagger(root, 'one'); - const ctagger1 = new ChildTagger(ctagger, 'two'); - ctagger.tags.setTag('Parent', 'Is always right'); - ctagger1.tags.setTag('Parent', 'Is wrong', {sticky: false}); - const parent = root.node.resolve(ctagger.tags); - const child = root.node.resolve(ctagger1.tags); - test.deepEqual(parent, child); - test.done(); - - }, - 'tags propagate from all parents'(test: Test) { - const root = new Root(); - const ctagger = new ChildTagger(root, 'one'); - new ChildTagger(ctagger, 'two'); - const cNoTag = new Child(ctagger, 'three'); - const ctagger2 = new ChildTagger(cNoTag, 'four'); - const tag = {key: 'Name', value: 'TheCakeIsALie'}; - ctagger.tags.setTag(tag.key, tag.value, {propagate: true}); - test.deepEqual(root.node.resolve(ctagger2.tags), [tag]); - test.done(); - }, - 'a tag can be removed and added back'(test: Test) { - const root = new Root(); - const ctagger = new ChildTagger(root, 'one'); - const tag = {key: 'Name', value: 'TheCakeIsALie'}; - ctagger.tags.setTag(tag.key, tag.value, {propagate: true}); - test.deepEqual(root.node.resolve(ctagger.tags), [tag]); - ctagger.tags.removeTag(tag.key); - test.deepEqual(root.node.resolve(ctagger.tags), undefined); - ctagger.tags.setTag(tag.key, tag.value, {propagate: true}); - test.deepEqual(root.node.resolve(ctagger.tags), [tag]); - test.done(); - }, - 'removeTag removes a tag by key'(test: Test) { - const root = new Root(); - const ctagger = new ChildTagger(root, 'one'); - const ctagger1 = new ChildTagger(ctagger, 'two'); - const ctagger2 = new ChildTagger(root, 'three'); - - // not taggable at all - new Child(ctagger, 'notag'); - - const tag = {key: 'Name', value: 'TheCakeIsALie'}; - ctagger.tags.setTag(tag.key, tag.value); - ctagger.tags.removeTag('Name'); - - for (const construct of [ctagger, ctagger1, ctagger2]) { - test.deepEqual(root.node.resolve(construct.tags), undefined); - } - test.done(); - }, - 'removeTag with blockPropagate removes any propagated tags'(test: Test) { - const root = new Root(); - const ctagger = new ChildTagger(root, 'one'); - const ctagger1 = new ChildTagger(ctagger, 'two'); - ctagger.tags.setTag('Env', 'Dev'); - ctagger1.tags.removeTag('Env', {blockPropagate: true}); - const result = root.node.resolve(ctagger.tags); - test.deepEqual(result, [{key: 'Env', value: 'Dev'}]); - test.deepEqual(root.node.resolve(ctagger1.tags), undefined); - test.done(); - }, - 'children can override parent propagated tags'(test: Test) { - const root = new Root(); - const ctagger = new ChildTagger(root, 'one'); - const ctagChild = new ChildTagger(ctagger, 'one'); - const tag = {key: 'BestBeach', value: 'StoneSteps'}; - const tag2 = {key: 'BestBeach', value: 'k-38'}; - ctagger.tags.setTag(tag2.key, tag2.value); - ctagger.tags.setTag(tag.key, tag.value); - ctagChild.tags.setTag(tag2.key, tag2.value); - const parentTags = root.node.resolve(ctagger.tags); - const childTags = root.node.resolve(ctagChild.tags); - test.deepEqual(parentTags, [tag]); - test.deepEqual(childTags, [tag2]); + '#setTag() supports setting a tag regardless of Type'(test: Test) { + const notTaggable = new TagManager(TagType.NotTaggable); + notTaggable.setTag('key', 'value'); + test.deepEqual(notTaggable.renderTags(), undefined); + test.done(); + }, + 'when there are no tags': { + '#renderTags() returns undefined'(test: Test) { + const mgr = new TagManager(TagType.Standard); + test.deepEqual(mgr.renderTags(), undefined ); test.done(); }, - 'resolve() returns all tags'(test: Test) { - const root = new Root(); - const ctagger = new ChildTagger(root, 'one'); - const ctagChild = new ChildTagger(ctagger, 'one'); - const tagsNoProp = [ - {key: 'NorthCountySpot', value: 'Tabletops'}, - {key: 'Crowded', value: 'Trestles'}, - ]; - const tagsProp = [ - {key: 'BestBeach', value: 'StoneSteps'}, - {key: 'BestWaves', value: 'Blacks'}, - ]; - for (const tag of tagsNoProp) { - ctagger.tags.setTag(tag.key, tag.value, {propagate: false}); - } - for (const tag of tagsProp) { - ctagger.tags.setTag(tag.key, tag.value); - } - const allTags = tagsNoProp.concat(tagsProp); - const cAll = ctagger.tags; - const cProp = ctagChild.tags; - - for (const tag of root.node.resolve(cAll)) { - const expectedTag = allTags.filter( (t) => (t.key === tag.key)); - test.deepEqual(expectedTag[0].value, tag.value); - } - for (const tag of root.node.resolve(cProp)) { - const expectedTag = tagsProp.filter( (t) => (t.key === tag.key)); - test.deepEqual(expectedTag[0].value, tag.value); - } + '#renderTags() returns undefined with set and remove'(test: Test) { + const mgr = new TagManager(TagType.Standard); + mgr.setTag('foo', 'bar'); + mgr.removeTag('foo'); + test.deepEqual(mgr.renderTags(), undefined ); test.done(); }, - }, + } }; diff --git a/packages/@aws-cdk/cfnspec/lib/schema/property.ts b/packages/@aws-cdk/cfnspec/lib/schema/property.ts index 16dbad3a016e5..bcb87287904db 100644 --- a/packages/@aws-cdk/cfnspec/lib/schema/property.ts +++ b/packages/@aws-cdk/cfnspec/lib/schema/property.ts @@ -5,6 +5,7 @@ export type ScalarProperty = PrimitiveProperty | ComplexProperty | UnionProperty export type CollectionProperty = ListProperty |Β MapProperty | UnionProperty; export type ListProperty = PrimitiveListProperty | ComplexListProperty; export type MapProperty = PrimitiveMapProperty | ComplexMapProperty; +export type TagProperty = TagPropertyStandard | TagPropertyAutoScalingGroup | TagPropertyJson | TagPropertyStringMap; export interface PropertyBase extends Documented { /** Indicates whether the property is required. */ @@ -79,6 +80,22 @@ export interface ComplexMapProperty extends MapPropertyBase { ItemType: string; } +export interface TagPropertyStandard extends PropertyBase { + ItemType: 'Tag'; +} + +export interface TagPropertyAutoScalingGroup extends PropertyBase { + ItemType: 'TagProperty'; +} + +export interface TagPropertyJson extends PropertyBase { + PrimitiveType: PrimitiveType.Json; +} + +export interface TagPropertyStringMap extends PropertyBase { + PrimitiveItemType: 'String'; +} + /** * A property type that can be one of several types. Currently used only in SAM. */ @@ -196,4 +213,35 @@ export enum PropertyScrutinyType { export function isPropertyScrutinyType(str: string): str is PropertyScrutinyType { return (PropertyScrutinyType as any)[str] !== undefined; -} \ No newline at end of file +} + +/** + * This function validates that the property **can** be a Tag Property + * + * The property is only a Tag if the name of this property is Tags, which is + * validated using `ResourceType.isTaggable(resource)`. + */ +export function isTagProperty(prop: Property): prop is TagProperty { + return ( + isTagPropertyStandard(prop) || + isTagPropertyAutoScalingGroup(prop) || + isTagPropertyJson(prop) || + isTagPropertyStringMap(prop) + ); +} + +export function isTagPropertyStandard(prop: Property): prop is TagPropertyStandard { + return (prop as TagPropertyStandard).ItemType === 'Tag'; +} + +export function isTagPropertyAutoScalingGroup(prop: Property): prop is TagPropertyAutoScalingGroup { + return (prop as TagPropertyAutoScalingGroup).ItemType === 'TagProperty'; +} + +export function isTagPropertyJson(prop: Property): prop is TagPropertyJson { + return (prop as TagPropertyJson).PrimitiveType === PrimitiveType.Json; +} + +export function isTagPropertyStringMap(prop: Property): prop is TagPropertyStringMap { + return (prop as TagPropertyStringMap).PrimitiveItemType === 'String'; +} diff --git a/packages/@aws-cdk/cfnspec/lib/schema/resource-type.ts b/packages/@aws-cdk/cfnspec/lib/schema/resource-type.ts index 1b00979d72cd6..7e561c4590f46 100644 --- a/packages/@aws-cdk/cfnspec/lib/schema/resource-type.ts +++ b/packages/@aws-cdk/cfnspec/lib/schema/resource-type.ts @@ -1,5 +1,5 @@ import { Documented, PrimitiveType } from './base-types'; -import { Property } from './property'; +import { isTagProperty, Property, TagProperty } from './property'; export interface ResourceType extends Documented { /** @@ -28,6 +28,13 @@ export interface ResourceType extends Documented { ScrutinyType?: ResourceScrutinyType; } +export interface TaggableResource extends ResourceType { + Properties: { + Tags: TagProperty; + [name: string]: Property; + } +} + export type Attribute = PrimitiveAttribute | ListAttribute; export interface PrimitiveAttribute { @@ -46,6 +53,20 @@ export interface ComplexListAttribute { ItemType: string; } +/** + * Determine if the resource supports tags + * + * This function combined with isTagProperty determines if the `cdk.TagManager` + * and `cdk.TaggableResource` can process these tags. If not, standard code + * generation of properties will be used. + */ +export function isTaggableResource(spec: ResourceType): spec is TaggableResource { + if (spec.Properties && spec.Properties.Tags) { + return isTagProperty(spec.Properties.Tags); + } + return false; +} + export function isPrimitiveAttribute(spec: Attribute): spec is PrimitiveAttribute { return !!(spec as PrimitiveAttribute).PrimitiveType; } @@ -117,4 +138,4 @@ export enum ResourceScrutinyType { export function isResourceScrutinyType(str: string): str is ResourceScrutinyType { return (ResourceScrutinyType as any)[str] !== undefined; -} \ No newline at end of file +} diff --git a/tools/cfn2ts/lib/codegen.ts b/tools/cfn2ts/lib/codegen.ts index 17be51bc8ddce..dfdccc10674d2 100644 --- a/tools/cfn2ts/lib/codegen.ts +++ b/tools/cfn2ts/lib/codegen.ts @@ -8,6 +8,8 @@ import { itemTypeNames, PropertyAttributeName, scalarTypeNames, SpecName } from const CORE = genspec.CORE_NAMESPACE; const RESOURCE_BASE_CLASS = `${CORE}.Resource`; // base class for all resources const CONSTRUCT_CLASS = `${CORE}.Construct`; +const TAG_TYPE = `${CORE}.TagType`; +const TAG_MANAGER = `${CORE}.TagManager`; interface Dictionary { [key: string]: T; } @@ -231,6 +233,20 @@ export default class CodeGenerator { attributes.push(refAttribute); } } + // set the TagType to help format tags later + const tagEnum = tagType(spec); + if (tagEnum !== `${TAG_TYPE}.NotTaggable`) { + this.code.line(); + this.code.line('/**'); + this.code.line(' * The ``TagManager`` handles setting, removing and formatting tags'); + this.code.line(' *'); + this.code.line(' * Tags should be managed either passing them as properties during'); + this.code.line(' * initiation or by calling methods on this object. If both techniques are'); + this.code.line(' * used only the tags from the TagManager will be used. ``TagAspects``'); + this.code.line(' * will use the manager.'); + this.code.line(' */'); + this.code.line(`public readonly tags = new ${TAG_MANAGER}(${tagEnum});`); + } // // Constructor @@ -633,4 +649,20 @@ function tokenizableType(alternatives: string[]) { // TODO: number return false; -} \ No newline at end of file +} + +function tagType(resource: schema.ResourceType): string { + if (schema.isTaggableResource(resource)) { + const prop = resource.Properties.Tags; + if (schema.isTagPropertyStandard(prop)) { + return `${TAG_TYPE}.Standard`; + } + if (schema.isTagPropertyAutoScalingGroup(prop)) { + return `${TAG_TYPE}.AutoScalingGroup`; + } + if (schema.isTagPropertyJson(prop) || schema.isTagPropertyStringMap(prop)) { + return `${TAG_TYPE}.Map`; + } + } + return `${TAG_TYPE}.NotTaggable`; +} diff --git a/tools/cfn2ts/lib/genspec.ts b/tools/cfn2ts/lib/genspec.ts index 1d65543141817..add31b6df554d 100644 --- a/tools/cfn2ts/lib/genspec.ts +++ b/tools/cfn2ts/lib/genspec.ts @@ -87,7 +87,7 @@ export class CodeName { } } -export const TAG_NAME = new CodeName('', CORE_NAMESPACE, 'Tag'); +export const TAG_NAME = new CodeName('', CORE_NAMESPACE, 'CfnTag'); export const TOKEN_NAME = new CodeName('', CORE_NAMESPACE, 'Token'); /** From 419a701d501ff39a2997216fc53e1305c33e37e8 Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Sun, 13 Jan 2019 21:39:47 -0800 Subject: [PATCH 02/38] fixing public access to resource properties --- packages/@aws-cdk/cdk/lib/cloudformation/resource.ts | 2 +- packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/resource.ts b/packages/@aws-cdk/cdk/lib/cloudformation/resource.ts index 390b1dcd5175e..06dafb9b944fd 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/resource.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/resource.ts @@ -67,7 +67,7 @@ export class Resource extends Referenceable { * * This object is rendered via a call to "renderProperties(this.properties)". */ - public readonly properties: any; + protected readonly properties: any; /** * AWS resource property overrides. diff --git a/packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts b/packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts index e4b2909c6faf4..f8dda2abaff67 100644 --- a/packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts +++ b/packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts @@ -3,13 +3,16 @@ import { RemoveTag, Resource, Stack, Tag, TagManager, TagType } from '../../lib' class TaggableResource extends Resource { public readonly tags = new TagManager(TagType.Standard); + public testProperties() { + return this.properties; + } } -class AsgTaggableResource extends Resource { +class AsgTaggableResource extends TaggableResource { public readonly tags = new TagManager(TagType.AutoScalingGroup); } -class MapTaggableResource extends Resource { +class MapTaggableResource extends TaggableResource { public readonly tags = new TagManager(TagType.Map); } @@ -127,7 +130,7 @@ export = { test.deepEqual(map.tags.renderTags(), undefined); test.deepEqual(res2.tags.renderTags(), undefined); test.deepEqual(asg.tags.renderTags(), [{key: 'foo', value: 'bar', propagateAtLaunch: true}]); - test.deepEqual(map.properties.tags, undefined); + test.deepEqual(map.testProperties().tags, undefined); test.done(); }, 'exclude prevents tag application to resource types in the list'(test: Test) { @@ -174,7 +177,7 @@ export = { aspectBranch.apply(new Tag('aspects', 'rule')); root.testInvokeAspects(); test.deepEqual(aspectBranch.tags.renderTags(), [{key: 'aspects', value: 'rule'}]); - test.deepEqual(cfnBranch.properties.tags, [{key: 'cfn', value: 'is cool'}]); + test.deepEqual(cfnBranch.testProperties().tags, [{key: 'cfn', value: 'is cool'}]); test.done(); }, }; From 0a0d0109224451e0e030c8b3fa68d9b1bd88af24 Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Sun, 13 Jan 2019 22:33:57 -0800 Subject: [PATCH 03/38] fixing dynamo tag implementation --- packages/@aws-cdk/aws-dynamodb/lib/table.ts | 9 +-------- .../aws-dynamodb/test/integ.dynamodb.ondemand.ts | 5 +++-- .../@aws-cdk/aws-dynamodb/test/integ.dynamodb.ts | 4 ++-- packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts | 12 +++++++++--- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/@aws-cdk/aws-dynamodb/lib/table.ts b/packages/@aws-cdk/aws-dynamodb/lib/table.ts index cd8ebef7be464..2baacde4a6247 100644 --- a/packages/@aws-cdk/aws-dynamodb/lib/table.ts +++ b/packages/@aws-cdk/aws-dynamodb/lib/table.ts @@ -1,7 +1,7 @@ import appscaling = require('@aws-cdk/aws-applicationautoscaling'); import iam = require('@aws-cdk/aws-iam'); import cdk = require('@aws-cdk/cdk'); -import { Construct, TagManager, Tags, Token } from '@aws-cdk/cdk'; +import { Construct, Token } from '@aws-cdk/cdk'; import { CfnTable } from './dynamodb.generated'; import { EnableScalingProps, IScalableTableAttribute } from './scalable-attribute-api'; import { ScalableTableAttribute } from './scalable-table-attribute'; @@ -88,12 +88,6 @@ export interface TableProps { */ streamSpecification?: StreamViewType; - /** - * The AWS resource tags to associate with the table. - * @default undefined - */ - tags?: Tags; - /** * The name of TTL attribute. * @default undefined, TTL is disabled @@ -216,7 +210,6 @@ export class Table extends Construct { }, sseSpecification: props.sseEnabled ? { sseEnabled: props.sseEnabled } : undefined, streamSpecification: props.streamSpecification ? { streamViewType: props.streamSpecification } : undefined, - tags: new TagManager(this, { initialTags: props.tags }), timeToLiveSpecification: props.ttlAttributeName ? { attributeName: props.ttlAttributeName, enabled: true } : undefined }); diff --git a/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.ondemand.ts b/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.ondemand.ts index e805abc7dd19e..f1c04e8ccdb52 100644 --- a/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.ondemand.ts +++ b/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.ondemand.ts @@ -1,4 +1,4 @@ -import { App, Stack } from '@aws-cdk/cdk'; +import { App, Stack, Tag } from '@aws-cdk/cdk'; import { Attribute, AttributeType, BillingMode, ProjectionType, StreamViewType, Table } from '../lib'; // CDK parameters @@ -48,11 +48,12 @@ const tableWithGlobalAndLocalSecondaryIndex = new Table(stack, TABLE_WITH_GLOBAL pitrEnabled: true, sseEnabled: true, streamSpecification: StreamViewType.KeysOnly, - tags: { Environment: 'Production' }, billingMode: BillingMode.PayPerRequest, ttlAttributeName: 'timeToLive' }); +tableWithGlobalAndLocalSecondaryIndex.apply(new Tag('Environment', 'Production')); + tableWithGlobalAndLocalSecondaryIndex.addPartitionKey(TABLE_PARTITION_KEY); tableWithGlobalAndLocalSecondaryIndex.addSortKey(TABLE_SORT_KEY); tableWithGlobalAndLocalSecondaryIndex.addGlobalSecondaryIndex({ diff --git a/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.ts b/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.ts index e8124d879bea9..fd4daff81ed22 100644 --- a/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.ts +++ b/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.ts @@ -1,4 +1,4 @@ -import { App, Stack } from '@aws-cdk/cdk'; +import { App, Stack, Tag } from '@aws-cdk/cdk'; import { Attribute, AttributeType, ProjectionType, StreamViewType, Table } from '../lib'; // CDK parameters @@ -47,10 +47,10 @@ const tableWithGlobalAndLocalSecondaryIndex = new Table(stack, TABLE_WITH_GLOBAL pitrEnabled: true, sseEnabled: true, streamSpecification: StreamViewType.KeysOnly, - tags: { Environment: 'Production' }, ttlAttributeName: 'timeToLive' }); +tableWithGlobalAndLocalSecondaryIndex.apply(new Tag('Environment', 'Production')); tableWithGlobalAndLocalSecondaryIndex.addPartitionKey(TABLE_PARTITION_KEY); tableWithGlobalAndLocalSecondaryIndex.addSortKey(TABLE_SORT_KEY); tableWithGlobalAndLocalSecondaryIndex.addGlobalSecondaryIndex({ diff --git a/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts b/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts index c19eaeaf6521b..76cc894056faa 100644 --- a/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts +++ b/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts @@ -1,6 +1,6 @@ import { expect, haveResource } from '@aws-cdk/assert'; import iam = require('@aws-cdk/aws-iam'); -import { App, Stack } from '@aws-cdk/cdk'; +import { App, Stack, Tag } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import { Attribute, @@ -391,11 +391,11 @@ export = { sseEnabled: true, billingMode: BillingMode.Provisioned, streamSpecification: StreamViewType.KeysOnly, - tags: { Environment: 'Production' }, ttlAttributeName: 'timeToLive' }); table.addPartitionKey(TABLE_PARTITION_KEY); table.addSortKey(TABLE_SORT_KEY); + table.apply(new Tag('Environment', 'Production')); const template = app.synthesizeTemplate(); test.deepEqual(template, { @@ -1357,12 +1357,18 @@ export = { }, }; +class TestStack extends Stack { + public testInvokeAspects() { + this.invokeAspects(); + } +} class TestApp { private readonly app = new App(); // tslint:disable-next-line:member-ordering - public readonly stack: Stack = new Stack(this.app, STACK_NAME); + public readonly stack: TestStack = new TestStack(this.app, STACK_NAME); public synthesizeTemplate() { + this.stack.testInvokeAspects(); return this.app.synthesizeStack(this.stack.name).template; } } From 01099564b0f1006ec35d6e624c6dd217bdbb24af Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Tue, 15 Jan 2019 21:16:06 -0800 Subject: [PATCH 04/38] refactoring to add in a TestStack for easy unit testing when aspects are involved --- .../test/test.pipeline-deploy-stack-action.ts | 4 +-- packages/@aws-cdk/assert/lib/expect.ts | 9 ++++++ packages/@aws-cdk/assert/lib/index.ts | 1 + packages/@aws-cdk/assert/lib/test-stack.ts | 7 +++++ .../aws-autoscaling/lib/auto-scaling-group.ts | 4 ++- .../test/test.scheduled-action.ts | 11 ++------ .../aws-cloudtrail/test/test.cloudtrail.ts | 4 +-- packages/@aws-cdk/aws-ec2/test/test.vpc.ts | 16 ++--------- .../test/ec2/integ.lb-awsvpc-nw.expected.json | 6 ++++ .../test/ec2/integ.lb-bridge-nw.expected.json | 6 ++++ .../@aws-cdk/aws-ecs/test/test.ecs-cluster.ts | 4 +-- packages/@aws-cdk/aws-kms/test/test.key.ts | 28 +++++++------------ 12 files changed, 53 insertions(+), 47 deletions(-) create mode 100644 packages/@aws-cdk/assert/lib/test-stack.ts diff --git a/packages/@aws-cdk/app-delivery/test/test.pipeline-deploy-stack-action.ts b/packages/@aws-cdk/app-delivery/test/test.pipeline-deploy-stack-action.ts index 25f41261ec849..14ba06ce442ee 100644 --- a/packages/@aws-cdk/app-delivery/test/test.pipeline-deploy-stack-action.ts +++ b/packages/@aws-cdk/app-delivery/test/test.pipeline-deploy-stack-action.ts @@ -9,7 +9,7 @@ import cxapi = require('@aws-cdk/cx-api'); import fc = require('fast-check'); import nodeunit = require('nodeunit'); -import { countResources, expect, haveResource, isSuperObject } from '@aws-cdk/assert'; +import { countResources, expect, haveResource, isSuperObject, TestStack } from '@aws-cdk/assert'; import { PipelineDeployStackAction } from '../lib/pipeline-deploy-stack-action'; interface SelfUpdatingPipeline { @@ -299,7 +299,7 @@ class FakeAction extends api.Action { } function getTestStack(): cdk.Stack { - return new cdk.Stack(undefined, 'TestStack', { env: { account: '123456789012', region: 'us-east-1' } }); + return new TestStack(undefined, 'TestStack', { env: { account: '123456789012', region: 'us-east-1' } }); } function createSelfUpdatingStack(pipelineStack: cdk.Stack): SelfUpdatingPipeline { diff --git a/packages/@aws-cdk/assert/lib/expect.ts b/packages/@aws-cdk/assert/lib/expect.ts index a983da0d96cd3..caf6e60123ee4 100644 --- a/packages/@aws-cdk/assert/lib/expect.ts +++ b/packages/@aws-cdk/assert/lib/expect.ts @@ -1,6 +1,7 @@ import cdk = require('@aws-cdk/cdk'); import api = require('@aws-cdk/cx-api'); import { StackInspector } from './inspector'; +import { TestStack } from './test-stack'; export function expect(stack: api.SynthesizedStack | cdk.Stack, skipValidation = false): StackInspector { // Can't use 'instanceof' here, that breaks if we have multiple copies @@ -18,6 +19,10 @@ export function expect(stack: api.SynthesizedStack | cdk.Stack, skipValidation = } } + if (isTestStack(stack)) { + stack.testInvokeAspects(); + } + sstack = { name: stack.name, template: stack.toCloudFormation(), @@ -50,3 +55,7 @@ function collectStackMetadata(root: cdk.ConstructNode): api.StackMetadata { } return result; } + +function isTestStack(t: any): t is TestStack { + return t.testInvokeAspects !== undefined; +} diff --git a/packages/@aws-cdk/assert/lib/index.ts b/packages/@aws-cdk/assert/lib/index.ts index e811cd2e0bb0d..426ce5c9bcf19 100644 --- a/packages/@aws-cdk/assert/lib/index.ts +++ b/packages/@aws-cdk/assert/lib/index.ts @@ -1,6 +1,7 @@ export * from './assertion'; export * from './expect'; export * from './inspector'; +export * from './test-stack'; export * from './assertions/exist'; export * from './assertions/have-resource'; diff --git a/packages/@aws-cdk/assert/lib/test-stack.ts b/packages/@aws-cdk/assert/lib/test-stack.ts new file mode 100644 index 0000000000000..3c58062176ab9 --- /dev/null +++ b/packages/@aws-cdk/assert/lib/test-stack.ts @@ -0,0 +1,7 @@ +import cdk = require('@aws-cdk/cdk'); + +export class TestStack extends cdk.Stack { + public testInvokeAspects() { + this.invokeAspects(); + } +} diff --git a/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts b/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts index 0b091505e01f4..3c040d65e633a 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts @@ -199,7 +199,9 @@ export class AutoScalingGroup extends cdk.Construct implements IAutoScalingGroup }); this.connections = new ec2.Connections({ securityGroups: [this.securityGroup] }); this.securityGroups.push(this.securityGroup); - this.apply(new cdk.Tag(NAME_TAG, this.node.path, { applyToLaunchInstances: true })); + this.apply(new cdk.Tag(NAME_TAG, this.node.path, { + applyToLaunchInstances: true, + })); this.role = new iam.Role(this, 'InstanceRole', { assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com') diff --git a/packages/@aws-cdk/aws-autoscaling/test/test.scheduled-action.ts b/packages/@aws-cdk/aws-autoscaling/test/test.scheduled-action.ts index 770c57cc29c6a..afe70277416ac 100644 --- a/packages/@aws-cdk/aws-autoscaling/test/test.scheduled-action.ts +++ b/packages/@aws-cdk/aws-autoscaling/test/test.scheduled-action.ts @@ -1,4 +1,4 @@ -import { expect, haveResource, MatchStyle } from '@aws-cdk/assert'; +import { expect, haveResource, MatchStyle, TestStack } from '@aws-cdk/assert'; import ec2 = require('@aws-cdk/aws-ec2'); import cdk = require('@aws-cdk/cdk'); import { Test } from 'nodeunit'; @@ -7,7 +7,7 @@ import autoscaling = require('../lib'); export = { 'can schedule an action'(test: Test) { // GIVEN - const stack = new cdk.Stack(); + const stack = new TestStack(); const asg = makeAutoScalingGroup(stack); // WHEN @@ -27,7 +27,7 @@ export = { 'correctly formats date objects'(test: Test) { // GIVEN - const stack = new cdk.Stack(); + const stack = new TestStack(); const asg = makeAutoScalingGroup(stack); // WHEN @@ -114,11 +114,6 @@ function makeAutoScalingGroup(scope: cdk.Construct) { }); } -class TestStack extends cdk.Stack { - public testInvokeAspects(): void { - this.invokeAspects(); - } -} function getTestStack(): TestStack { return new TestStack(undefined, 'TestStack', { env: { account: '1234', region: 'us-east-1' } }); } diff --git a/packages/@aws-cdk/aws-cloudtrail/test/test.cloudtrail.ts b/packages/@aws-cdk/aws-cloudtrail/test/test.cloudtrail.ts index ae6066ec43ff4..7a6212234e2aa 100644 --- a/packages/@aws-cdk/aws-cloudtrail/test/test.cloudtrail.ts +++ b/packages/@aws-cdk/aws-cloudtrail/test/test.cloudtrail.ts @@ -1,4 +1,4 @@ -import { expect, haveResource, not } from '@aws-cdk/assert'; +import { expect, haveResource, not, TestStack } from '@aws-cdk/assert'; import { Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import { CloudTrail, LogRetention, ReadWriteType } from '../lib'; @@ -147,5 +147,5 @@ export = { }; function getTestStack(): Stack { - return new Stack(undefined, 'TestStack', { env: { account: '123456789012', region: 'us-east-1' } }); + return new TestStack(undefined, 'TestStack', { env: { account: '123456789012', region: 'us-east-1' } }); } diff --git a/packages/@aws-cdk/aws-ec2/test/test.vpc.ts b/packages/@aws-cdk/aws-ec2/test/test.vpc.ts index 4c9767d332a87..5af8408d46fce 100644 --- a/packages/@aws-cdk/aws-ec2/test/test.vpc.ts +++ b/packages/@aws-cdk/aws-ec2/test/test.vpc.ts @@ -1,5 +1,5 @@ -import { countResources, expect, haveResource, haveResourceLike, isSuperObject } from '@aws-cdk/assert'; -import { AvailabilityZoneProvider, Construct, Stack, Tag } from '@aws-cdk/cdk'; +import { countResources, expect, haveResource, haveResourceLike, isSuperObject, TestStack } from '@aws-cdk/assert'; +import { AvailabilityZoneProvider, Construct, Tag } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import { CfnVPC, DefaultInstanceTenancy, IVpcNetwork, SubnetType, VpcNetwork } from '../lib'; @@ -28,7 +28,6 @@ export = { 'the Name tag is defaulted to path'(test: Test) { const stack = getTestStack(); new VpcNetwork(stack, 'TheVPC'); - stack.testInvokeAspects(); expect(stack).to( haveResource('AWS::EC2::VPC', hasTags( [ {Key: 'Name', Value: 'TheVPC'} ])) @@ -294,7 +293,6 @@ export = { subnetName: 'egress' }, }); - stack.testInvokeAspects(); expect(stack).to(countResources("AWS::EC2::NatGateway", 3)); for (let i = 1; i < 4; i++) { expect(stack).to(haveResource('AWS::EC2::Subnet', hasTags([{ @@ -353,7 +351,6 @@ export = { // overwrite to set propagate vpc.apply(new Tag('BusinessUnit', 'Marketing', {include: [CfnVPC.resourceTypeName]})); vpc.apply(new Tag('VpcType', 'Good')); - stack.testInvokeAspects(); expect(stack).to(haveResource("AWS::EC2::VPC", hasTags(toCfnTags(allTags)))); const taggables = ['Subnet', 'InternetGateway', 'NatGateway', 'RouteTable']; const propTags = toCfnTags(tags); @@ -367,7 +364,6 @@ export = { 'Subnet Name will propagate to route tables and NATGW'(test: Test) { const stack = getTestStack(); const vpc = new VpcNetwork(stack, 'TheVPC'); - stack.testInvokeAspects(); for (const subnet of vpc.publicSubnets) { const tag = {Key: 'Name', Value: subnet.node.path}; expect(stack).to(haveResource('AWS::EC2::NatGateway', hasTags([tag]))); @@ -384,10 +380,8 @@ export = { const vpc = new VpcNetwork(stack, 'TheVPC'); const tag = {Key: 'Late', Value: 'Adder'}; - stack.testInvokeAspects(); expect(stack).notTo(haveResource('AWS::EC2::VPC', hasTags([tag]))); vpc.apply(new Tag(tag.Key, tag.Value)); - stack.testInvokeAspects(); expect(stack).to(haveResource('AWS::EC2::VPC', hasTags([tag]))); test.done(); }, @@ -536,12 +530,6 @@ export = { }, }; -class TestStack extends Stack { - public testInvokeAspects(): void { - this.invokeAspects(); - } -} - function getTestStack(): TestStack { return new TestStack(undefined, 'TestStack', { env: { account: '123456789012', region: 'us-east-1' } }); } 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 fc5ddba7d30ee..cff577e35c37d 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 @@ -608,6 +608,12 @@ } } }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ/EcsCluster/DefaultAutoScalingGroup" + } + ], "Timeout": 310 }, "DependsOn": [ 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 4e80a7b3cdb0f..31e990b4fb5d9 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 @@ -629,6 +629,12 @@ } } }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-ecs/EcsCluster/DefaultAutoScalingGroup" + } + ], "Timeout": 310 }, "DependsOn": [ diff --git a/packages/@aws-cdk/aws-ecs/test/test.ecs-cluster.ts b/packages/@aws-cdk/aws-ecs/test/test.ecs-cluster.ts index 8748b7cadbc87..ca3181e8665fa 100644 --- a/packages/@aws-cdk/aws-ecs/test/test.ecs-cluster.ts +++ b/packages/@aws-cdk/aws-ecs/test/test.ecs-cluster.ts @@ -1,4 +1,4 @@ -import { expect, haveResource } from '@aws-cdk/assert'; +import { expect, haveResource, TestStack } from '@aws-cdk/assert'; import ec2 = require('@aws-cdk/aws-ec2'); import { InstanceType } from '@aws-cdk/aws-ec2'; import cdk = require('@aws-cdk/cdk'); @@ -9,7 +9,7 @@ export = { "When creating an ECS Cluster": { "with only required properties set, it correctly sets default properties"(test: Test) { // GIVEN - const stack = new cdk.Stack(); + const stack = new TestStack(); const vpc = new ec2.VpcNetwork(stack, 'MyVpc', {}); const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc, diff --git a/packages/@aws-cdk/aws-kms/test/test.key.ts b/packages/@aws-cdk/aws-kms/test/test.key.ts index 7ba07a37e171e..98859138bf705 100644 --- a/packages/@aws-cdk/aws-kms/test/test.key.ts +++ b/packages/@aws-cdk/aws-kms/test/test.key.ts @@ -1,13 +1,13 @@ -import { exactlyMatchTemplate, expect } from '@aws-cdk/assert'; +import { exactlyMatchTemplate, expect, TestStack } from '@aws-cdk/assert'; import { PolicyDocument, PolicyStatement } from '@aws-cdk/aws-iam'; -import { App, Stack, Tag } from '@aws-cdk/cdk'; +import { App, Tag } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import { EncryptionKey } from '../lib'; export = { 'default key'(test: Test) { const app = new App(); - const stack = new Stack(app, 'TestStack'); + const stack = new TestStack(app, 'TestStack'); new EncryptionKey(stack, 'MyKey'); @@ -67,7 +67,7 @@ export = { 'default with some permission'(test: Test) { const app = new App(); - const stack = new Stack(app, 'Test'); + const stack = new TestStack(app, 'Test'); const key = new EncryptionKey(stack, 'MyKey'); const p = new PolicyStatement().addAllResources().addAction('kms:encrypt'); @@ -138,9 +138,7 @@ export = { }, 'key with some options'(test: Test) { - // const app = getTestApp(); const app = new App(); - // const stack = new Stack(app, 'Test'); const stack = new TestStack(app, 'Test'); const key = new EncryptionKey(stack, 'MyKey', { @@ -154,7 +152,7 @@ export = { key.apply(new Tag('tag1', 'value1')); key.apply(new Tag('tag2', 'value2')); key.apply(new Tag('tag3', '')); - // app.testInvokeAspects(); + stack.testInvokeAspects(); expect(app.synthesizeStack(stack.name)).to(exactlyMatchTemplate({ Resources: { @@ -237,7 +235,7 @@ export = { 'addAlias creates an alias'(test: Test) { const app = new App(); - const stack = new Stack(app, 'Test'); + const stack = new TestStack(app, 'Test'); const key = new EncryptionKey(stack, 'MyKey', { enableKeyRotation: true, @@ -317,7 +315,7 @@ export = { }, 'import/export can be used to bring in an existing key'(test: Test) { - const stack1 = new Stack(); + const stack1 = new TestStack(); const policy = new PolicyDocument(); policy.addStatement(new PolicyStatement().addAllResources()); const myKey = new EncryptionKey(stack1, 'MyKey', { policy }); @@ -356,7 +354,7 @@ export = { } }); - const stack2 = new Stack(); + const stack2 = new TestStack(); const myKeyImported = EncryptionKey.import(stack2, 'MyKeyImported', exportedKeyRef); // addAlias can be called on imported keys. @@ -381,7 +379,7 @@ export = { 'addToResourcePolicy allowNoOp and there is no policy': { 'succeed if set to true (default)'(test: Test) { - const stack = new Stack(); + const stack = new TestStack(); const key = EncryptionKey.import(stack, 'Imported', { keyArn: 'foo/bar' }); @@ -392,7 +390,7 @@ export = { 'fails if set to false'(test: Test) { - const stack = new Stack(); + const stack = new TestStack(); const key = EncryptionKey.import(stack, 'Imported', { keyArn: 'foo/bar' }); @@ -405,9 +403,3 @@ export = { } } }; - -class TestStack extends Stack { - public testInvokeAspects(): void { - this.invokeAspects(); - } -} From 2188764d5dc542053b6d46836956d5b98e188d65 Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Tue, 15 Jan 2019 21:40:14 -0800 Subject: [PATCH 05/38] adding documentation to classes --- packages/@aws-cdk/cdk/lib/aspects/aspect.ts | 18 ++++++++- .../@aws-cdk/cdk/lib/aspects/tag-aspect.ts | 38 ++++++++++++++++++- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/cdk/lib/aspects/aspect.ts b/packages/@aws-cdk/cdk/lib/aspects/aspect.ts index b54e87303da9b..4c138ae646648 100644 --- a/packages/@aws-cdk/cdk/lib/aspects/aspect.ts +++ b/packages/@aws-cdk/cdk/lib/aspects/aspect.ts @@ -16,12 +16,22 @@ export interface IAspect { } /** - * TODO: description + * The representation of an Apect */ export abstract class Aspect implements IAspect { + /** + * The type of the aspect + */ public abstract readonly type: string; + private readonly visitedBy: {[id: string]: boolean} = {}; + /** + * The visit function is invoked during synthesis for each aspect + * + * The visit function will visit each child node in the construct tree. Each + * Node will only be visited once. + */ public visit(construct: IConstruct): void { if (this.visitedBy[construct.node.uniqueId] === true) { return; @@ -34,5 +44,11 @@ export abstract class Aspect implements IAspect { } } + /** + * This is the function concrete Aspects should implement + * + * The ``visit()`` function will call this method to invoke the customized + * apsect actions. + */ protected abstract visitAction(construct: IConstruct): void; } diff --git a/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts b/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts index fdbc572251266..783d591548c1e 100644 --- a/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts +++ b/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts @@ -3,26 +3,54 @@ import { IConstruct } from '../core/construct'; import { Aspect } from './aspect'; export interface TagBaseProps extends TagProperties { - // TODO docs + /** + * The value of the tag + */ value?: string; } export interface TagProperties { - // TODO docs + /** + * This applies specifically to AutoScalingGroup PropagateAtLaunch + */ applyToLaunchInstances?: boolean; + + /** + * An array of Resource Types that will receive this tag + */ include?: string[]; + + /** + * An array of Resource Types that will not receive this tag + */ exclude?: string[]; } +/** + * The common functionality for Tag and Remove Tag Aspects + */ export abstract class TagBase extends Aspect { + /** + * Test if the construct is a CloudFormation Resource + */ public static isResource(resource: any): resource is Resource { return resource.resourceType !== undefined; } + /** + * The ``taggable`` type for these aspects + */ public readonly type: string = 'taggable'; + /** + * The string key for the tag + */ public readonly key: string; + + /** + * The string value of the tag + */ public readonly value?: string; private readonly include: string[]; @@ -56,6 +84,9 @@ export abstract class TagBase extends Aspect { protected abstract applyTag(resource: ITaggable): void; } +/** + * The Tag Aspect will handle adding a tag to this node and cascading tags to children + */ export class Tag extends TagBase { private readonly applyToLaunchInstances: boolean; @@ -73,6 +104,9 @@ export class Tag extends TagBase { } } +/** + * The RemoveTag Aspect will handle removing tags from this node and children + */ export class RemoveTag extends TagBase { constructor(key: string, props: TagProperties = {}) { From ce95e6454e563c3e7e117fceaabbb29d878f2e7f Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Tue, 15 Jan 2019 21:45:12 -0800 Subject: [PATCH 06/38] cleaning up some extraneous comments --- packages/@aws-cdk/cdk/lib/cloudformation/stack.ts | 3 +-- packages/@aws-cdk/cdk/lib/core/construct.ts | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/stack.ts b/packages/@aws-cdk/cdk/lib/cloudformation/stack.ts index b93d32b7e52a9..0c0e76bc64347 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/stack.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/stack.ts @@ -230,7 +230,6 @@ export class Stack extends Construct { * Rename a generated logical identities */ public renameLogical(oldId: string, newId: string) { - // tslint:disable-next-line:no-console if (this.node.children.length > 0) { throw new Error("All renames must be set up before adding elements to the stack"); } @@ -511,4 +510,4 @@ function stackElements(node: IConstruct, into: StackElement[] = []): StackElemen // These imports have to be at the end to prevent circular imports import { ArnComponents, arnFromComponents, parseArn } from './arn'; import { Aws } from './pseudo'; -import { StackElement } from './stack-element'; \ No newline at end of file +import { StackElement } from './stack-element'; diff --git a/packages/@aws-cdk/cdk/lib/core/construct.ts b/packages/@aws-cdk/cdk/lib/core/construct.ts index 844d7740c2a7d..fe09c27a0557e 100644 --- a/packages/@aws-cdk/cdk/lib/core/construct.ts +++ b/packages/@aws-cdk/cdk/lib/core/construct.ts @@ -123,7 +123,6 @@ export class ConstructNode { * @returns a child by path or undefined if not found. */ public tryFindChild(path: string): IConstruct | undefined { - // tslint:disable-next-line:no-console if (path.startsWith(PATH_SEP)) { throw new Error('Path must be relative'); } From f3a7e37a2eb06e6f7b6e4a56b06c5e1fb6a71cd5 Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Tue, 15 Jan 2019 22:47:09 -0800 Subject: [PATCH 07/38] adding tag-manager unit tests --- packages/@aws-cdk/cdk/lib/core/tag-manager.ts | 2 - .../cdk/test/core/test.tag-manager.ts | 74 ++++++++++++++----- 2 files changed, 55 insertions(+), 21 deletions(-) diff --git a/packages/@aws-cdk/cdk/lib/core/tag-manager.ts b/packages/@aws-cdk/cdk/lib/core/tag-manager.ts index a0d01b668d341..f4983e5e5faf9 100644 --- a/packages/@aws-cdk/cdk/lib/core/tag-manager.ts +++ b/packages/@aws-cdk/cdk/lib/core/tag-manager.ts @@ -41,8 +41,6 @@ export class TagManager { * @param key The key of the tag to remove */ public removeTag(key: string): void { - // tslint:disable-next-line:no-console - console.log(`removing ${key}`); delete this.tags[key]; } diff --git a/packages/@aws-cdk/cdk/test/core/test.tag-manager.ts b/packages/@aws-cdk/cdk/test/core/test.tag-manager.ts index 8997c8a6e2641..4c8a913b831d2 100644 --- a/packages/@aws-cdk/cdk/test/core/test.tag-manager.ts +++ b/packages/@aws-cdk/cdk/test/core/test.tag-manager.ts @@ -2,18 +2,6 @@ import { Test } from 'nodeunit'; import { TagType } from '../../lib/cloudformation/resource'; import { TagManager } from '../../lib/core/tag-manager'; -// class TaggableResource extends Resource { -// public readonly tagType = TagType.Standard; -// } -// -// class AsgTaggableResource extends Resource { -// public readonly tagType = TagType.AutoScalingGroup; -// } -// -// class MapTaggableResource extends Resource { -// public readonly tagType = TagType.Map; -// } - export = { '#setTag() supports setting a tag regardless of Type'(test: Test) { const notTaggable = new TagManager(TagType.NotTaggable); @@ -21,18 +9,66 @@ export = { test.deepEqual(notTaggable.renderTags(), undefined); test.done(); }, - 'when there are no tags': { - '#renderTags() returns undefined'(test: Test) { + 'when a tag does not exist': { + '#removeTag() does not throw an error'(test: Test) { const mgr = new TagManager(TagType.Standard); - test.deepEqual(mgr.renderTags(), undefined ); + test.doesNotThrow(() => (mgr.removeTag('dne'))); + test.done(); + }, + '#setTag() creates the tag'(test: Test) { + const mgr = new TagManager(TagType.Standard); + mgr.setTag('dne', 'notanymore'); + test.deepEqual(mgr.renderTags(), [{key: 'dne', value: 'notanymore'}]); + test.done(); + } + }, + 'when a tag does exist': { + '#removeTag() deletes the tag'(test: Test) { + const mgr = new TagManager(TagType.Standard); + mgr.setTag('dne', 'notanymore'); + mgr.removeTag('dne'); + test.deepEqual(mgr.renderTags(), undefined); test.done(); }, - '#renderTags() returns undefined with set and remove'(test: Test) { + '#setTag() overwrites the tag'(test: Test) { + const mgr = new TagManager(TagType.Standard); + mgr.setTag('dne', 'notanymore'); + mgr.setTag('dne', 'iwin'); + test.deepEqual(mgr.renderTags(), [{key: 'dne', value: 'iwin'}]); + test.done(); + } + }, + 'when there are no tags': { + '#renderTags() returns undefined'(test: Test) { const mgr = new TagManager(TagType.Standard); - mgr.setTag('foo', 'bar'); - mgr.removeTag('foo'); test.deepEqual(mgr.renderTags(), undefined ); test.done(); }, - } + }, + '#renderTags() handles standard, map, and ASG tag formats'(test: Test) { + const tagged: TagManager[] = []; + const standard = new TagManager(TagType.Standard); + const asg = new TagManager(TagType.AutoScalingGroup); + const mapper = new TagManager(TagType.Map); + tagged.push(standard); + tagged.push(asg); + tagged.push(mapper); + for (const res of tagged) { + res.setTag('foo', 'bar'); + res.setTag('asg', 'only', {applyToLaunchInstances: false}); + } + test.deepEqual(standard.renderTags(), [ + {key: 'foo', value: 'bar'}, + {key: 'asg', value: 'only'}, + ]); + test.deepEqual(asg.renderTags(), [ + {key: 'foo', value: 'bar', propagateAtLaunch: true}, + {key: 'asg', value: 'only', propagateAtLaunch: false}, + ]); + test.deepEqual(mapper.renderTags(), { + foo: 'bar', + asg: 'only', + }); + test.done(); + }, }; From 6cc19dbbdef65702ba83c88ecf168b8bcab038bd Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Wed, 16 Jan 2019 01:04:40 -0800 Subject: [PATCH 08/38] convert dynamo test to use expect --- .../aws-dynamodb/test/test.dynamodb.ts | 1316 +++++++---------- 1 file changed, 569 insertions(+), 747 deletions(-) diff --git a/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts b/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts index 76cc894056faa..92eea5dffd75a 100644 --- a/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts +++ b/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts @@ -1,6 +1,6 @@ -import { expect, haveResource } from '@aws-cdk/assert'; +import { expect, countResources, haveResource, TestStack } from '@aws-cdk/assert'; import iam = require('@aws-cdk/aws-iam'); -import { App, Stack, Tag } from '@aws-cdk/cdk'; +import { Tag } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import { Attribute, @@ -16,7 +16,6 @@ import { // tslint:disable:object-literal-key-quotes // CDK parameters -const STACK_NAME = 'MyStack'; const CONSTRUCT_NAME = 'MyTable'; // DynamoDB table parameters @@ -67,323 +66,246 @@ function* LSI_GENERATOR() { export = { 'default properties': { 'fails without a hash key'(test: Test) { - const app = new TestApp(); - new Table(app.stack, CONSTRUCT_NAME); - test.throws(() => app.synthesizeTemplate(), /partition key/); - + const stack = new TestStack(); + new Table(stack, CONSTRUCT_NAME); + test.throws(() => expect(stack).to(countResources('AWS::DynamoDB::Table')), /partition key/); test.done(); }, 'hash key only'(test: Test) { - const app = new TestApp(); - new Table(app.stack, CONSTRUCT_NAME).addPartitionKey(TABLE_PARTITION_KEY); - const template = app.synthesizeTemplate(); - - test.deepEqual(template, { - Resources: { - MyTable794EDED1: { - Type: 'AWS::DynamoDB::Table', - Properties: { - AttributeDefinitions: [{ AttributeName: 'hashKey', AttributeType: 'S' }], - KeySchema: [{ AttributeName: 'hashKey', KeyType: 'HASH' }], - ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 } - } - } - } - }); - + const stack = new TestStack(); + new Table(stack, CONSTRUCT_NAME).addPartitionKey(TABLE_PARTITION_KEY); + + expect(stack).to(haveResource('AWS::DynamoDB::Table', { + AttributeDefinitions: [{ AttributeName: 'hashKey', AttributeType: 'S' }], + KeySchema: [{ AttributeName: 'hashKey', KeyType: 'HASH' }], + ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 } + })); test.done(); }, - 'hash + range key'(test: Test) { - const app = new TestApp(); - new Table(app.stack, CONSTRUCT_NAME) - .addPartitionKey(TABLE_PARTITION_KEY) - .addSortKey(TABLE_SORT_KEY); - const template = app.synthesizeTemplate(); - - test.deepEqual(template, { - Resources: { - MyTable794EDED1: { - Type: 'AWS::DynamoDB::Table', - Properties: { - AttributeDefinitions: [ - { AttributeName: 'hashKey', AttributeType: 'S' }, - { AttributeName: 'sortKey', AttributeType: 'N' } - ], - KeySchema: [ - { AttributeName: 'hashKey', KeyType: 'HASH' }, - { AttributeName: 'sortKey', KeyType: 'RANGE' } - ], - ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 }, - } - } - } - }); - - test.done(); - }, + 'hash + range key'(test: Test) { + const stack = new TestStack(); + new Table(stack, CONSTRUCT_NAME) + .addPartitionKey(TABLE_PARTITION_KEY) + .addSortKey(TABLE_SORT_KEY); + expect(stack).to(haveResource('AWS::DynamoDB::Table', { + AttributeDefinitions: [ + { AttributeName: 'hashKey', AttributeType: 'S' }, + { AttributeName: 'sortKey', AttributeType: 'N' } + ], + KeySchema: [ + { AttributeName: 'hashKey', KeyType: 'HASH' }, + { AttributeName: 'sortKey', KeyType: 'RANGE' } + ], + ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 }, + })); + test.done(); + }, - 'hash + range key can also be specified in props'(test: Test) { - const app = new TestApp(); + 'hash + range key can also be specified in props'(test: Test) { + const stack = new TestStack(); - new Table(app.stack, CONSTRUCT_NAME, { - partitionKey: TABLE_PARTITION_KEY, - sortKey: TABLE_SORT_KEY - }); + new Table(stack, CONSTRUCT_NAME, { + partitionKey: TABLE_PARTITION_KEY, + sortKey: TABLE_SORT_KEY + }); - const template = app.synthesizeTemplate(); - - test.deepEqual(template, { - Resources: { - MyTable794EDED1: { - Type: 'AWS::DynamoDB::Table', - Properties: { - AttributeDefinitions: [ - { AttributeName: 'hashKey', AttributeType: 'S' }, - { AttributeName: 'sortKey', AttributeType: 'N' } - ], - KeySchema: [ - { AttributeName: 'hashKey', KeyType: 'HASH' }, - { AttributeName: 'sortKey', KeyType: 'RANGE' } - ], - ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 }, - } - } - } - }); + expect(stack).to(haveResource('AWS::DynamoDB::Table', + { + AttributeDefinitions: [ + { AttributeName: 'hashKey', AttributeType: 'S' }, + { AttributeName: 'sortKey', AttributeType: 'N' } + ], + KeySchema: [ + { AttributeName: 'hashKey', KeyType: 'HASH' }, + { AttributeName: 'sortKey', KeyType: 'RANGE' } + ], + ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 }, + })); - test.done(); - }, + test.done(); + }, - 'point-in-time recovery is not enabled'(test: Test) { - const app = new TestApp(); - new Table(app.stack, CONSTRUCT_NAME) - .addPartitionKey(TABLE_PARTITION_KEY) - .addSortKey(TABLE_SORT_KEY); - const template = app.synthesizeTemplate(); - - test.deepEqual(template, { - Resources: { - MyTable794EDED1: { - Type: 'AWS::DynamoDB::Table', - Properties: { - AttributeDefinitions: [ - { AttributeName: 'hashKey', AttributeType: 'S' }, - { AttributeName: 'sortKey', AttributeType: 'N' } - ], - KeySchema: [ - { AttributeName: 'hashKey', KeyType: 'HASH' }, - { AttributeName: 'sortKey', KeyType: 'RANGE' } - ], - ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 } - } - } - } - }); + 'point-in-time recovery is not enabled'(test: Test) { + const stack = new TestStack(); + new Table(stack, CONSTRUCT_NAME) + .addPartitionKey(TABLE_PARTITION_KEY) + .addSortKey(TABLE_SORT_KEY); - test.done(); - }, + expect(stack).to(haveResource('AWS::DynamoDB::Table', + { + AttributeDefinitions: [ + { AttributeName: 'hashKey', AttributeType: 'S' }, + { AttributeName: 'sortKey', AttributeType: 'N' } + ], + KeySchema: [ + { AttributeName: 'hashKey', KeyType: 'HASH' }, + { AttributeName: 'sortKey', KeyType: 'RANGE' } + ], + ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 } + } + )); + test.done(); + }, - 'server-side encryption is not enabled'(test: Test) { - const app = new TestApp(); - new Table(app.stack, CONSTRUCT_NAME) - .addPartitionKey(TABLE_PARTITION_KEY) - .addSortKey(TABLE_SORT_KEY); - const template = app.synthesizeTemplate(); - - test.deepEqual(template, { - Resources: { - MyTable794EDED1: { - Type: 'AWS::DynamoDB::Table', - Properties: { - AttributeDefinitions: [ - { AttributeName: 'hashKey', AttributeType: 'S' }, - { AttributeName: 'sortKey', AttributeType: 'N' } - ], - KeySchema: [ - { AttributeName: 'hashKey', KeyType: 'HASH' }, - { AttributeName: 'sortKey', KeyType: 'RANGE' } - ], - ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 } - } - } - } - }); + 'server-side encryption is not enabled'(test: Test) { + const stack = new TestStack(); + new Table(stack, CONSTRUCT_NAME) + .addPartitionKey(TABLE_PARTITION_KEY) + .addSortKey(TABLE_SORT_KEY); - test.done(); - }, + expect(stack).to(haveResource('AWS::DynamoDB::Table', + { + AttributeDefinitions: [ + { AttributeName: 'hashKey', AttributeType: 'S' }, + { AttributeName: 'sortKey', AttributeType: 'N' } + ], + KeySchema: [ + { AttributeName: 'hashKey', KeyType: 'HASH' }, + { AttributeName: 'sortKey', KeyType: 'RANGE' } + ], + ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 } + } + )); + test.done(); + }, - 'stream is not enabled'(test: Test) { - const app = new TestApp(); - new Table(app.stack, CONSTRUCT_NAME) - .addPartitionKey(TABLE_PARTITION_KEY) - .addSortKey(TABLE_SORT_KEY); - const template = app.synthesizeTemplate(); - - test.deepEqual(template, { - Resources: { - MyTable794EDED1: { - Type: 'AWS::DynamoDB::Table', - Properties: { - AttributeDefinitions: [ - { AttributeName: 'hashKey', AttributeType: 'S' }, - { AttributeName: 'sortKey', AttributeType: 'N' } - ], - KeySchema: [ - { AttributeName: 'hashKey', KeyType: 'HASH' }, - { AttributeName: 'sortKey', KeyType: 'RANGE' } - ], - ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 } - } - } - } - }); + 'stream is not enabled'(test: Test) { + const stack = new TestStack(); + new Table(stack, CONSTRUCT_NAME) + .addPartitionKey(TABLE_PARTITION_KEY) + .addSortKey(TABLE_SORT_KEY); - test.done(); - }, + expect(stack).to(haveResource('AWS::DynamoDB::Table', + { + AttributeDefinitions: [ + { AttributeName: 'hashKey', AttributeType: 'S' }, + { AttributeName: 'sortKey', AttributeType: 'N' } + ], + KeySchema: [ + { AttributeName: 'hashKey', KeyType: 'HASH' }, + { AttributeName: 'sortKey', KeyType: 'RANGE' } + ], + ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 } + } + )); + test.done(); + }, - 'ttl is not enabled'(test: Test) { - const app = new TestApp(); - new Table(app.stack, CONSTRUCT_NAME) - .addPartitionKey(TABLE_PARTITION_KEY) - .addSortKey(TABLE_SORT_KEY); - const template = app.synthesizeTemplate(); - - test.deepEqual(template, { - Resources: { - MyTable794EDED1: { - Type: 'AWS::DynamoDB::Table', - Properties: { - AttributeDefinitions: [ - { AttributeName: 'hashKey', AttributeType: 'S' }, - { AttributeName: 'sortKey', AttributeType: 'N' } - ], - KeySchema: [ - { AttributeName: 'hashKey', KeyType: 'HASH' }, - { AttributeName: 'sortKey', KeyType: 'RANGE' } - ], - ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 } - } - } - } - }); + 'ttl is not enabled'(test: Test) { + const stack = new TestStack(); + new Table(stack, CONSTRUCT_NAME) + .addPartitionKey(TABLE_PARTITION_KEY) + .addSortKey(TABLE_SORT_KEY); - test.done(); - }, + expect(stack).to(haveResource('AWS::DynamoDB::Table', + { + AttributeDefinitions: [ + { AttributeName: 'hashKey', AttributeType: 'S' }, + { AttributeName: 'sortKey', AttributeType: 'N' } + ], + KeySchema: [ + { AttributeName: 'hashKey', KeyType: 'HASH' }, + { AttributeName: 'sortKey', KeyType: 'RANGE' } + ], + ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 } + } + )); + test.done(); + }, - 'can specify new and old images'(test: Test) { - const app = new TestApp(); - const table = new Table(app.stack, CONSTRUCT_NAME, { - tableName: TABLE_NAME, - readCapacity: 42, - writeCapacity: 1337, - streamSpecification: StreamViewType.NewAndOldImages - }); - table.addPartitionKey(TABLE_PARTITION_KEY); - table.addSortKey(TABLE_SORT_KEY); - const template = app.synthesizeTemplate(); - - test.deepEqual(template, { - Resources: { - MyTable794EDED1: { - Type: 'AWS::DynamoDB::Table', - Properties: { - AttributeDefinitions: [ - { AttributeName: 'hashKey', AttributeType: 'S' }, - { AttributeName: 'sortKey', AttributeType: 'N' } - ], - StreamSpecification: { StreamViewType: 'NEW_AND_OLD_IMAGES' }, - KeySchema: [ - { AttributeName: 'hashKey', KeyType: 'HASH' }, - { AttributeName: 'sortKey', KeyType: 'RANGE' } - ], - ProvisionedThroughput: { ReadCapacityUnits: 42, WriteCapacityUnits: 1337 }, - TableName: 'MyTable' - } - } - } - }); + 'can specify new and old images'(test: Test) { + const stack = new TestStack(); + const table = new Table(stack, CONSTRUCT_NAME, { + tableName: TABLE_NAME, + readCapacity: 42, + writeCapacity: 1337, + streamSpecification: StreamViewType.NewAndOldImages + }); + table.addPartitionKey(TABLE_PARTITION_KEY); + table.addSortKey(TABLE_SORT_KEY); - test.done(); - }, + expect(stack).to(haveResource('AWS::DynamoDB::Table', + { + AttributeDefinitions: [ + { AttributeName: 'hashKey', AttributeType: 'S' }, + { AttributeName: 'sortKey', AttributeType: 'N' } + ], + StreamSpecification: { StreamViewType: 'NEW_AND_OLD_IMAGES' }, + KeySchema: [ + { AttributeName: 'hashKey', KeyType: 'HASH' }, + { AttributeName: 'sortKey', KeyType: 'RANGE' } + ], + ProvisionedThroughput: { ReadCapacityUnits: 42, WriteCapacityUnits: 1337 }, + TableName: 'MyTable' + } + )); + test.done(); + }, - 'can specify new images only'(test: Test) { - const app = new TestApp(); - const table = new Table(app.stack, CONSTRUCT_NAME, { - tableName: TABLE_NAME, - readCapacity: 42, - writeCapacity: 1337, - streamSpecification: StreamViewType.NewImage - }); - table.addPartitionKey(TABLE_PARTITION_KEY); - table.addSortKey(TABLE_SORT_KEY); - const template = app.synthesizeTemplate(); - - test.deepEqual(template, { - Resources: { - MyTable794EDED1: { - Type: 'AWS::DynamoDB::Table', - Properties: { - KeySchema: [ - { AttributeName: 'hashKey', KeyType: 'HASH' }, - { AttributeName: 'sortKey', KeyType: 'RANGE' } - ], - ProvisionedThroughput: { ReadCapacityUnits: 42, WriteCapacityUnits: 1337 }, - AttributeDefinitions: [ - { AttributeName: 'hashKey', AttributeType: 'S' }, - { AttributeName: 'sortKey', AttributeType: 'N' } - ], - StreamSpecification: { StreamViewType: 'NEW_IMAGE' }, - TableName: 'MyTable', - } - } - } - }); + 'can specify new images only'(test: Test) { + const stack = new TestStack(); + const table = new Table(stack, CONSTRUCT_NAME, { + tableName: TABLE_NAME, + readCapacity: 42, + writeCapacity: 1337, + streamSpecification: StreamViewType.NewImage + }); + table.addPartitionKey(TABLE_PARTITION_KEY); + table.addSortKey(TABLE_SORT_KEY); - test.done(); - }, + expect(stack).to(haveResource('AWS::DynamoDB::Table', + { + KeySchema: [ + { AttributeName: 'hashKey', KeyType: 'HASH' }, + { AttributeName: 'sortKey', KeyType: 'RANGE' } + ], + ProvisionedThroughput: { ReadCapacityUnits: 42, WriteCapacityUnits: 1337 }, + AttributeDefinitions: [ + { AttributeName: 'hashKey', AttributeType: 'S' }, + { AttributeName: 'sortKey', AttributeType: 'N' } + ], + StreamSpecification: { StreamViewType: 'NEW_IMAGE' }, + TableName: 'MyTable', + } + )); + test.done(); + }, - 'can specify old images only'(test: Test) { - const app = new TestApp(); - const table = new Table(app.stack, CONSTRUCT_NAME, { - tableName: TABLE_NAME, - readCapacity: 42, - writeCapacity: 1337, - streamSpecification: StreamViewType.OldImage - }); - table.addPartitionKey(TABLE_PARTITION_KEY); - table.addSortKey(TABLE_SORT_KEY); - const template = app.synthesizeTemplate(); - - test.deepEqual(template, { - Resources: { - MyTable794EDED1: { - Type: 'AWS::DynamoDB::Table', - Properties: { - KeySchema: [ - { AttributeName: 'hashKey', KeyType: 'HASH' }, - { AttributeName: 'sortKey', KeyType: 'RANGE' } - ], - ProvisionedThroughput: { ReadCapacityUnits: 42, WriteCapacityUnits: 1337 }, - AttributeDefinitions: [ - { AttributeName: 'hashKey', AttributeType: 'S' }, - { AttributeName: 'sortKey', AttributeType: 'N' } - ], - StreamSpecification: { StreamViewType: 'OLD_IMAGE' }, - TableName: 'MyTable', - } - } - } - }); + 'can specify old images only'(test: Test) { + const stack = new TestStack(); + const table = new Table(stack, CONSTRUCT_NAME, { + tableName: TABLE_NAME, + readCapacity: 42, + writeCapacity: 1337, + streamSpecification: StreamViewType.OldImage + }); + table.addPartitionKey(TABLE_PARTITION_KEY); + table.addSortKey(TABLE_SORT_KEY); - test.done(); - } - }, + expect(stack).to(haveResource('AWS::DynamoDB::Table', + { + KeySchema: [ + { AttributeName: 'hashKey', KeyType: 'HASH' }, + { AttributeName: 'sortKey', KeyType: 'RANGE' } + ], + ProvisionedThroughput: { ReadCapacityUnits: 42, WriteCapacityUnits: 1337 }, + AttributeDefinitions: [ + { AttributeName: 'hashKey', AttributeType: 'S' }, + { AttributeName: 'sortKey', AttributeType: 'N' } + ], + StreamSpecification: { StreamViewType: 'OLD_IMAGE' }, + TableName: 'MyTable', + } + )); + test.done(); + } +}, 'when specifying every property'(test: Test) { - const app = new TestApp(); - const table = new Table(app.stack, CONSTRUCT_NAME, { + const stack = new TestStack(); + const table = new Table(stack, CONSTRUCT_NAME, { tableName: TABLE_NAME, readCapacity: 42, writeCapacity: 1337, @@ -396,84 +318,70 @@ export = { table.addPartitionKey(TABLE_PARTITION_KEY); table.addSortKey(TABLE_SORT_KEY); table.apply(new Tag('Environment', 'Production')); - const template = app.synthesizeTemplate(); - test.deepEqual(template, { - Resources: { - MyTable794EDED1: { - Type: 'AWS::DynamoDB::Table', - Properties: { - AttributeDefinitions: [ - { AttributeName: 'hashKey', AttributeType: 'S' }, - { AttributeName: 'sortKey', AttributeType: 'N' } - ], - KeySchema: [ - { AttributeName: 'hashKey', KeyType: 'HASH' }, - { AttributeName: 'sortKey', KeyType: 'RANGE' } - ], - ProvisionedThroughput: { - ReadCapacityUnits: 42, - WriteCapacityUnits: 1337 - }, - PointInTimeRecoverySpecification: { PointInTimeRecoveryEnabled: true }, - SSESpecification: { SSEEnabled: true }, - StreamSpecification: { StreamViewType: 'KEYS_ONLY' }, - TableName: 'MyTable', - Tags: [ { Key: 'Environment', Value: 'Production' } ], - TimeToLiveSpecification: { AttributeName: 'timeToLive', Enabled: true } - } - } + expect(stack).to(haveResource('AWS::DynamoDB::Table', + { + AttributeDefinitions: [ + { AttributeName: 'hashKey', AttributeType: 'S' }, + { AttributeName: 'sortKey', AttributeType: 'N' } + ], + KeySchema: [ + { AttributeName: 'hashKey', KeyType: 'HASH' }, + { AttributeName: 'sortKey', KeyType: 'RANGE' } + ], + ProvisionedThroughput: { + ReadCapacityUnits: 42, + WriteCapacityUnits: 1337 + }, + PointInTimeRecoverySpecification: { PointInTimeRecoveryEnabled: true }, + SSESpecification: { SSEEnabled: true }, + StreamSpecification: { StreamViewType: 'KEYS_ONLY' }, + TableName: 'MyTable', + Tags: [ { Key: 'Environment', Value: 'Production' } ], + TimeToLiveSpecification: { AttributeName: 'timeToLive', Enabled: true } } - }); - + )); test.done(); }, 'when specifying PAY_PER_REQUEST billing mode'(test: Test) { - const app = new TestApp(); - new Table(app.stack, CONSTRUCT_NAME, { + const stack = new TestStack(); + new Table(stack, CONSTRUCT_NAME, { tableName: TABLE_NAME, billingMode: BillingMode.PayPerRequest, partitionKey: TABLE_PARTITION_KEY }); - const template = app.synthesizeTemplate(); - test.deepEqual(template, { - Resources: { - MyTable794EDED1: { - Type: 'AWS::DynamoDB::Table', - Properties: { - KeySchema: [ - { AttributeName: 'hashKey', KeyType: 'HASH' }, - ], - BillingMode: 'PAY_PER_REQUEST', - AttributeDefinitions: [ - { AttributeName: 'hashKey', AttributeType: 'S' }, - ], - TableName: 'MyTable', - } - } + expect(stack).to(haveResource('AWS::DynamoDB::Table', + { + KeySchema: [ + { AttributeName: 'hashKey', KeyType: 'HASH' }, + ], + BillingMode: 'PAY_PER_REQUEST', + AttributeDefinitions: [ + { AttributeName: 'hashKey', AttributeType: 'S' }, + ], + TableName: 'MyTable', } - }); - + )); test.done(); }, 'error when specifying read or write capacity with a PAY_PER_REQUEST billing mode'(test: Test) { - const app = new TestApp(); - test.throws(() => new Table(app.stack, CONSTRUCT_NAME, { + const stack = new TestStack(); + test.throws(() => new Table(stack, CONSTRUCT_NAME, { tableName: TABLE_NAME, billingMode: BillingMode.PayPerRequest, partitionKey: TABLE_PARTITION_KEY, readCapacity: 1 })); - test.throws(() => new Table(app.stack, CONSTRUCT_NAME, { + test.throws(() => new Table(stack, CONSTRUCT_NAME, { tableName: TABLE_NAME, billingMode: BillingMode.PayPerRequest, partitionKey: TABLE_PARTITION_KEY, writeCapacity: 1 })); - test.throws(() => new Table(app.stack, CONSTRUCT_NAME, { + test.throws(() => new Table(stack, CONSTRUCT_NAME, { tableName: TABLE_NAME, billingMode: BillingMode.PayPerRequest, partitionKey: TABLE_PARTITION_KEY, @@ -484,8 +392,8 @@ export = { }, 'when adding a global secondary index with hash key only'(test: Test) { - const app = new TestApp(); - new Table(app.stack, CONSTRUCT_NAME) + const stack = new TestStack(); + new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY) .addGlobalSecondaryIndex({ @@ -494,44 +402,37 @@ export = { readCapacity: 42, writeCapacity: 1337 }); - const template = app.synthesizeTemplate(); - test.deepEqual(template, { - Resources: { - MyTable794EDED1: { - Type: 'AWS::DynamoDB::Table', - Properties: { - AttributeDefinitions: [ - { AttributeName: 'hashKey', AttributeType: 'S' }, - { AttributeName: 'sortKey', AttributeType: 'N' }, - { AttributeName: 'gsiHashKey', AttributeType: 'S' }, - ], + expect(stack).to(haveResource('AWS::DynamoDB::Table', + { + AttributeDefinitions: [ + { AttributeName: 'hashKey', AttributeType: 'S' }, + { AttributeName: 'sortKey', AttributeType: 'N' }, + { AttributeName: 'gsiHashKey', AttributeType: 'S' }, + ], + KeySchema: [ + { AttributeName: 'hashKey', KeyType: 'HASH' }, + { AttributeName: 'sortKey', KeyType: 'RANGE' } + ], + ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 }, + GlobalSecondaryIndexes: [ + { + IndexName: 'MyGSI', KeySchema: [ - { AttributeName: 'hashKey', KeyType: 'HASH' }, - { AttributeName: 'sortKey', KeyType: 'RANGE' } + { AttributeName: 'gsiHashKey', KeyType: 'HASH' }, ], - ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 }, - GlobalSecondaryIndexes: [ - { - IndexName: 'MyGSI', - KeySchema: [ - { AttributeName: 'gsiHashKey', KeyType: 'HASH' }, - ], - Projection: { ProjectionType: 'ALL' }, - ProvisionedThroughput: { ReadCapacityUnits: 42, WriteCapacityUnits: 1337 } - } - ] + Projection: { ProjectionType: 'ALL' }, + ProvisionedThroughput: { ReadCapacityUnits: 42, WriteCapacityUnits: 1337 } } - } + ] } - }); - + )); test.done(); }, 'when adding a global secondary index with hash + range key'(test: Test) { - const app = new TestApp(); - new Table(app.stack, CONSTRUCT_NAME) + const stack = new TestStack(); + new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY) .addGlobalSecondaryIndex({ @@ -542,46 +443,39 @@ export = { readCapacity: 42, writeCapacity: 1337 }); - const template = app.synthesizeTemplate(); - test.deepEqual(template, { - Resources: { - MyTable794EDED1: { - Type: 'AWS::DynamoDB::Table', - Properties: { - AttributeDefinitions: [ - { AttributeName: 'hashKey', AttributeType: 'S' }, - { AttributeName: 'sortKey', AttributeType: 'N' }, - { AttributeName: 'gsiHashKey', AttributeType: 'S' }, - { AttributeName: 'gsiSortKey', AttributeType: 'B' } - ], + expect(stack).to(haveResource('AWS::DynamoDB::Table', + { + AttributeDefinitions: [ + { AttributeName: 'hashKey', AttributeType: 'S' }, + { AttributeName: 'sortKey', AttributeType: 'N' }, + { AttributeName: 'gsiHashKey', AttributeType: 'S' }, + { AttributeName: 'gsiSortKey', AttributeType: 'B' } + ], + KeySchema: [ + { AttributeName: 'hashKey', KeyType: 'HASH' }, + { AttributeName: 'sortKey', KeyType: 'RANGE' } + ], + ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 }, + GlobalSecondaryIndexes: [ + { + IndexName: 'MyGSI', KeySchema: [ - { AttributeName: 'hashKey', KeyType: 'HASH' }, - { AttributeName: 'sortKey', KeyType: 'RANGE' } + { AttributeName: 'gsiHashKey', KeyType: 'HASH' }, + { AttributeName: 'gsiSortKey', KeyType: 'RANGE' } ], - ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 }, - GlobalSecondaryIndexes: [ - { - IndexName: 'MyGSI', - KeySchema: [ - { AttributeName: 'gsiHashKey', KeyType: 'HASH' }, - { AttributeName: 'gsiSortKey', KeyType: 'RANGE' } - ], - Projection: { ProjectionType: 'ALL' }, - ProvisionedThroughput: { ReadCapacityUnits: 42, WriteCapacityUnits: 1337 } - } - ] + Projection: { ProjectionType: 'ALL' }, + ProvisionedThroughput: { ReadCapacityUnits: 42, WriteCapacityUnits: 1337 } } - } + ] } - }); - + )); test.done(); }, 'when adding a global secondary index with projection type KEYS_ONLY'(test: Test) { - const app = new TestApp(); - new Table(app.stack, CONSTRUCT_NAME) + const stack = new TestStack(); + new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY) .addGlobalSecondaryIndex({ @@ -590,46 +484,39 @@ export = { sortKey: GSI_SORT_KEY, projectionType: ProjectionType.KeysOnly, }); - const template = app.synthesizeTemplate(); - test.deepEqual(template, { - Resources: { - MyTable794EDED1: { - Type: 'AWS::DynamoDB::Table', - Properties: { - AttributeDefinitions: [ - { AttributeName: 'hashKey', AttributeType: 'S' }, - { AttributeName: 'sortKey', AttributeType: 'N' }, - { AttributeName: 'gsiHashKey', AttributeType: 'S' }, - { AttributeName: 'gsiSortKey', AttributeType: 'B' } - ], + expect(stack).to(haveResource('AWS::DynamoDB::Table', + { + AttributeDefinitions: [ + { AttributeName: 'hashKey', AttributeType: 'S' }, + { AttributeName: 'sortKey', AttributeType: 'N' }, + { AttributeName: 'gsiHashKey', AttributeType: 'S' }, + { AttributeName: 'gsiSortKey', AttributeType: 'B' } + ], + KeySchema: [ + { AttributeName: 'hashKey', KeyType: 'HASH' }, + { AttributeName: 'sortKey', KeyType: 'RANGE' } + ], + ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 }, + GlobalSecondaryIndexes: [ + { + IndexName: 'MyGSI', KeySchema: [ - { AttributeName: 'hashKey', KeyType: 'HASH' }, - { AttributeName: 'sortKey', KeyType: 'RANGE' } + { AttributeName: 'gsiHashKey', KeyType: 'HASH' }, + { AttributeName: 'gsiSortKey', KeyType: 'RANGE' } ], - ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 }, - GlobalSecondaryIndexes: [ - { - IndexName: 'MyGSI', - KeySchema: [ - { AttributeName: 'gsiHashKey', KeyType: 'HASH' }, - { AttributeName: 'gsiSortKey', KeyType: 'RANGE' } - ], - Projection: { ProjectionType: 'KEYS_ONLY' }, - ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 } - } - ] + Projection: { ProjectionType: 'KEYS_ONLY' }, + ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 } } - } + ] } - }); - + )); test.done(); }, 'when adding a global secondary index with projection type INCLUDE'(test: Test) { - const app = new TestApp(); - const table = new Table(app.stack, CONSTRUCT_NAME) + const stack = new TestStack(); + const table = new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY); const gsiNonKeyAttributeGenerator = NON_KEY_ATTRIBUTE_GENERATOR(GSI_NON_KEY); @@ -642,46 +529,39 @@ export = { readCapacity: 42, writeCapacity: 1337 }); - const template = app.synthesizeTemplate(); - test.deepEqual(template, { - Resources: { - MyTable794EDED1: { - Type: 'AWS::DynamoDB::Table', - Properties: { - AttributeDefinitions: [ - { AttributeName: 'hashKey', AttributeType: 'S' }, - { AttributeName: 'sortKey', AttributeType: 'N' }, - { AttributeName: 'gsiHashKey', AttributeType: 'S' }, - { AttributeName: 'gsiSortKey', AttributeType: 'B' } - ], + expect(stack).to(haveResource('AWS::DynamoDB::Table', + { + AttributeDefinitions: [ + { AttributeName: 'hashKey', AttributeType: 'S' }, + { AttributeName: 'sortKey', AttributeType: 'N' }, + { AttributeName: 'gsiHashKey', AttributeType: 'S' }, + { AttributeName: 'gsiSortKey', AttributeType: 'B' } + ], + KeySchema: [ + { AttributeName: 'hashKey', KeyType: 'HASH' }, + { AttributeName: 'sortKey', KeyType: 'RANGE' } + ], + ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 }, + GlobalSecondaryIndexes: [ + { + IndexName: 'MyGSI', KeySchema: [ - { AttributeName: 'hashKey', KeyType: 'HASH' }, - { AttributeName: 'sortKey', KeyType: 'RANGE' } + { AttributeName: 'gsiHashKey', KeyType: 'HASH' }, + { AttributeName: 'gsiSortKey', KeyType: 'RANGE' } ], - ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 }, - GlobalSecondaryIndexes: [ - { - IndexName: 'MyGSI', - KeySchema: [ - { AttributeName: 'gsiHashKey', KeyType: 'HASH' }, - { AttributeName: 'gsiSortKey', KeyType: 'RANGE' } - ], - Projection: { NonKeyAttributes: ['gsiNonKey0', 'gsiNonKey1'], ProjectionType: 'INCLUDE' }, - ProvisionedThroughput: { ReadCapacityUnits: 42, WriteCapacityUnits: 1337 } - } - ] + Projection: { NonKeyAttributes: ['gsiNonKey0', 'gsiNonKey1'], ProjectionType: 'INCLUDE' }, + ProvisionedThroughput: { ReadCapacityUnits: 42, WriteCapacityUnits: 1337 } } - } + ] } - }); - + )); test.done(); }, 'when adding a global secondary index on a table with PAY_PER_REQUEST billing mode'(test: Test) { - const app = new TestApp(); - new Table(app.stack, CONSTRUCT_NAME, { + const stack = new TestStack(); + new Table(stack, CONSTRUCT_NAME, { billingMode: BillingMode.PayPerRequest, partitionKey: TABLE_PARTITION_KEY, sortKey: TABLE_SORT_KEY @@ -689,43 +569,36 @@ export = { indexName: GSI_NAME, partitionKey: GSI_PARTITION_KEY, }); - const template = app.synthesizeTemplate(); - test.deepEqual(template, { - Resources: { - MyTable794EDED1: { - Type: 'AWS::DynamoDB::Table', - Properties: { - AttributeDefinitions: [ - { AttributeName: 'hashKey', AttributeType: 'S' }, - { AttributeName: 'sortKey', AttributeType: 'N' }, - { AttributeName: 'gsiHashKey', AttributeType: 'S' }, - ], - BillingMode: 'PAY_PER_REQUEST', + expect(stack).to(haveResource('AWS::DynamoDB::Table', + { + AttributeDefinitions: [ + { AttributeName: 'hashKey', AttributeType: 'S' }, + { AttributeName: 'sortKey', AttributeType: 'N' }, + { AttributeName: 'gsiHashKey', AttributeType: 'S' }, + ], + BillingMode: 'PAY_PER_REQUEST', + KeySchema: [ + { AttributeName: 'hashKey', KeyType: 'HASH' }, + { AttributeName: 'sortKey', KeyType: 'RANGE' } + ], + GlobalSecondaryIndexes: [ + { + IndexName: 'MyGSI', KeySchema: [ - { AttributeName: 'hashKey', KeyType: 'HASH' }, - { AttributeName: 'sortKey', KeyType: 'RANGE' } + { AttributeName: 'gsiHashKey', KeyType: 'HASH' }, ], - GlobalSecondaryIndexes: [ - { - IndexName: 'MyGSI', - KeySchema: [ - { AttributeName: 'gsiHashKey', KeyType: 'HASH' }, - ], - Projection: { ProjectionType: 'ALL' } - } - ] + Projection: { ProjectionType: 'ALL' } } - } + ] } - }); - + )); test.done(); }, 'error when adding a global secondary index with projection type INCLUDE, but without specifying non-key attributes'(test: Test) { - const app = new TestApp(); - const table = new Table(app.stack, CONSTRUCT_NAME) + const stack = new TestStack(); + const table = new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY); @@ -740,8 +613,8 @@ export = { }, 'error when adding a global secondary index with projection type ALL, but with non-key attributes'(test: Test) { - const app = new TestApp(); - const table = new Table(app.stack, CONSTRUCT_NAME) + const stack = new TestStack(); + const table = new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY); const gsiNonKeyAttributeGenerator = NON_KEY_ATTRIBUTE_GENERATOR(GSI_NON_KEY); @@ -756,8 +629,8 @@ export = { }, 'error when adding a global secondary index with projection type KEYS_ONLY, but with non-key attributes'(test: Test) { - const app = new TestApp(); - const table = new Table(app.stack, CONSTRUCT_NAME) + const stack = new TestStack(); + const table = new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY); const gsiNonKeyAttributeGenerator = NON_KEY_ATTRIBUTE_GENERATOR(GSI_NON_KEY); @@ -773,8 +646,8 @@ export = { }, 'error when adding a global secondary index with projection type INCLUDE, but with more than 20 non-key attributes'(test: Test) { - const app = new TestApp(); - const table = new Table(app.stack, CONSTRUCT_NAME) + const stack = new TestStack(); + const table = new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY); const gsiNonKeyAttributeGenerator = NON_KEY_ATTRIBUTE_GENERATOR(GSI_NON_KEY); @@ -795,8 +668,8 @@ export = { }, 'error when adding a global secondary index with projection type INCLUDE, but with key attributes'(test: Test) { - const app = new TestApp(); - const table = new Table(app.stack, CONSTRUCT_NAME) + const stack = new TestStack(); + const table = new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY); @@ -813,8 +686,8 @@ export = { }, 'error when adding a global secondary index with read or write capacity on a PAY_PER_REQUEST table'(test: Test) { - const app = new TestApp(); - const table = new Table(app.stack, CONSTRUCT_NAME, { + const stack = new TestStack(); + const table = new Table(stack, CONSTRUCT_NAME, { partitionKey: TABLE_PARTITION_KEY, billingMode: BillingMode.PayPerRequest }); @@ -843,88 +716,81 @@ export = { }, 'when adding multiple global secondary indexes'(test: Test) { - const app = new TestApp(); - const table = new Table(app.stack, CONSTRUCT_NAME) + const stack = new TestStack(); + const table = new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY); const gsiGenerator = GSI_GENERATOR(); for (let i = 0; i < 5; i++) { table.addGlobalSecondaryIndex(gsiGenerator.next().value); } - const template = app.synthesizeTemplate(); - test.deepEqual(template, { - Resources: { - MyTable794EDED1: { - Type: 'AWS::DynamoDB::Table', - Properties: { - AttributeDefinitions: [ - { AttributeName: 'hashKey', AttributeType: 'S' }, - { AttributeName: 'sortKey', AttributeType: 'N' }, - { AttributeName: 'gsiHashKey0', AttributeType: 'S' }, - { AttributeName: 'gsiHashKey1', AttributeType: 'S' }, - { AttributeName: 'gsiHashKey2', AttributeType: 'S' }, - { AttributeName: 'gsiHashKey3', AttributeType: 'S' }, - { AttributeName: 'gsiHashKey4', AttributeType: 'S' } + expect(stack).to(haveResource('AWS::DynamoDB::Table', + { + AttributeDefinitions: [ + { AttributeName: 'hashKey', AttributeType: 'S' }, + { AttributeName: 'sortKey', AttributeType: 'N' }, + { AttributeName: 'gsiHashKey0', AttributeType: 'S' }, + { AttributeName: 'gsiHashKey1', AttributeType: 'S' }, + { AttributeName: 'gsiHashKey2', AttributeType: 'S' }, + { AttributeName: 'gsiHashKey3', AttributeType: 'S' }, + { AttributeName: 'gsiHashKey4', AttributeType: 'S' } + ], + KeySchema: [ + { AttributeName: 'hashKey', KeyType: 'HASH' }, + { AttributeName: 'sortKey', KeyType: 'RANGE' } + ], + ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 }, + GlobalSecondaryIndexes: [ + { + IndexName: 'MyGSI0', + KeySchema: [ + { AttributeName: 'gsiHashKey0', KeyType: 'HASH' }, ], + Projection: { ProjectionType: 'ALL' }, + ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 } + }, + { + IndexName: 'MyGSI1', KeySchema: [ - { AttributeName: 'hashKey', KeyType: 'HASH' }, - { AttributeName: 'sortKey', KeyType: 'RANGE' } + { AttributeName: 'gsiHashKey1', KeyType: 'HASH' }, ], - ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 }, - GlobalSecondaryIndexes: [ - { - IndexName: 'MyGSI0', - KeySchema: [ - { AttributeName: 'gsiHashKey0', KeyType: 'HASH' }, - ], - Projection: { ProjectionType: 'ALL' }, - ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 } - }, - { - IndexName: 'MyGSI1', - KeySchema: [ - { AttributeName: 'gsiHashKey1', KeyType: 'HASH' }, - ], - Projection: { ProjectionType: 'ALL' }, - ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 } - }, - { - IndexName: 'MyGSI2', - KeySchema: [ - { AttributeName: 'gsiHashKey2', KeyType: 'HASH' }, - ], - Projection: { ProjectionType: 'ALL' }, - ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 } - }, - { - IndexName: 'MyGSI3', - KeySchema: [ - { AttributeName: 'gsiHashKey3', KeyType: 'HASH' }, - ], - Projection: { ProjectionType: 'ALL' }, - ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 } - }, - { - IndexName: 'MyGSI4', - KeySchema: [ - { AttributeName: 'gsiHashKey4', KeyType: 'HASH' }, - ], - Projection: { ProjectionType: 'ALL' }, - ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 } - }, - ] - } - } + Projection: { ProjectionType: 'ALL' }, + ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 } + }, + { + IndexName: 'MyGSI2', + KeySchema: [ + { AttributeName: 'gsiHashKey2', KeyType: 'HASH' }, + ], + Projection: { ProjectionType: 'ALL' }, + ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 } + }, + { + IndexName: 'MyGSI3', + KeySchema: [ + { AttributeName: 'gsiHashKey3', KeyType: 'HASH' }, + ], + Projection: { ProjectionType: 'ALL' }, + ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 } + }, + { + IndexName: 'MyGSI4', + KeySchema: [ + { AttributeName: 'gsiHashKey4', KeyType: 'HASH' }, + ], + Projection: { ProjectionType: 'ALL' }, + ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 } + }, + ] } - }); - + )); test.done(); }, 'error when adding more than 5 global secondary indexes'(test: Test) { - const app = new TestApp(); - const table = new Table(app.stack, CONSTRUCT_NAME) + const stack = new TestStack(); + const table = new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY); const gsiGenerator = GSI_GENERATOR(); @@ -939,96 +805,82 @@ export = { }, 'when adding a global secondary index without specifying read and write capacity'(test: Test) { - const app = new TestApp(); - new Table(app.stack, CONSTRUCT_NAME) + const stack = new TestStack(); + new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY) .addGlobalSecondaryIndex({ indexName: GSI_NAME, partitionKey: GSI_PARTITION_KEY, }); - const template = app.synthesizeTemplate(); - test.deepEqual(template, { - Resources: { - MyTable794EDED1: { - Type: 'AWS::DynamoDB::Table', - Properties: { - AttributeDefinitions: [ - { AttributeName: 'hashKey', AttributeType: 'S' }, - { AttributeName: 'sortKey', AttributeType: 'N' }, - { AttributeName: 'gsiHashKey', AttributeType: 'S' } - ], + expect(stack).to(haveResource('AWS::DynamoDB::Table', + { + AttributeDefinitions: [ + { AttributeName: 'hashKey', AttributeType: 'S' }, + { AttributeName: 'sortKey', AttributeType: 'N' }, + { AttributeName: 'gsiHashKey', AttributeType: 'S' } + ], + KeySchema: [ + { AttributeName: 'hashKey', KeyType: 'HASH' }, + { AttributeName: 'sortKey', KeyType: 'RANGE' } + ], + ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 }, + GlobalSecondaryIndexes: [ + { + IndexName: 'MyGSI', KeySchema: [ - { AttributeName: 'hashKey', KeyType: 'HASH' }, - { AttributeName: 'sortKey', KeyType: 'RANGE' } + { AttributeName: 'gsiHashKey', KeyType: 'HASH' }, ], - ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 }, - GlobalSecondaryIndexes: [ - { - IndexName: 'MyGSI', - KeySchema: [ - { AttributeName: 'gsiHashKey', KeyType: 'HASH' }, - ], - Projection: { ProjectionType: 'ALL' }, - ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 } - } - ] + Projection: { ProjectionType: 'ALL' }, + ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 } } - } + ] } - }); - + )); test.done(); }, 'when adding a local secondary index with hash + range key'(test: Test) { - const app = new TestApp(); - new Table(app.stack, CONSTRUCT_NAME) + const stack = new TestStack(); + new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY) .addLocalSecondaryIndex({ indexName: LSI_NAME, sortKey: LSI_SORT_KEY, }); - const template = app.synthesizeTemplate(); - test.deepEqual(template, { - Resources: { - MyTable794EDED1: { - Type: 'AWS::DynamoDB::Table', - Properties: { - AttributeDefinitions: [ - { AttributeName: 'hashKey', AttributeType: 'S' }, - { AttributeName: 'sortKey', AttributeType: 'N' }, - { AttributeName: 'lsiSortKey', AttributeType: 'N' } - ], + expect(stack).to(haveResource('AWS::DynamoDB::Table', + { + AttributeDefinitions: [ + { AttributeName: 'hashKey', AttributeType: 'S' }, + { AttributeName: 'sortKey', AttributeType: 'N' }, + { AttributeName: 'lsiSortKey', AttributeType: 'N' } + ], + KeySchema: [ + { AttributeName: 'hashKey', KeyType: 'HASH' }, + { AttributeName: 'sortKey', KeyType: 'RANGE' } + ], + ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 }, + LocalSecondaryIndexes: [ + { + IndexName: 'MyLSI', KeySchema: [ { AttributeName: 'hashKey', KeyType: 'HASH' }, - { AttributeName: 'sortKey', KeyType: 'RANGE' } - ], - ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 }, - LocalSecondaryIndexes: [ - { - IndexName: 'MyLSI', - KeySchema: [ - { AttributeName: 'hashKey', KeyType: 'HASH' }, - { AttributeName: 'lsiSortKey', KeyType: 'RANGE' } - ], - Projection: { ProjectionType: 'ALL' }, - } + { AttributeName: 'lsiSortKey', KeyType: 'RANGE' } ], + Projection: { ProjectionType: 'ALL' }, } - } + ], } - }); - + )); test.done(); }, 'when adding a local secondary index with projection type KEYS_ONLY'(test: Test) { - const app = new TestApp(); - new Table(app.stack, CONSTRUCT_NAME) + const stack = new TestStack(); + new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY) .addLocalSecondaryIndex({ @@ -1036,44 +888,37 @@ export = { sortKey: LSI_SORT_KEY, projectionType: ProjectionType.KeysOnly }); - const template = app.synthesizeTemplate(); - test.deepEqual(template, { - Resources: { - MyTable794EDED1: { - Type: 'AWS::DynamoDB::Table', - Properties: { - AttributeDefinitions: [ - { AttributeName: 'hashKey', AttributeType: 'S' }, - { AttributeName: 'sortKey', AttributeType: 'N' }, - { AttributeName: 'lsiSortKey', AttributeType: 'N' } - ], + expect(stack).to(haveResource('AWS::DynamoDB::Table', + { + AttributeDefinitions: [ + { AttributeName: 'hashKey', AttributeType: 'S' }, + { AttributeName: 'sortKey', AttributeType: 'N' }, + { AttributeName: 'lsiSortKey', AttributeType: 'N' } + ], + KeySchema: [ + { AttributeName: 'hashKey', KeyType: 'HASH' }, + { AttributeName: 'sortKey', KeyType: 'RANGE' } + ], + ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 }, + LocalSecondaryIndexes: [ + { + IndexName: 'MyLSI', KeySchema: [ { AttributeName: 'hashKey', KeyType: 'HASH' }, - { AttributeName: 'sortKey', KeyType: 'RANGE' } - ], - ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 }, - LocalSecondaryIndexes: [ - { - IndexName: 'MyLSI', - KeySchema: [ - { AttributeName: 'hashKey', KeyType: 'HASH' }, - { AttributeName: 'lsiSortKey', KeyType: 'RANGE' } - ], - Projection: { ProjectionType: 'KEYS_ONLY' }, - } + { AttributeName: 'lsiSortKey', KeyType: 'RANGE' } ], + Projection: { ProjectionType: 'KEYS_ONLY' }, } - } + ], } - }); - + )); test.done(); }, 'when adding a local secondary index with projection type INCLUDE'(test: Test) { - const app = new TestApp(); - const table = new Table(app.stack, CONSTRUCT_NAME) + const stack = new TestStack(); + const table = new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY); const lsiNonKeyAttributeGenerator = NON_KEY_ATTRIBUTE_GENERATOR(LSI_NON_KEY); @@ -1084,44 +929,37 @@ export = { nonKeyAttributes: [ lsiNonKeyAttributeGenerator.next().value, lsiNonKeyAttributeGenerator.next().value ] }); - const template = app.synthesizeTemplate(); - test.deepEqual(template, { - Resources: { - MyTable794EDED1: { - Type: 'AWS::DynamoDB::Table', - Properties: { - AttributeDefinitions: [ - { AttributeName: 'hashKey', AttributeType: 'S' }, - { AttributeName: 'sortKey', AttributeType: 'N' }, - { AttributeName: 'lsiSortKey', AttributeType: 'N' } - ], + expect(stack).to(haveResource('AWS::DynamoDB::Table', + { + AttributeDefinitions: [ + { AttributeName: 'hashKey', AttributeType: 'S' }, + { AttributeName: 'sortKey', AttributeType: 'N' }, + { AttributeName: 'lsiSortKey', AttributeType: 'N' } + ], + KeySchema: [ + { AttributeName: 'hashKey', KeyType: 'HASH' }, + { AttributeName: 'sortKey', KeyType: 'RANGE' } + ], + ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 }, + LocalSecondaryIndexes: [ + { + IndexName: 'MyLSI', KeySchema: [ { AttributeName: 'hashKey', KeyType: 'HASH' }, - { AttributeName: 'sortKey', KeyType: 'RANGE' } - ], - ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5 }, - LocalSecondaryIndexes: [ - { - IndexName: 'MyLSI', - KeySchema: [ - { AttributeName: 'hashKey', KeyType: 'HASH' }, - { AttributeName: 'lsiSortKey', KeyType: 'RANGE' } - ], - Projection: { NonKeyAttributes: ['lsiNonKey0', 'lsiNonKey1'], ProjectionType: 'INCLUDE' }, - } + { AttributeName: 'lsiSortKey', KeyType: 'RANGE' } ], + Projection: { NonKeyAttributes: ['lsiNonKey0', 'lsiNonKey1'], ProjectionType: 'INCLUDE' }, } - } + ], } - }); - + )); test.done(); }, 'error when adding more than 5 local secondary indexes'(test: Test) { - const app = new TestApp(); - const table = new Table(app.stack, CONSTRUCT_NAME) + const stack = new TestStack(); + const table = new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY); const lsiGenerator = LSI_GENERATOR(); @@ -1136,8 +974,8 @@ export = { }, 'error when adding a local secondary index before specifying a partition key of the table'(test: Test) { - const app = new TestApp(); - const table = new Table(app.stack, CONSTRUCT_NAME) + const stack = new TestStack(); + const table = new Table(stack, CONSTRUCT_NAME) .addSortKey(TABLE_SORT_KEY); test.throws(() => table.addLocalSecondaryIndex({ @@ -1149,8 +987,8 @@ export = { }, 'error when adding a local secondary index with the name of a global secondary index'(test: Test) { - const app = new TestApp(); - const table = new Table(app.stack, CONSTRUCT_NAME) + const stack = new TestStack(); + const table = new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY); table.addGlobalSecondaryIndex({ @@ -1167,8 +1005,8 @@ export = { }, 'error when validating construct if a local secondary index exists without a sort key of the table'(test: Test) { - const app = new TestApp(); - const table = new Table(app.stack, CONSTRUCT_NAME) + const stack = new TestStack(); + const table = new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY); table.addLocalSecondaryIndex({ indexName: LSI_NAME, @@ -1185,21 +1023,21 @@ export = { 'can enable Read AutoScaling'(test: Test) { // GIVEN - const app = new TestApp(); - const table = new Table(app.stack, CONSTRUCT_NAME, { readCapacity: 42, writeCapacity: 1337 }); + const stack = new TestStack(); + const table = new Table(stack, CONSTRUCT_NAME, { readCapacity: 42, writeCapacity: 1337 }); table.addPartitionKey(TABLE_PARTITION_KEY); // WHEN table.autoScaleReadCapacity({ minCapacity: 50, maxCapacity: 500 }).scaleOnUtilization({ targetUtilizationPercent: 75 }); // THEN - expect(app.stack).to(haveResource('AWS::ApplicationAutoScaling::ScalableTarget', { + expect(stack).to(haveResource('AWS::ApplicationAutoScaling::ScalableTarget', { MaxCapacity: 500, MinCapacity: 50, ScalableDimension: 'dynamodb:table:ReadCapacityUnits', ServiceNamespace: 'dynamodb' })); - expect(app.stack).to(haveResource('AWS::ApplicationAutoScaling::ScalingPolicy', { + expect(stack).to(haveResource('AWS::ApplicationAutoScaling::ScalingPolicy', { PolicyType: 'TargetTrackingScaling', TargetTrackingScalingPolicyConfiguration: { PredefinedMetricSpecification: { PredefinedMetricType: 'DynamoDBReadCapacityUtilization' }, @@ -1212,21 +1050,21 @@ export = { 'can enable Write AutoScaling'(test: Test) { // GIVEN - const app = new TestApp(); - const table = new Table(app.stack, CONSTRUCT_NAME, { readCapacity: 42, writeCapacity: 1337 }); + const stack = new TestStack(); + const table = new Table(stack, CONSTRUCT_NAME, { readCapacity: 42, writeCapacity: 1337 }); table.addPartitionKey(TABLE_PARTITION_KEY); // WHEN table.autoScaleWriteCapacity({ minCapacity: 50, maxCapacity: 500 }).scaleOnUtilization({ targetUtilizationPercent: 75 }); // THEN - expect(app.stack).to(haveResource('AWS::ApplicationAutoScaling::ScalableTarget', { + expect(stack).to(haveResource('AWS::ApplicationAutoScaling::ScalableTarget', { MaxCapacity: 500, MinCapacity: 50, ScalableDimension: 'dynamodb:table:WriteCapacityUnits', ServiceNamespace: 'dynamodb' })); - expect(app.stack).to(haveResource('AWS::ApplicationAutoScaling::ScalingPolicy', { + expect(stack).to(haveResource('AWS::ApplicationAutoScaling::ScalingPolicy', { PolicyType: 'TargetTrackingScaling', TargetTrackingScalingPolicyConfiguration: { PredefinedMetricSpecification: { PredefinedMetricType: 'DynamoDBWriteCapacityUtilization' }, @@ -1239,8 +1077,8 @@ export = { 'cannot enable AutoScaling twice on the same property'(test: Test) { // GIVEN - const app = new TestApp(); - const table = new Table(app.stack, CONSTRUCT_NAME, { readCapacity: 42, writeCapacity: 1337 }); + const stack = new TestStack(); + const table = new Table(stack, CONSTRUCT_NAME, { readCapacity: 42, writeCapacity: 1337 }); table.addPartitionKey(TABLE_PARTITION_KEY); table.autoScaleReadCapacity({ minCapacity: 50, maxCapacity: 500 }).scaleOnUtilization({ targetUtilizationPercent: 75 }); @@ -1254,8 +1092,8 @@ export = { 'error when enabling AutoScaling on the PAY_PER_REQUEST table'(test: Test) { // GIVEN - const app = new TestApp(); - const table = new Table(app.stack, CONSTRUCT_NAME, { billingMode: BillingMode.PayPerRequest }); + const stack = new TestStack(); + const table = new Table(stack, CONSTRUCT_NAME, { billingMode: BillingMode.PayPerRequest }); table.addPartitionKey(TABLE_PARTITION_KEY); table.addGlobalSecondaryIndex({ indexName: GSI_NAME, @@ -1279,8 +1117,8 @@ export = { 'error when specifying Read Auto Scaling with invalid scalingTargetValue < 10'(test: Test) { // GIVEN - const app = new TestApp(); - const table = new Table(app.stack, CONSTRUCT_NAME, { readCapacity: 42, writeCapacity: 1337 }); + const stack = new TestStack(); + const table = new Table(stack, CONSTRUCT_NAME, { readCapacity: 42, writeCapacity: 1337 }); // THEN test.throws(() => { @@ -1292,8 +1130,8 @@ export = { 'error when specifying Read Auto Scaling with invalid minimumCapacity'(test: Test) { // GIVEN - const app = new TestApp(); - const table = new Table(app.stack, CONSTRUCT_NAME, { readCapacity: 42, writeCapacity: 1337 }); + const stack = new TestStack(); + const table = new Table(stack, CONSTRUCT_NAME, { readCapacity: 42, writeCapacity: 1337 }); // THEN test.throws(() => table.autoScaleReadCapacity({ minCapacity: 10, maxCapacity: 5 })); @@ -1303,8 +1141,8 @@ export = { 'can autoscale on a schedule'(test: Test) { // GIVEN - const app = new TestApp(); - const table = new Table(app.stack, CONSTRUCT_NAME, { readCapacity: 42, writeCapacity: 1337 }); + const stack = new TestStack(); + const table = new Table(stack, CONSTRUCT_NAME, { readCapacity: 42, writeCapacity: 1337 }); table.addPartitionKey({ name: 'Hash', type: AttributeType.String }); // WHEN @@ -1315,7 +1153,7 @@ export = { }); // THEN - expect(app.stack).to(haveResource('AWS::ApplicationAutoScaling::ScalableTarget', { + expect(stack).to(haveResource('AWS::ApplicationAutoScaling::ScalableTarget', { ScheduledActions: [ { ScalableTargetAction: { "MaxCapacity": 10 }, @@ -1335,47 +1173,31 @@ export = { [ 'action1', 'action2' ], (p, t) => t.grant(p, 'dynamodb:action1', 'dynamodb:action2')); }, - '"grantReadData" allows the principal to read data from the table'(test: Test) { - testGrant(test, - [ 'BatchGetItem', 'GetRecords', 'GetShardIterator', 'Query', 'GetItem', 'Scan' ], (p, t) => t.grantReadData(p)); - }, + '"grantReadData" allows the principal to read data from the table'(test: Test) { + testGrant(test, + [ 'BatchGetItem', 'GetRecords', 'GetShardIterator', 'Query', 'GetItem', 'Scan' ], (p, t) => t.grantReadData(p)); + }, - '"grantWriteData" allows the principal to write data to the table'(test: Test) { - testGrant(test, [ - 'BatchWriteItem', 'PutItem', 'UpdateItem', 'DeleteItem' ], (p, t) => t.grantWriteData(p)); - }, + '"grantWriteData" allows the principal to write data to the table'(test: Test) { + testGrant(test, [ + 'BatchWriteItem', 'PutItem', 'UpdateItem', 'DeleteItem' ], (p, t) => t.grantWriteData(p)); + }, - '"grantReadWriteData" allows the principal to read/write data'(test: Test) { - testGrant(test, [ - 'BatchGetItem', 'GetRecords', 'GetShardIterator', 'Query', 'GetItem', 'Scan', - 'BatchWriteItem', 'PutItem', 'UpdateItem', 'DeleteItem' ], (p, t) => t.grantReadWriteData(p)); - }, + '"grantReadWriteData" allows the principal to read/write data'(test: Test) { + testGrant(test, [ + 'BatchGetItem', 'GetRecords', 'GetShardIterator', 'Query', 'GetItem', 'Scan', + 'BatchWriteItem', 'PutItem', 'UpdateItem', 'DeleteItem' ], (p, t) => t.grantReadWriteData(p)); + }, - '"grantFullAccess" allows the principal to perform any action on the table ("*")'(test: Test) { - testGrant(test, [ '*' ], (p, t) => t.grantFullAccess(p)); - } + '"grantFullAccess" allows the principal to perform any action on the table ("*")'(test: Test) { + testGrant(test, [ '*' ], (p, t) => t.grantFullAccess(p)); + } }, }; -class TestStack extends Stack { - public testInvokeAspects() { - this.invokeAspects(); - } -} -class TestApp { - private readonly app = new App(); - // tslint:disable-next-line:member-ordering - public readonly stack: TestStack = new TestStack(this.app, STACK_NAME); - - public synthesizeTemplate() { - this.stack.testInvokeAspects(); - return this.app.synthesizeStack(this.stack.name).template; - } -} - function testGrant(test: Test, expectedActions: string[], invocation: (user: iam.IPrincipal, table: Table) => void) { // GIVEN - const stack = new Stack(); + const stack = new TestStack(); const table = new Table(stack, 'my-table'); table.addPartitionKey({ name: 'ID', type: AttributeType.String }); From 67a738b7a866fc8ffd6bad118a57184cdce45b35 Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Wed, 16 Jan 2019 01:27:43 -0800 Subject: [PATCH 09/38] test stack cleanup for kms --- packages/@aws-cdk/aws-kms/test/test.key.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/@aws-cdk/aws-kms/test/test.key.ts b/packages/@aws-cdk/aws-kms/test/test.key.ts index 98859138bf705..027b4dcb5e057 100644 --- a/packages/@aws-cdk/aws-kms/test/test.key.ts +++ b/packages/@aws-cdk/aws-kms/test/test.key.ts @@ -6,12 +6,11 @@ import { EncryptionKey } from '../lib'; export = { 'default key'(test: Test) { - const app = new App(); - const stack = new TestStack(app, 'TestStack'); + const stack = new TestStack(); new EncryptionKey(stack, 'MyKey'); - expect(app.synthesizeStack(stack.name)).to(exactlyMatchTemplate({ + expect(stack).to(exactlyMatchTemplate({ Resources: { MyKey6AB29FA6: { Type: "AWS::KMS::Key", @@ -138,8 +137,7 @@ export = { }, 'key with some options'(test: Test) { - const app = new App(); - const stack = new TestStack(app, 'Test'); + const stack = new TestStack(); const key = new EncryptionKey(stack, 'MyKey', { enableKeyRotation: true, @@ -153,8 +151,7 @@ export = { key.apply(new Tag('tag2', 'value2')); key.apply(new Tag('tag3', '')); - stack.testInvokeAspects(); - expect(app.synthesizeStack(stack.name)).to(exactlyMatchTemplate({ + expect(stack).to(exactlyMatchTemplate({ Resources: { MyKey6AB29FA6: { Type: "AWS::KMS::Key", From 068726a1d53bdd5184b5c493905c4d7056ee89c7 Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Wed, 16 Jan 2019 08:30:13 -0800 Subject: [PATCH 10/38] fixing dynamo typos in tests --- packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts b/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts index 92eea5dffd75a..8e96f6e7230eb 100644 --- a/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts +++ b/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts @@ -1,4 +1,4 @@ -import { expect, countResources, haveResource, TestStack } from '@aws-cdk/assert'; +import { countResources, expect, haveResource, TestStack } from '@aws-cdk/assert'; import iam = require('@aws-cdk/aws-iam'); import { Tag } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; @@ -929,7 +929,6 @@ export = { nonKeyAttributes: [ lsiNonKeyAttributeGenerator.next().value, lsiNonKeyAttributeGenerator.next().value ] }); - expect(stack).to(haveResource('AWS::DynamoDB::Table', { AttributeDefinitions: [ From 63895747e67ca8e68d9505b8e793e87338c01d8a Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Thu, 17 Jan 2019 23:35:34 -0800 Subject: [PATCH 11/38] refactoring for naming of visitTree and PR comments --- packages/@aws-cdk/cdk/lib/aspects/aspect.ts | 10 ++-- .../@aws-cdk/cdk/lib/aspects/tag-aspect.ts | 51 +++++++++---------- .../cdk/lib/cloudformation/resource.ts | 13 +++-- packages/@aws-cdk/cdk/lib/core/construct.ts | 2 +- 4 files changed, 39 insertions(+), 37 deletions(-) diff --git a/packages/@aws-cdk/cdk/lib/aspects/aspect.ts b/packages/@aws-cdk/cdk/lib/aspects/aspect.ts index 4c138ae646648..f73b117c7ca34 100644 --- a/packages/@aws-cdk/cdk/lib/aspects/aspect.ts +++ b/packages/@aws-cdk/cdk/lib/aspects/aspect.ts @@ -12,7 +12,7 @@ export interface IAspect { /** * All aspects can visit by IConstructs */ - visit(node: IConstruct): void; + visitTree(node: IConstruct): void; } /** @@ -32,15 +32,15 @@ export abstract class Aspect implements IAspect { * The visit function will visit each child node in the construct tree. Each * Node will only be visited once. */ - public visit(construct: IConstruct): void { + public visitTree(construct: IConstruct): void { if (this.visitedBy[construct.node.uniqueId] === true) { return; } this.visitedBy[construct.node.uniqueId] = true; - this.visitAction(construct); + this.visit(construct); for (const child of construct.node.children) { // recurse through all children - this.visit(child); + this.visitTree(child); } } @@ -50,5 +50,5 @@ export abstract class Aspect implements IAspect { * The ``visit()`` function will call this method to invoke the customized * apsect actions. */ - protected abstract visitAction(construct: IConstruct): void; + protected abstract visit(construct: IConstruct): void; } diff --git a/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts b/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts index 783d591548c1e..144d368c0c226 100644 --- a/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts +++ b/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts @@ -2,14 +2,7 @@ import { ITaggable, Resource } from '../cloudformation/resource'; import { IConstruct } from '../core/construct'; import { Aspect } from './aspect'; -export interface TagBaseProps extends TagProperties { - /** - * The value of the tag - */ - value?: string; -} - -export interface TagProperties { +export interface TagAspectProps { /** * This applies specifically to AutoScalingGroup PropagateAtLaunch */ @@ -17,11 +10,20 @@ export interface TagProperties { /** * An array of Resource Types that will receive this tag + * + * An empty array will match any Resource. A non-empty array will apply this + * tag only to Resource types that are included in this array. + * @default [] */ include?: string[]; /** * An array of Resource Types that will not receive this tag + * + * An empty array will allow this tag to be applied to all resources. A + * non-empty array will apply this tag only if the Resource type is not in + * this array. + * @default [] */ exclude?: string[]; } @@ -31,13 +33,6 @@ export interface TagProperties { */ export abstract class TagBase extends Aspect { - /** - * Test if the construct is a CloudFormation Resource - */ - public static isResource(resource: any): resource is Resource { - return resource.resourceType !== undefined; - } - /** * The ``taggable`` type for these aspects */ @@ -48,25 +43,19 @@ export abstract class TagBase extends Aspect { */ public readonly key: string; - /** - * The string value of the tag - */ - public readonly value?: string; - private readonly include: string[]; private readonly exclude: string[]; - constructor(key: string, props: TagBaseProps = {}) { + constructor(key: string, props: TagAspectProps = {}) { super(); this.key = key; - this.value = props.value; this.include = props.include || []; this.exclude = props.exclude || []; } - protected visitAction(construct: IConstruct): void { - if (!TagBase.isResource(construct)) { + public visit(construct: IConstruct): void { + if (!Resource.isResource(construct)) { return; } const resource = construct as Resource; @@ -89,14 +78,20 @@ export abstract class TagBase extends Aspect { */ export class Tag extends TagBase { + /** + * The string value of the tag + */ + public readonly value: string; + private readonly applyToLaunchInstances: boolean; - constructor(key: string, value: string, props: TagProperties = {}) { - super(key, {value, ...props}); + constructor(key: string, value: string, props: TagAspectProps = {}) { + super(key, {...props}); this.applyToLaunchInstances = props.applyToLaunchInstances !== false; - if (this.value === undefined) { + if (value === undefined) { throw new Error('Tag must have a value'); } + this.value = value; } protected applyTag(resource: ITaggable) { @@ -109,7 +104,7 @@ export class Tag extends TagBase { */ export class RemoveTag extends TagBase { - constructor(key: string, props: TagProperties = {}) { + constructor(key: string, props: TagAspectProps = {}) { super(key, props); } diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/resource.ts b/packages/@aws-cdk/cdk/lib/cloudformation/resource.ts index 06dafb9b944fd..c5eba79da3763 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/resource.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/resource.ts @@ -52,6 +52,13 @@ export class Resource extends Referenceable { return resource.tags !== undefined; } + /** + * determines if the reosurce is taggable + */ + public static isResource(resource: any): resource is Resource { + return resource.resourceType !== undefined; + } + /** * Options for this resource, such as condition, update policy etc. */ @@ -259,9 +266,9 @@ export class Resource extends Referenceable { export enum TagType { Standard = 'StandardTag', - AutoScalingGroup = 'AutoScalingGroupTag', - Map = 'StringToStringMap', - NotTaggable = 'NotTaggable', + AutoScalingGroup = 'AutoScalingGroupTag', + Map = 'StringToStringMap', + NotTaggable = 'NotTaggable', } export interface ResourceOptions { diff --git a/packages/@aws-cdk/cdk/lib/core/construct.ts b/packages/@aws-cdk/cdk/lib/core/construct.ts index fe09c27a0557e..57873d2b6ff1f 100644 --- a/packages/@aws-cdk/cdk/lib/core/construct.ts +++ b/packages/@aws-cdk/cdk/lib/core/construct.ts @@ -548,7 +548,7 @@ export class Construct implements IConstruct { */ protected invokeAspects(): void { for (const aspect of this.node.aspects) { - aspect.visit(this); + aspect.visitTree(this); } for (const child of this.node.children) { (child as Construct).invokeAspects(); From 12be7ad632965d9a6aa22e25a3e67d81735e06c1 Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Fri, 18 Jan 2019 00:33:34 -0800 Subject: [PATCH 12/38] moving aspects into prepareTree() --- packages/@aws-cdk/cdk/lib/app.ts | 3 --- packages/@aws-cdk/cdk/lib/aspects/aspect.ts | 14 +++++------ packages/@aws-cdk/cdk/lib/core/construct.ts | 23 ++++++++++--------- .../cdk/test/aspects/test.tag-aspect.ts | 2 +- 4 files changed, 19 insertions(+), 23 deletions(-) diff --git a/packages/@aws-cdk/cdk/lib/app.ts b/packages/@aws-cdk/cdk/lib/app.ts index 2d985ab006c2d..1d51d6f4ab033 100644 --- a/packages/@aws-cdk/cdk/lib/app.ts +++ b/packages/@aws-cdk/cdk/lib/app.ts @@ -41,9 +41,6 @@ export class App extends Root { return; } - // invoke all aspects before rendering - this.invokeAspects(); - const result: cxapi.SynthesizeResponse = { version: cxapi.PROTO_RESPONSE_VERSION, stacks: this.synthesizeStacks(Object.keys(this.stacks)), diff --git a/packages/@aws-cdk/cdk/lib/aspects/aspect.ts b/packages/@aws-cdk/cdk/lib/aspects/aspect.ts index f73b117c7ca34..02638bdc789f3 100644 --- a/packages/@aws-cdk/cdk/lib/aspects/aspect.ts +++ b/packages/@aws-cdk/cdk/lib/aspects/aspect.ts @@ -33,14 +33,12 @@ export abstract class Aspect implements IAspect { * Node will only be visited once. */ public visitTree(construct: IConstruct): void { - if (this.visitedBy[construct.node.uniqueId] === true) { - return; - } - this.visitedBy[construct.node.uniqueId] = true; - this.visit(construct); - for (const child of construct.node.children) { - // recurse through all children - this.visitTree(child); + for (const child of construct.node.findAll()) { + if (this.visitedBy[child.node.uniqueId] === true) { + return; + } + this.visitedBy[child.node.uniqueId] = true; + this.visit(child); } } diff --git a/packages/@aws-cdk/cdk/lib/core/construct.ts b/packages/@aws-cdk/cdk/lib/core/construct.ts index 57873d2b6ff1f..d21c55e5595ea 100644 --- a/packages/@aws-cdk/cdk/lib/core/construct.ts +++ b/packages/@aws-cdk/cdk/lib/core/construct.ts @@ -295,6 +295,7 @@ export class ConstructNode { * Run 'prepare()' on all constructs in the tree */ public prepareTree() { + this.invokeAspects(); const constructs = this.host.node.findAll(ConstructOrder.BreadthFirst); // Use .reverse() to achieve post-order traversal for (const construct of constructs.reverse()) { @@ -452,6 +453,17 @@ export class ConstructNode { return Array.from(ret); } + /** + * Triggers each aspect to invoke visit + */ + protected invokeAspects(): void { + for (const aspect of this.aspects) { + aspect.visitTree(this.host); + } + for (const child of this.children) { + (child as Construct).node.invokeAspects(); + } + } /** * Return the path of components up to but excluding the root */ @@ -543,17 +555,6 @@ export class Construct implements IConstruct { // Intentionally left blank } - /** - * Triggers each aspect to invoke visit - */ - protected invokeAspects(): void { - for (const aspect of this.node.aspects) { - aspect.visitTree(this); - } - for (const child of this.node.children) { - (child as Construct).invokeAspects(); - } - } } /** diff --git a/packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts b/packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts index f8dda2abaff67..ab3e11ae7a955 100644 --- a/packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts +++ b/packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts @@ -21,7 +21,7 @@ class TestRoot extends Stack { super(undefined, 'TestStack', { env: { account: '123456789012', region: 'us-east-1' } }); } public testInvokeAspects() { - this.invokeAspects(); + this.node.prepareTree(); } } From 257a15f77ad8c0ba4cfa312d1bfdb921b488f5c7 Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Sat, 19 Jan 2019 05:51:21 -0800 Subject: [PATCH 13/38] refactor aspect control to construct --- packages/@aws-cdk/cdk/lib/aspects/aspect.ts | 27 +---------- .../@aws-cdk/cdk/lib/aspects/tag-aspect.ts | 10 +--- packages/@aws-cdk/cdk/lib/core/construct.ts | 22 +++++---- .../cdk/test/aspects/test.tag-aspect.ts | 48 +++++++++---------- 4 files changed, 42 insertions(+), 65 deletions(-) diff --git a/packages/@aws-cdk/cdk/lib/aspects/aspect.ts b/packages/@aws-cdk/cdk/lib/aspects/aspect.ts index 02638bdc789f3..e55fcc37308f7 100644 --- a/packages/@aws-cdk/cdk/lib/aspects/aspect.ts +++ b/packages/@aws-cdk/cdk/lib/aspects/aspect.ts @@ -4,15 +4,10 @@ import { IConstruct } from '../core/construct'; * Represents an Aspect */ export interface IAspect { - /** - * The type of Aspect - */ - readonly type: string; - /** * All aspects can visit by IConstructs */ - visitTree(node: IConstruct): void; + visit(node: IConstruct): void; } /** @@ -24,29 +19,11 @@ export abstract class Aspect implements IAspect { */ public abstract readonly type: string; - private readonly visitedBy: {[id: string]: boolean} = {}; - - /** - * The visit function is invoked during synthesis for each aspect - * - * The visit function will visit each child node in the construct tree. Each - * Node will only be visited once. - */ - public visitTree(construct: IConstruct): void { - for (const child of construct.node.findAll()) { - if (this.visitedBy[child.node.uniqueId] === true) { - return; - } - this.visitedBy[child.node.uniqueId] = true; - this.visit(child); - } - } - /** * This is the function concrete Aspects should implement * * The ``visit()`` function will call this method to invoke the customized * apsect actions. */ - protected abstract visit(construct: IConstruct): void; + public abstract visit(construct: IConstruct): void; } diff --git a/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts b/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts index 144d368c0c226..9e51246f3b2ce 100644 --- a/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts +++ b/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts @@ -1,6 +1,6 @@ import { ITaggable, Resource } from '../cloudformation/resource'; import { IConstruct } from '../core/construct'; -import { Aspect } from './aspect'; +import { IAspect } from './aspect'; export interface TagAspectProps { /** @@ -31,12 +31,7 @@ export interface TagAspectProps { /** * The common functionality for Tag and Remove Tag Aspects */ -export abstract class TagBase extends Aspect { - - /** - * The ``taggable`` type for these aspects - */ - public readonly type: string = 'taggable'; +export abstract class TagBase implements IAspect { /** * The string key for the tag @@ -47,7 +42,6 @@ export abstract class TagBase extends Aspect { private readonly exclude: string[]; constructor(key: string, props: TagAspectProps = {}) { - super(); this.key = key; this.include = props.include || []; diff --git a/packages/@aws-cdk/cdk/lib/core/construct.ts b/packages/@aws-cdk/cdk/lib/core/construct.ts index d21c55e5595ea..b7e02079e9a3c 100644 --- a/packages/@aws-cdk/cdk/lib/core/construct.ts +++ b/packages/@aws-cdk/cdk/lib/core/construct.ts @@ -51,6 +51,8 @@ export class ConstructNode { */ private _locked = false; + private invokedAspects: IAspect[] = []; + constructor(private readonly host: Construct, scope: IConstruct, id: string) { id = id || ''; // if undefined, convert to empty string @@ -295,8 +297,11 @@ export class ConstructNode { * Run 'prepare()' on all constructs in the tree */ public prepareTree() { - this.invokeAspects(); const constructs = this.host.node.findAll(ConstructOrder.BreadthFirst); + // Aspects are applied root to leaf + for (const construct of constructs) { + construct.node.invokeAspects(); + } // Use .reverse() to achieve post-order traversal for (const construct of constructs.reverse()) { if (Construct.isConstruct(construct)) { @@ -456,13 +461,14 @@ export class ConstructNode { /** * Triggers each aspect to invoke visit */ - protected invokeAspects(): void { - for (const aspect of this.aspects) { - aspect.visitTree(this.host); - } - for (const child of this.children) { - (child as Construct).node.invokeAspects(); - } + private invokeAspects(): void { + const descendants = this.findAll(); + const nonInvoked = this.aspects.filter( + aspect => !this.invokedAspects.includes(aspect)); + nonInvoked.forEach( aspect => { + descendants.forEach( member => aspect.visit(member)) + this.invokedAspects.push(aspect); + }); } /** * Return the path of components up to but excluding the root diff --git a/packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts b/packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts index ab3e11ae7a955..cc7080f8f7b69 100644 --- a/packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts +++ b/packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts @@ -16,18 +16,18 @@ class MapTaggableResource extends TaggableResource { public readonly tags = new TagManager(TagType.Map); } -class TestRoot extends Stack { - constructor() { - super(undefined, 'TestStack', { env: { account: '123456789012', region: 'us-east-1' } }); - } - public testInvokeAspects() { - this.node.prepareTree(); - } -} +// class TestRoot extends Stack { +// constructor() { +// super(undefined, 'TestStack', { env: { account: '123456789012', region: 'us-east-1' } }); +// } +// public testInvokeAspects() { +// this.node.prepareTree(); +// } +// } export = { 'Tag visit all children of the applied node'(test: Test) { - const root = new TestRoot(); + const root = new Stack(); const res = new TaggableResource(root, 'FakeResource', { type: 'AWS::Fake::Thing', }); @@ -43,7 +43,7 @@ export = { }); res.apply(new Tag('foo', 'bar')); test.deepEqual(res.node.aspects.length, 1); - root.testInvokeAspects(); + root.node.prepareTree(); test.deepEqual(res.tags.renderTags(), [{key: 'foo', value: 'bar'}]); test.deepEqual(res2.tags.renderTags(), [{key: 'foo', value: 'bar'}]); test.deepEqual(map.tags.renderTags(), {foo: 'bar'}); @@ -51,7 +51,7 @@ export = { test.done(); }, 'The last aspect applied takes precedence'(test: Test) { - const root = new TestRoot(); + const root = new Stack(); const res = new TaggableResource(root, 'FakeResource', { type: 'AWS::Fake::Thing', }); @@ -62,13 +62,13 @@ export = { res.apply(new Tag('foo', 'foobar')); res.apply(new Tag('foo', 'baz')); res2.apply(new Tag('foo', 'good')); - root.testInvokeAspects(); + root.node.prepareTree(); test.deepEqual(res.tags.renderTags(), [{key: 'foo', value: 'baz'}]); test.deepEqual(res2.tags.renderTags(), [{key: 'foo', value: 'good'}]); test.done(); }, 'RemoveTag will remove a tag if it exists'(test: Test) { - const root = new TestRoot(); + const root = new Stack(); const res = new TaggableResource(root, 'FakeResource', { type: 'AWS::Fake::Thing', }); @@ -86,7 +86,7 @@ export = { res.apply(new Tag('first', 'there is only 1')); res.apply(new RemoveTag('root')); res.apply(new RemoveTag('doesnotexist')); - root.testInvokeAspects(); + root.node.prepareTree(); test.deepEqual(res.tags.renderTags(), [{key: 'first', value: 'there is only 1'}]); test.deepEqual(map.tags.renderTags(), {first: 'there is only 1'}); @@ -95,22 +95,22 @@ export = { test.done(); }, 'the #visit function is idempotent'(test: Test) { - const root = new TestRoot(); + const root = new Stack(); const res = new TaggableResource(root, 'FakeResource', { type: 'AWS::Fake::Thing', }); res.apply(new Tag('foo', 'bar')); - root.testInvokeAspects(); + root.node.prepareTree(); test.deepEqual(res.tags.renderTags(), [{key: 'foo', value: 'bar'}]); - root.testInvokeAspects(); + root.node.prepareTree(); test.deepEqual(res.tags.renderTags(), [{key: 'foo', value: 'bar'}]); - root.testInvokeAspects(); + root.node.prepareTree(); test.deepEqual(res.tags.renderTags(), [{key: 'foo', value: 'bar'}]); test.done(); }, 'include restricts tag application to resources types in the list'(test: Test) { - const root = new TestRoot(); + const root = new Stack(); const res = new TaggableResource(root, 'FakeResource', { type: 'AWS::Fake::Thing', }); @@ -125,7 +125,7 @@ export = { type: 'AWS::Fake::Map', }); res.apply(new Tag('foo', 'bar', {include: ['AWS::Fake::Asg']})); - root.testInvokeAspects(); + root.node.prepareTree(); test.deepEqual(res.tags.renderTags(), undefined); test.deepEqual(map.tags.renderTags(), undefined); test.deepEqual(res2.tags.renderTags(), undefined); @@ -134,7 +134,7 @@ export = { test.done(); }, 'exclude prevents tag application to resource types in the list'(test: Test) { - const root = new TestRoot(); + const root = new Stack(); const res = new TaggableResource(root, 'FakeResource', { type: 'AWS::Fake::Thing', }); @@ -149,7 +149,7 @@ export = { type: 'AWS::Fake::Map', }); res.apply(new Tag('foo', 'bar', {exclude: ['AWS::Fake::Asg']})); - root.testInvokeAspects(); + root.node.prepareTree(); test.deepEqual(res.tags.renderTags(), [{key: 'foo', value: 'bar'}]); test.deepEqual(res2.tags.renderTags(), [{key: 'foo', value: 'bar'}]); test.deepEqual(asg.tags.renderTags(), undefined); @@ -157,7 +157,7 @@ export = { test.done(); }, 'Aspects are mutually exclusive with tags created by L1 Constructor'(test: Test) { - const root = new TestRoot(); + const root = new Stack(); const aspectBranch = new TaggableResource(root, 'FakeBranchA', { type: 'AWS::Fake::Thing', properties: { @@ -175,7 +175,7 @@ export = { }, }); aspectBranch.apply(new Tag('aspects', 'rule')); - root.testInvokeAspects(); + root.node.prepareTree(); test.deepEqual(aspectBranch.tags.renderTags(), [{key: 'aspects', value: 'rule'}]); test.deepEqual(cfnBranch.testProperties().tags, [{key: 'cfn', value: 'is cool'}]); test.done(); From 522e08ca199adbeb9fc435cd61d90461a954c159 Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Sat, 19 Jan 2019 13:09:38 -0800 Subject: [PATCH 14/38] adding multiple visit aspects --- packages/@aws-cdk/cdk/lib/aspects/aspect.ts | 30 ++++----- .../@aws-cdk/cdk/lib/aspects/tag-aspect.ts | 3 +- packages/@aws-cdk/cdk/lib/core/construct.ts | 14 ++-- .../cdk/test/aspects/test.tag-aspect.ts | 9 --- packages/@aws-cdk/cdk/test/test.aspect.ts | 64 +++++++++++++++++++ 5 files changed, 90 insertions(+), 30 deletions(-) create mode 100644 packages/@aws-cdk/cdk/test/test.aspect.ts diff --git a/packages/@aws-cdk/cdk/lib/aspects/aspect.ts b/packages/@aws-cdk/cdk/lib/aspects/aspect.ts index e55fcc37308f7..086d1df3148ad 100644 --- a/packages/@aws-cdk/cdk/lib/aspects/aspect.ts +++ b/packages/@aws-cdk/cdk/lib/aspects/aspect.ts @@ -1,29 +1,27 @@ import { IConstruct } from '../core/construct'; -/** - * Represents an Aspect - */ -export interface IAspect { +export enum AspectVisitType { /** - * All aspects can visit by IConstructs + * The aspect will visit each Construct only once */ - visit(node: IConstruct): void; + Single = 'Single', + + /** + * The aspect will visit each contruct as manay times as invoked + */ + Multiple = 'Multiple' } /** - * The representation of an Apect + * Represents an Aspect */ -export abstract class Aspect implements IAspect { +export interface IAspect { /** - * The type of the aspect + * Aspects invocation pattern */ - public abstract readonly type: string; - + readonly visitType: AspectVisitType; /** - * This is the function concrete Aspects should implement - * - * The ``visit()`` function will call this method to invoke the customized - * apsect actions. + * All aspects can visit a IConstruct */ - public abstract visit(construct: IConstruct): void; + visit(node: IConstruct): void; } diff --git a/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts b/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts index 9e51246f3b2ce..86b3a54640d94 100644 --- a/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts +++ b/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts @@ -1,6 +1,6 @@ import { ITaggable, Resource } from '../cloudformation/resource'; import { IConstruct } from '../core/construct'; -import { IAspect } from './aspect'; +import { AspectVisitType, IAspect } from './aspect'; export interface TagAspectProps { /** @@ -38,6 +38,7 @@ export abstract class TagBase implements IAspect { */ public readonly key: string; + public readonly visitType: AspectVisitType = AspectVisitType.Single private readonly include: string[]; private readonly exclude: string[]; diff --git a/packages/@aws-cdk/cdk/lib/core/construct.ts b/packages/@aws-cdk/cdk/lib/core/construct.ts index b7e02079e9a3c..352cf7cda5214 100644 --- a/packages/@aws-cdk/cdk/lib/core/construct.ts +++ b/packages/@aws-cdk/cdk/lib/core/construct.ts @@ -1,5 +1,5 @@ import cxapi = require('@aws-cdk/cx-api'); -import { IAspect } from '../aspects/aspect'; +import { AspectVisitType, IAspect } from '../aspects/aspect'; import { CloudFormationJSON } from '../cloudformation/cloudformation-json'; import { makeUniqueId } from '../util/uniqueid'; import { Token, unresolved } from './tokens'; @@ -463,13 +463,19 @@ export class ConstructNode { */ private invokeAspects(): void { const descendants = this.findAll(); - const nonInvoked = this.aspects.filter( - aspect => !this.invokedAspects.includes(aspect)); - nonInvoked.forEach( aspect => { + const aspectsToVisit = this.visitableAspects(); + aspectsToVisit.forEach( aspect => { descendants.forEach( member => aspect.visit(member)) this.invokedAspects.push(aspect); }); } + + private visitableAspects(): IAspect[] { + return this.aspects.filter( aspect => { + return aspect.visitType === AspectVisitType.Multiple || + !this.invokedAspects.includes(aspect) + }); + } /** * Return the path of components up to but excluding the root */ diff --git a/packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts b/packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts index cc7080f8f7b69..3b27b90e867f6 100644 --- a/packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts +++ b/packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts @@ -16,15 +16,6 @@ class MapTaggableResource extends TaggableResource { public readonly tags = new TagManager(TagType.Map); } -// class TestRoot extends Stack { -// constructor() { -// super(undefined, 'TestStack', { env: { account: '123456789012', region: 'us-east-1' } }); -// } -// public testInvokeAspects() { -// this.node.prepareTree(); -// } -// } - export = { 'Tag visit all children of the applied node'(test: Test) { const root = new Stack(); diff --git a/packages/@aws-cdk/cdk/test/test.aspect.ts b/packages/@aws-cdk/cdk/test/test.aspect.ts new file mode 100644 index 0000000000000..f4419373376bf --- /dev/null +++ b/packages/@aws-cdk/cdk/test/test.aspect.ts @@ -0,0 +1,64 @@ +import { Test } from 'nodeunit'; +import { IConstruct, Root } from '../lib/core/construct'; +import { AspectVisitType, IAspect } from '../lib/aspects/aspect'; + +class MyConstruct extends Root { + public static IsMyConstruct(x: any): x is MyConstruct { + return x.visitCounter !== undefined; + } + public visitCounter: number = 0; +} + +class VisitMany implements IAspect { + public readonly visitType = AspectVisitType.Multiple; + + public visit(node: IConstruct): void { + if (MyConstruct.IsMyConstruct(node)) { + node.visitCounter += 1; + } + } +} + +class VisitOnce implements IAspect { + public readonly visitType = AspectVisitType.Single; + + public visit(node: IConstruct): void { + if (MyConstruct.IsMyConstruct(node)) { + node.visitCounter += 1; + } + } +} +export = { + 'Aspects with multiple visit type': { + 'are invoked every time'(test: Test) { + const root = new MyConstruct(); + root.apply(new VisitMany()); + root.node.prepareTree(); + test.deepEqual(root.visitCounter, 1); + root.node.prepareTree(); + test.deepEqual(root.visitCounter, 2); + test.done(); + }, + }, + 'Aspects with single visit type': { + 'are invoked only once'(test: Test) { + const root = new MyConstruct(); + root.apply(new VisitOnce()); + root.node.prepareTree(); + test.deepEqual(root.visitCounter, 1); + root.node.prepareTree(); + test.deepEqual(root.visitCounter, 1); + test.done(); + }, + }, + 'A construct can have both Aspect types'(test: Test) { + const root = new MyConstruct(); + root.apply(new VisitOnce()); + root.apply(new VisitMany()); + root.node.prepareTree(); + test.deepEqual(root.visitCounter, 2); + root.node.prepareTree(); + test.deepEqual(root.visitCounter, 3); + test.done(); + }, +} From 2467a5e1d0e92c533e11e40c3e860dd5f010a762 Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Sat, 19 Jan 2019 13:35:45 -0800 Subject: [PATCH 15/38] reverting TestStack --- .../test/test.pipeline-deploy-stack-action.ts | 4 +- packages/@aws-cdk/assert/lib/expect.ts | 9 -- packages/@aws-cdk/assert/lib/index.ts | 1 - packages/@aws-cdk/assert/lib/test-stack.ts | 7 -- .../test/test.auto-scaling-group.ts | 12 +-- .../test/test.scheduled-action.ts | 13 +-- .../aws-cloudtrail/test/test.cloudtrail.ts | 4 +- .../aws-dynamodb/test/test.dynamodb.ts | 90 +++++++++---------- packages/@aws-cdk/aws-ec2/test/test.vpc.ts | 8 +- .../@aws-cdk/aws-ecs/test/test.ecs-cluster.ts | 4 +- packages/@aws-cdk/aws-kms/test/test.key.ts | 20 ++--- 11 files changed, 71 insertions(+), 101 deletions(-) delete mode 100644 packages/@aws-cdk/assert/lib/test-stack.ts diff --git a/packages/@aws-cdk/app-delivery/test/test.pipeline-deploy-stack-action.ts b/packages/@aws-cdk/app-delivery/test/test.pipeline-deploy-stack-action.ts index 14ba06ce442ee..25f41261ec849 100644 --- a/packages/@aws-cdk/app-delivery/test/test.pipeline-deploy-stack-action.ts +++ b/packages/@aws-cdk/app-delivery/test/test.pipeline-deploy-stack-action.ts @@ -9,7 +9,7 @@ import cxapi = require('@aws-cdk/cx-api'); import fc = require('fast-check'); import nodeunit = require('nodeunit'); -import { countResources, expect, haveResource, isSuperObject, TestStack } from '@aws-cdk/assert'; +import { countResources, expect, haveResource, isSuperObject } from '@aws-cdk/assert'; import { PipelineDeployStackAction } from '../lib/pipeline-deploy-stack-action'; interface SelfUpdatingPipeline { @@ -299,7 +299,7 @@ class FakeAction extends api.Action { } function getTestStack(): cdk.Stack { - return new TestStack(undefined, 'TestStack', { env: { account: '123456789012', region: 'us-east-1' } }); + return new cdk.Stack(undefined, 'TestStack', { env: { account: '123456789012', region: 'us-east-1' } }); } function createSelfUpdatingStack(pipelineStack: cdk.Stack): SelfUpdatingPipeline { diff --git a/packages/@aws-cdk/assert/lib/expect.ts b/packages/@aws-cdk/assert/lib/expect.ts index caf6e60123ee4..a983da0d96cd3 100644 --- a/packages/@aws-cdk/assert/lib/expect.ts +++ b/packages/@aws-cdk/assert/lib/expect.ts @@ -1,7 +1,6 @@ import cdk = require('@aws-cdk/cdk'); import api = require('@aws-cdk/cx-api'); import { StackInspector } from './inspector'; -import { TestStack } from './test-stack'; export function expect(stack: api.SynthesizedStack | cdk.Stack, skipValidation = false): StackInspector { // Can't use 'instanceof' here, that breaks if we have multiple copies @@ -19,10 +18,6 @@ export function expect(stack: api.SynthesizedStack | cdk.Stack, skipValidation = } } - if (isTestStack(stack)) { - stack.testInvokeAspects(); - } - sstack = { name: stack.name, template: stack.toCloudFormation(), @@ -55,7 +50,3 @@ function collectStackMetadata(root: cdk.ConstructNode): api.StackMetadata { } return result; } - -function isTestStack(t: any): t is TestStack { - return t.testInvokeAspects !== undefined; -} diff --git a/packages/@aws-cdk/assert/lib/index.ts b/packages/@aws-cdk/assert/lib/index.ts index 426ce5c9bcf19..e811cd2e0bb0d 100644 --- a/packages/@aws-cdk/assert/lib/index.ts +++ b/packages/@aws-cdk/assert/lib/index.ts @@ -1,7 +1,6 @@ export * from './assertion'; export * from './expect'; export * from './inspector'; -export * from './test-stack'; export * from './assertions/exist'; export * from './assertions/have-resource'; diff --git a/packages/@aws-cdk/assert/lib/test-stack.ts b/packages/@aws-cdk/assert/lib/test-stack.ts deleted file mode 100644 index 3c58062176ab9..0000000000000 --- a/packages/@aws-cdk/assert/lib/test-stack.ts +++ /dev/null @@ -1,7 +0,0 @@ -import cdk = require('@aws-cdk/cdk'); - -export class TestStack extends cdk.Stack { - public testInvokeAspects() { - this.invokeAspects(); - } -} diff --git a/packages/@aws-cdk/aws-autoscaling/test/test.auto-scaling-group.ts b/packages/@aws-cdk/aws-autoscaling/test/test.auto-scaling-group.ts index 710446ae53f88..2fb458f47c391 100644 --- a/packages/@aws-cdk/aws-autoscaling/test/test.auto-scaling-group.ts +++ b/packages/@aws-cdk/aws-autoscaling/test/test.auto-scaling-group.ts @@ -18,8 +18,6 @@ export = { vpc }); - stack.testInvokeAspects(); - expect(stack).toMatch({ "Resources": { "MyFleetInstanceSecurityGroup774E8234": { @@ -385,7 +383,6 @@ export = { asg.apply( new cdk.Tag('superfood', 'acai')); asg.apply( new cdk.Tag('notsuper', 'caramel', { applyToLaunchInstances: false })); - stack.testInvokeAspects(); // THEN expect(stack).to(haveResource("AWS::AutoScaling::AutoScalingGroup", { Tags: [ @@ -426,11 +423,6 @@ function mockSecurityGroup(stack: cdk.Stack) { }); } -class TestStack extends cdk.Stack { - public testInvokeAspects(): void { - this.invokeAspects(); - } -} -function getTestStack(): TestStack { - return new TestStack(undefined, 'TestStack', { env: { account: '1234', region: 'us-east-1' } }); +function getTestStack(): cdk.Stack { + return new cdk.Stack(undefined, 'TestStack', { env: { account: '1234', region: 'us-east-1' } }); } diff --git a/packages/@aws-cdk/aws-autoscaling/test/test.scheduled-action.ts b/packages/@aws-cdk/aws-autoscaling/test/test.scheduled-action.ts index afe70277416ac..48615e9e84068 100644 --- a/packages/@aws-cdk/aws-autoscaling/test/test.scheduled-action.ts +++ b/packages/@aws-cdk/aws-autoscaling/test/test.scheduled-action.ts @@ -1,4 +1,4 @@ -import { expect, haveResource, MatchStyle, TestStack } from '@aws-cdk/assert'; +import { expect, haveResource, MatchStyle, } from '@aws-cdk/assert'; import ec2 = require('@aws-cdk/aws-ec2'); import cdk = require('@aws-cdk/cdk'); import { Test } from 'nodeunit'; @@ -7,7 +7,7 @@ import autoscaling = require('../lib'); export = { 'can schedule an action'(test: Test) { // GIVEN - const stack = new TestStack(); + const stack = new cdk.Stack(); const asg = makeAutoScalingGroup(stack); // WHEN @@ -27,7 +27,7 @@ export = { 'correctly formats date objects'(test: Test) { // GIVEN - const stack = new TestStack(); + const stack = new cdk.Stack(); const asg = makeAutoScalingGroup(stack); // WHEN @@ -47,7 +47,7 @@ export = { 'autoscaling group has recommended updatepolicy for scheduled actions'(test: Test) { // GIVEN - const stack = getTestStack(); + const stack = new cdk.Stack(); const asg = makeAutoScalingGroup(stack); // WHEN @@ -56,7 +56,6 @@ export = { minCapacity: 10, }); - stack.testInvokeAspects(); // THEN expect(stack).toMatch({ Resources: { @@ -113,7 +112,3 @@ function makeAutoScalingGroup(scope: cdk.Construct) { updateType: autoscaling.UpdateType.RollingUpdate, }); } - -function getTestStack(): TestStack { - return new TestStack(undefined, 'TestStack', { env: { account: '1234', region: 'us-east-1' } }); -} diff --git a/packages/@aws-cdk/aws-cloudtrail/test/test.cloudtrail.ts b/packages/@aws-cdk/aws-cloudtrail/test/test.cloudtrail.ts index 7a6212234e2aa..ae6066ec43ff4 100644 --- a/packages/@aws-cdk/aws-cloudtrail/test/test.cloudtrail.ts +++ b/packages/@aws-cdk/aws-cloudtrail/test/test.cloudtrail.ts @@ -1,4 +1,4 @@ -import { expect, haveResource, not, TestStack } from '@aws-cdk/assert'; +import { expect, haveResource, not } from '@aws-cdk/assert'; import { Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import { CloudTrail, LogRetention, ReadWriteType } from '../lib'; @@ -147,5 +147,5 @@ export = { }; function getTestStack(): Stack { - return new TestStack(undefined, 'TestStack', { env: { account: '123456789012', region: 'us-east-1' } }); + return new Stack(undefined, 'TestStack', { env: { account: '123456789012', region: 'us-east-1' } }); } diff --git a/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts b/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts index 8e96f6e7230eb..675c92439200b 100644 --- a/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts +++ b/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts @@ -1,6 +1,6 @@ -import { countResources, expect, haveResource, TestStack } from '@aws-cdk/assert'; +import { countResources, expect, haveResource } from '@aws-cdk/assert'; import iam = require('@aws-cdk/aws-iam'); -import { Tag } from '@aws-cdk/cdk'; +import { Stack, Tag } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import { Attribute, @@ -66,14 +66,14 @@ function* LSI_GENERATOR() { export = { 'default properties': { 'fails without a hash key'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); new Table(stack, CONSTRUCT_NAME); test.throws(() => expect(stack).to(countResources('AWS::DynamoDB::Table')), /partition key/); test.done(); }, 'hash key only'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); new Table(stack, CONSTRUCT_NAME).addPartitionKey(TABLE_PARTITION_KEY); expect(stack).to(haveResource('AWS::DynamoDB::Table', { @@ -85,7 +85,7 @@ export = { }, 'hash + range key'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY); @@ -104,7 +104,7 @@ export = { }, 'hash + range key can also be specified in props'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); new Table(stack, CONSTRUCT_NAME, { partitionKey: TABLE_PARTITION_KEY, @@ -128,7 +128,7 @@ export = { }, 'point-in-time recovery is not enabled'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY); @@ -150,7 +150,7 @@ export = { }, 'server-side encryption is not enabled'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY); @@ -172,7 +172,7 @@ export = { }, 'stream is not enabled'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY); @@ -194,7 +194,7 @@ export = { }, 'ttl is not enabled'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY); @@ -216,7 +216,7 @@ export = { }, 'can specify new and old images'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); const table = new Table(stack, CONSTRUCT_NAME, { tableName: TABLE_NAME, readCapacity: 42, @@ -245,7 +245,7 @@ export = { }, 'can specify new images only'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); const table = new Table(stack, CONSTRUCT_NAME, { tableName: TABLE_NAME, readCapacity: 42, @@ -274,7 +274,7 @@ export = { }, 'can specify old images only'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); const table = new Table(stack, CONSTRUCT_NAME, { tableName: TABLE_NAME, readCapacity: 42, @@ -304,7 +304,7 @@ export = { }, 'when specifying every property'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); const table = new Table(stack, CONSTRUCT_NAME, { tableName: TABLE_NAME, readCapacity: 42, @@ -345,7 +345,7 @@ export = { }, 'when specifying PAY_PER_REQUEST billing mode'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); new Table(stack, CONSTRUCT_NAME, { tableName: TABLE_NAME, billingMode: BillingMode.PayPerRequest, @@ -368,7 +368,7 @@ export = { }, 'error when specifying read or write capacity with a PAY_PER_REQUEST billing mode'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); test.throws(() => new Table(stack, CONSTRUCT_NAME, { tableName: TABLE_NAME, billingMode: BillingMode.PayPerRequest, @@ -392,7 +392,7 @@ export = { }, 'when adding a global secondary index with hash key only'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY) @@ -431,7 +431,7 @@ export = { }, 'when adding a global secondary index with hash + range key'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY) @@ -474,7 +474,7 @@ export = { }, 'when adding a global secondary index with projection type KEYS_ONLY'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY) @@ -515,7 +515,7 @@ export = { }, 'when adding a global secondary index with projection type INCLUDE'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); const table = new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY); @@ -560,7 +560,7 @@ export = { }, 'when adding a global secondary index on a table with PAY_PER_REQUEST billing mode'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); new Table(stack, CONSTRUCT_NAME, { billingMode: BillingMode.PayPerRequest, partitionKey: TABLE_PARTITION_KEY, @@ -597,7 +597,7 @@ export = { }, 'error when adding a global secondary index with projection type INCLUDE, but without specifying non-key attributes'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); const table = new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY); @@ -613,7 +613,7 @@ export = { }, 'error when adding a global secondary index with projection type ALL, but with non-key attributes'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); const table = new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY); @@ -629,7 +629,7 @@ export = { }, 'error when adding a global secondary index with projection type KEYS_ONLY, but with non-key attributes'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); const table = new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY); @@ -646,7 +646,7 @@ export = { }, 'error when adding a global secondary index with projection type INCLUDE, but with more than 20 non-key attributes'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); const table = new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY); @@ -668,7 +668,7 @@ export = { }, 'error when adding a global secondary index with projection type INCLUDE, but with key attributes'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); const table = new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY); @@ -686,7 +686,7 @@ export = { }, 'error when adding a global secondary index with read or write capacity on a PAY_PER_REQUEST table'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); const table = new Table(stack, CONSTRUCT_NAME, { partitionKey: TABLE_PARTITION_KEY, billingMode: BillingMode.PayPerRequest @@ -716,7 +716,7 @@ export = { }, 'when adding multiple global secondary indexes'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); const table = new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY); @@ -789,7 +789,7 @@ export = { }, 'error when adding more than 5 global secondary indexes'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); const table = new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY); @@ -805,7 +805,7 @@ export = { }, 'when adding a global secondary index without specifying read and write capacity'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY) @@ -842,7 +842,7 @@ export = { }, 'when adding a local secondary index with hash + range key'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY) @@ -879,7 +879,7 @@ export = { }, 'when adding a local secondary index with projection type KEYS_ONLY'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY) @@ -917,7 +917,7 @@ export = { }, 'when adding a local secondary index with projection type INCLUDE'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); const table = new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY); @@ -957,7 +957,7 @@ export = { }, 'error when adding more than 5 local secondary indexes'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); const table = new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY); @@ -973,7 +973,7 @@ export = { }, 'error when adding a local secondary index before specifying a partition key of the table'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); const table = new Table(stack, CONSTRUCT_NAME) .addSortKey(TABLE_SORT_KEY); @@ -986,7 +986,7 @@ export = { }, 'error when adding a local secondary index with the name of a global secondary index'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); const table = new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY) .addSortKey(TABLE_SORT_KEY); @@ -1004,7 +1004,7 @@ export = { }, 'error when validating construct if a local secondary index exists without a sort key of the table'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); const table = new Table(stack, CONSTRUCT_NAME) .addPartitionKey(TABLE_PARTITION_KEY); table.addLocalSecondaryIndex({ @@ -1022,7 +1022,7 @@ export = { 'can enable Read AutoScaling'(test: Test) { // GIVEN - const stack = new TestStack(); + const stack = new Stack(); const table = new Table(stack, CONSTRUCT_NAME, { readCapacity: 42, writeCapacity: 1337 }); table.addPartitionKey(TABLE_PARTITION_KEY); @@ -1049,7 +1049,7 @@ export = { 'can enable Write AutoScaling'(test: Test) { // GIVEN - const stack = new TestStack(); + const stack = new Stack(); const table = new Table(stack, CONSTRUCT_NAME, { readCapacity: 42, writeCapacity: 1337 }); table.addPartitionKey(TABLE_PARTITION_KEY); @@ -1076,7 +1076,7 @@ export = { 'cannot enable AutoScaling twice on the same property'(test: Test) { // GIVEN - const stack = new TestStack(); + const stack = new Stack(); const table = new Table(stack, CONSTRUCT_NAME, { readCapacity: 42, writeCapacity: 1337 }); table.addPartitionKey(TABLE_PARTITION_KEY); table.autoScaleReadCapacity({ minCapacity: 50, maxCapacity: 500 }).scaleOnUtilization({ targetUtilizationPercent: 75 }); @@ -1091,7 +1091,7 @@ export = { 'error when enabling AutoScaling on the PAY_PER_REQUEST table'(test: Test) { // GIVEN - const stack = new TestStack(); + const stack = new Stack(); const table = new Table(stack, CONSTRUCT_NAME, { billingMode: BillingMode.PayPerRequest }); table.addPartitionKey(TABLE_PARTITION_KEY); table.addGlobalSecondaryIndex({ @@ -1116,7 +1116,7 @@ export = { 'error when specifying Read Auto Scaling with invalid scalingTargetValue < 10'(test: Test) { // GIVEN - const stack = new TestStack(); + const stack = new Stack(); const table = new Table(stack, CONSTRUCT_NAME, { readCapacity: 42, writeCapacity: 1337 }); // THEN @@ -1129,7 +1129,7 @@ export = { 'error when specifying Read Auto Scaling with invalid minimumCapacity'(test: Test) { // GIVEN - const stack = new TestStack(); + const stack = new Stack(); const table = new Table(stack, CONSTRUCT_NAME, { readCapacity: 42, writeCapacity: 1337 }); // THEN @@ -1140,7 +1140,7 @@ export = { 'can autoscale on a schedule'(test: Test) { // GIVEN - const stack = new TestStack(); + const stack = new Stack(); const table = new Table(stack, CONSTRUCT_NAME, { readCapacity: 42, writeCapacity: 1337 }); table.addPartitionKey({ name: 'Hash', type: AttributeType.String }); @@ -1196,7 +1196,7 @@ export = { function testGrant(test: Test, expectedActions: string[], invocation: (user: iam.IPrincipal, table: Table) => void) { // GIVEN - const stack = new TestStack(); + const stack = new Stack(); const table = new Table(stack, 'my-table'); table.addPartitionKey({ name: 'ID', type: AttributeType.String }); diff --git a/packages/@aws-cdk/aws-ec2/test/test.vpc.ts b/packages/@aws-cdk/aws-ec2/test/test.vpc.ts index 5af8408d46fce..d5d57dd68161b 100644 --- a/packages/@aws-cdk/aws-ec2/test/test.vpc.ts +++ b/packages/@aws-cdk/aws-ec2/test/test.vpc.ts @@ -1,5 +1,5 @@ -import { countResources, expect, haveResource, haveResourceLike, isSuperObject, TestStack } from '@aws-cdk/assert'; -import { AvailabilityZoneProvider, Construct, Tag } from '@aws-cdk/cdk'; +import { countResources, expect, haveResource, haveResourceLike, isSuperObject } from '@aws-cdk/assert'; +import { AvailabilityZoneProvider, Construct, Stack, Tag } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import { CfnVPC, DefaultInstanceTenancy, IVpcNetwork, SubnetType, VpcNetwork } from '../lib'; @@ -530,8 +530,8 @@ export = { }, }; -function getTestStack(): TestStack { - return new TestStack(undefined, 'TestStack', { env: { account: '123456789012', region: 'us-east-1' } }); +function getTestStack(): Stack { + return new Stack(undefined, 'TestStack', { env: { account: '123456789012', region: 'us-east-1' } }); } /** diff --git a/packages/@aws-cdk/aws-ecs/test/test.ecs-cluster.ts b/packages/@aws-cdk/aws-ecs/test/test.ecs-cluster.ts index ca3181e8665fa..8748b7cadbc87 100644 --- a/packages/@aws-cdk/aws-ecs/test/test.ecs-cluster.ts +++ b/packages/@aws-cdk/aws-ecs/test/test.ecs-cluster.ts @@ -1,4 +1,4 @@ -import { expect, haveResource, TestStack } from '@aws-cdk/assert'; +import { expect, haveResource } from '@aws-cdk/assert'; import ec2 = require('@aws-cdk/aws-ec2'); import { InstanceType } from '@aws-cdk/aws-ec2'; import cdk = require('@aws-cdk/cdk'); @@ -9,7 +9,7 @@ export = { "When creating an ECS Cluster": { "with only required properties set, it correctly sets default properties"(test: Test) { // GIVEN - const stack = new TestStack(); + const stack = new cdk.Stack(); const vpc = new ec2.VpcNetwork(stack, 'MyVpc', {}); const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc, diff --git a/packages/@aws-cdk/aws-kms/test/test.key.ts b/packages/@aws-cdk/aws-kms/test/test.key.ts index 027b4dcb5e057..dd51d1d7bffe7 100644 --- a/packages/@aws-cdk/aws-kms/test/test.key.ts +++ b/packages/@aws-cdk/aws-kms/test/test.key.ts @@ -1,12 +1,12 @@ -import { exactlyMatchTemplate, expect, TestStack } from '@aws-cdk/assert'; +import { exactlyMatchTemplate, expect } from '@aws-cdk/assert'; import { PolicyDocument, PolicyStatement } from '@aws-cdk/aws-iam'; -import { App, Tag } from '@aws-cdk/cdk'; +import { App, Stack, Tag } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import { EncryptionKey } from '../lib'; export = { 'default key'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); new EncryptionKey(stack, 'MyKey'); @@ -66,7 +66,7 @@ export = { 'default with some permission'(test: Test) { const app = new App(); - const stack = new TestStack(app, 'Test'); + const stack = new Stack(app, 'Test'); const key = new EncryptionKey(stack, 'MyKey'); const p = new PolicyStatement().addAllResources().addAction('kms:encrypt'); @@ -137,7 +137,7 @@ export = { }, 'key with some options'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); const key = new EncryptionKey(stack, 'MyKey', { enableKeyRotation: true, @@ -232,7 +232,7 @@ export = { 'addAlias creates an alias'(test: Test) { const app = new App(); - const stack = new TestStack(app, 'Test'); + const stack = new Stack(app, 'Test'); const key = new EncryptionKey(stack, 'MyKey', { enableKeyRotation: true, @@ -312,7 +312,7 @@ export = { }, 'import/export can be used to bring in an existing key'(test: Test) { - const stack1 = new TestStack(); + const stack1 = new Stack(); const policy = new PolicyDocument(); policy.addStatement(new PolicyStatement().addAllResources()); const myKey = new EncryptionKey(stack1, 'MyKey', { policy }); @@ -351,7 +351,7 @@ export = { } }); - const stack2 = new TestStack(); + const stack2 = new Stack(); const myKeyImported = EncryptionKey.import(stack2, 'MyKeyImported', exportedKeyRef); // addAlias can be called on imported keys. @@ -376,7 +376,7 @@ export = { 'addToResourcePolicy allowNoOp and there is no policy': { 'succeed if set to true (default)'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); const key = EncryptionKey.import(stack, 'Imported', { keyArn: 'foo/bar' }); @@ -387,7 +387,7 @@ export = { 'fails if set to false'(test: Test) { - const stack = new TestStack(); + const stack = new Stack(); const key = EncryptionKey.import(stack, 'Imported', { keyArn: 'foo/bar' }); From fdae887d6a8115bdb3ca74fa37ab7eb179ab0594 Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Sat, 19 Jan 2019 13:42:04 -0800 Subject: [PATCH 16/38] linter fixes --- packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts | 2 +- packages/@aws-cdk/cdk/lib/core/construct.ts | 4 ++-- packages/@aws-cdk/cdk/test/test.aspect.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts b/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts index 86b3a54640d94..2594fc49e734b 100644 --- a/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts +++ b/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts @@ -38,7 +38,7 @@ export abstract class TagBase implements IAspect { */ public readonly key: string; - public readonly visitType: AspectVisitType = AspectVisitType.Single + public readonly visitType: AspectVisitType = AspectVisitType.Single; private readonly include: string[]; private readonly exclude: string[]; diff --git a/packages/@aws-cdk/cdk/lib/core/construct.ts b/packages/@aws-cdk/cdk/lib/core/construct.ts index 352cf7cda5214..a26bdb684142a 100644 --- a/packages/@aws-cdk/cdk/lib/core/construct.ts +++ b/packages/@aws-cdk/cdk/lib/core/construct.ts @@ -465,7 +465,7 @@ export class ConstructNode { const descendants = this.findAll(); const aspectsToVisit = this.visitableAspects(); aspectsToVisit.forEach( aspect => { - descendants.forEach( member => aspect.visit(member)) + descendants.forEach( member => aspect.visit(member)); this.invokedAspects.push(aspect); }); } @@ -473,7 +473,7 @@ export class ConstructNode { private visitableAspects(): IAspect[] { return this.aspects.filter( aspect => { return aspect.visitType === AspectVisitType.Multiple || - !this.invokedAspects.includes(aspect) + !this.invokedAspects.includes(aspect); }); } /** diff --git a/packages/@aws-cdk/cdk/test/test.aspect.ts b/packages/@aws-cdk/cdk/test/test.aspect.ts index f4419373376bf..92ab6abbe3712 100644 --- a/packages/@aws-cdk/cdk/test/test.aspect.ts +++ b/packages/@aws-cdk/cdk/test/test.aspect.ts @@ -1,6 +1,6 @@ import { Test } from 'nodeunit'; -import { IConstruct, Root } from '../lib/core/construct'; import { AspectVisitType, IAspect } from '../lib/aspects/aspect'; +import { IConstruct, Root } from '../lib/core/construct'; class MyConstruct extends Root { public static IsMyConstruct(x: any): x is MyConstruct { @@ -61,4 +61,4 @@ export = { test.deepEqual(root.visitCounter, 3); test.done(); }, -} +}; From 7409e5f47655b17bb2e9ed7bc0a17ce30482bca1 Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Sun, 20 Jan 2019 21:01:48 -0800 Subject: [PATCH 17/38] adding an aspect readme --- packages/@aws-cdk/cdk/lib/aspects/README.md | 292 ++++++++++++++++++++ 1 file changed, 292 insertions(+) create mode 100644 packages/@aws-cdk/cdk/lib/aspects/README.md diff --git a/packages/@aws-cdk/cdk/lib/aspects/README.md b/packages/@aws-cdk/cdk/lib/aspects/README.md new file mode 100644 index 0000000000000..28d576bc1977f --- /dev/null +++ b/packages/@aws-cdk/cdk/lib/aspects/README.md @@ -0,0 +1,292 @@ +## Aspects + +Aspects are a mechanism to extend the CDK without having to direclty impact the +class heirarchy directly. This is a form of [Aspect-oriented +programming](https://en.wikipedia.org/wiki/Aspect-oriented_programming) which we +have implemented using the [Visitor +Pattern](https://en.wikipedia.org/wiki/Aspect-oriented_programming). + +### Aspects in the CDK + +An aspect in the CDK is defined by the interface: + +```ts +/** + * Represents an Aspect + */ +export interface IAspect { + /** + * Aspects invocation pattern + */ + readonly visitType: AspectVisitType; + /** + * All aspects can visit a IConstruct + */ + visit(node: IConstruct): void; +} +``` + +Initially two types of aspects are supported. + +1. Single visit aspects will ensure the aspect visit function is only invoked + one time. +1. Multiple visit aspects will invoke the visit function as many times as + requested + +Aspects can be applied to any `Construct`. During the tree +preparation phase the aspect will visit each construct in the tree at least one +time. Aspects are invoked in the order they were added to the `Construct`, +starting at the `App` (root of the tree) and progressing in order to the leaf +nodes (most commonly the CloudFormation Resource). Aspect authors will implement +the `visit(IConstruct)` function can inspect the `Construct` for specific characteristics. +Such as, is this construct a CloudFormation Resource? + +### Tag and Remove Tag Aspect + +Tag support is the first use of Aspects. The goal was to enable the ability to +define tags in one place and them have applied consistently for all resources +that support tagging. In addition the developer should not have to know if the +resource supports tags. The developer defines the tagging intents for all +resources within a path. If the resources support tags they are added, else no +action is taken. + +In order to enable additional controls a Tag Aspect can specifically include or +exclude a CloudFormation Resource Type. See the TagAspectProps interface for +more details. + +#### Tag Example with ECS + +We are going to use the ECS example as starting point. + +For the purposes of example, this ECS cluster is for the Marketing Department. +Marketing has two core groups Business to Business (B2B) and Business to Consumer +(B2C). However, the Marketing team relies on the Platform team to help build the +common components across businesses and separates costs to match. The goal here +is tag the Platform team resources, the Marketing Department and then Marketing +groups to enable proper cost allocations. + + +```ts +import cdk = require('@aws-cdk/cdk'); +import ec2 = require('@aws-cdk/aws-ec2'); +import ecs = require('@aws-cdk/aws-ecs'); + +const app = new cdk.App(); +const marketingStack = new cdk.Stack(app, 'MarketingSystem'); + +const vpc = new ec2.VpcNetwork(marketingStack, 'MarketingVpc', { + maxAZs: 3 // Default is all AZs in region +}); + +const cluster = new ecs.Cluster(marketingStack, 'MarketingCluster', { + vpc: vpc +}); + +// Create a load-balanced Fargate service and make it public +new ecs.LoadBalancedFargateService(marketingStack, 'B2BService', { + cluster: cluster, // Required + cpu: '512', // Default is 256 + desiredCount: 6, // Default is 1 + image: ecs.ContainerImage.fromDockerHub('amazon/amazon-ecs-sample'), // Required + memoryMiB: '2048', // Default is 512 + publicLoadBalancer: true // Default is false +}); + +// Create a load-balanced Fargate service and make it public +new ecs.LoadBalancedFargateService(marketingStack, 'B2CService', { + cluster: cluster, // Required + cpu: '512', // Default is 256 + desiredCount: 6, // Default is 1 + image: ecs.ContainerImage.fromDockerHub('amazon/amazon-ecs-sample'), // Required + memoryMiB: '2048', // Default is 512 + publicLoadBalancer: true // Default is false +}); +``` + + +```ts +import cdk = require('@aws-cdk/cdk'); +import ec2 = require('@aws-cdk/aws-ec2'); +import ecs = require('@aws-cdk/aws-ecs'); + +const COST_CENTER_KEY = 'CostCenter'; + +const app = new cdk.App(); +// every resource starts with Marketing +app.apply(new cdk.Tag(COST_CENTER_KEY, 'Marketing')); + +const marketingStack = new cdk.Stack(app, 'MarketingSystem'); + +const vpc = new ec2.VpcNetwork(marketingStack, 'MarketingVpc', { + maxAZs: 3 // Default is all AZs in region +}); +// override the VPC tags with Platform +// this will tag the VPC, Subnets, Route Tables, IGW, and NatGWs +vpc.apply(new cdk.Tag(COST_CENTER_KEY, 'Platform')); + +const cluster = new ecs.Cluster(marketingStack, 'MarketingCluster', { + vpc: vpc +}); + +// Create a load-balanced Fargate service and make it public +const b2b = new ecs.LoadBalancedFargateService(marketingStack, 'B2BService', { + cluster: cluster, // Required + cpu: '512', // Default is 256 + desiredCount: 6, // Default is 1 + image: ecs.ContainerImage.fromDockerHub('amazon/amazon-ecs-sample'), // Required + memoryMiB: '2048', // Default is 512 + publicLoadBalancer: true // Default is false +}); + +// Create a load-balanced Fargate service and make it public +new ecs.LoadBalancedFargateService(marketingStack, 'B2CService', { + cluster: cluster, // Required + cpu: '512', // Default is 256 + desiredCount: 6, // Default is 1 + image: ecs.ContainerImage.fromDockerHub('amazon/amazon-ecs-sample'), // Required + memoryMiB: '2048', // Default is 512 + publicLoadBalancer: true // Default is false +}); +``` + +The resulting tags are as follows: + +| Construct Path | Tag Key | Tag Value | +| ----------|:---------|:-----| +|MarketingSystem/MarketingVpc|CostCenter|Platform| +|MarketingSystem/MarketingVpc|Name|MarketingSystem/MarketingVpc| +|MarketingSystem/MarketingVpc/PublicSubnet1| Name | Platform|MarketingSystem/MarketingVpc/PublicSubnet1| +|MarketingSystem/MarketingVpc/PublicSubnet1| aws-cdk:subnet-name | Public| +|MarketingSystem/MarketingVpc/PublicSubnet1| aws-cdk:subnet-type | Public| +|MarketingSystem/MarketingVpc/PublicSubnet1| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PublicSubnet1/RouteTable| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PublicSubnet1/RouteTable|Name| MarketingSystem/MarketingVpc/PublicSubnet1| +|MarketingSystem/MarketingVpc/PublicSubnet1/NATGateway| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PublicSubnet1/NATGateway|Name|MarketingSystem/MarketingVpc/PublicSubnet1| +|MarketingSystem/MarketingVpc/PublicSubnet2| Name | Platform|MarketingSystem/MarketingVpc/PublicSubnet2| +|MarketingSystem/MarketingVpc/PublicSubnet2| aws-cdk:subnet-name | Public| +|MarketingSystem/MarketingVpc/PublicSubnet2| aws-cdk:subnet-type | Public| +|MarketingSystem/MarketingVpc/PublicSubnet2| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PublicSubnet2/RouteTable| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PublicSubnet2/RouteTable|Name| MarketingSystem/MarketingVpc/PublicSubnet2| +|MarketingSystem/MarketingVpc/PublicSubnet2/NATGateway| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PublicSubnet2/NATGateway|Name|MarketingSystem/MarketingVpc/PublicSubnet2| +|MarketingSystem/MarketingVpc/PublicSubnet3| Name | Platform|MarketingSystem/MarketingVpc/PublicSubnet3| +|MarketingSystem/MarketingVpc/PublicSubnet3| aws-cdk:subnet-name | Public| +|MarketingSystem/MarketingVpc/PublicSubnet3| aws-cdk:subnet-type | Public| +|MarketingSystem/MarketingVpc/PublicSubnet3| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PublicSubnet3/RouteTable| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PublicSubnet3/RouteTable|Name| MarketingSystem/MarketingVpc/PublicSubnet3| +|MarketingSystem/MarketingVpc/PublicSubnet3/NATGateway| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PublicSubnet3/NATGateway|Name|MarketingSystem/MarketingVpc/PublicSubnet3| +|MarketingSystem/MarketingVpc/PrivateSubnet1| Name | Platform|MarketingSystem/MarketingVpc/PrivateSubnet1| +|MarketingSystem/MarketingVpc/PrivateSubnet1| aws-cdk:subnet-name | Private| +|MarketingSystem/MarketingVpc/PrivateSubnet1| aws-cdk:subnet-type | Private| +|MarketingSystem/MarketingVpc/PrivateSubnet1| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PrivateSubnet1/RouteTable| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PrivateSubnet1/RouteTable|Name| MarketingSystem/MarketingVpc/PrivateSubnet1| +|MarketingSystem/MarketingVpc/PrivateSubnet2| Name | Platform|MarketingSystem/MarketingVpc/PrivateSubnet2| +|MarketingSystem/MarketingVpc/PrivateSubnet2| aws-cdk:subnet-name | Private| +|MarketingSystem/MarketingVpc/PrivateSubnet2| aws-cdk:subnet-type | Private| +|MarketingSystem/MarketingVpc/PrivateSubnet2| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PrivateSubnet2/RouteTable| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PrivateSubnet2/RouteTable|Name| MarketingSystem/MarketingVpc/PrivateSubnet2| +|MarketingSystem/MarketingVpc/PrivateSubnet3| Name | Platform|MarketingSystem/MarketingVpc/PrivateSubnet3| +|MarketingSystem/MarketingVpc/PrivateSubnet3| aws-cdk:subnet-name | Private| +|MarketingSystem/MarketingVpc/PrivateSubnet3| aws-cdk:subnet-type | Private| +|MarketingSystem/MarketingVpc/PrivateSubnet3| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PrivateSubnet3/RouteTable| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PrivateSubnet3/RouteTable|Name| MarketingSystem/MarketingVpc/PrivateSubnet3| +|MarketingSystem/MarketingVpc/IGW|CostCenter|Platform| +|MarketingSystem/MarketingVpc/IGW|Name|MarketingSystem/MarketingVpc| +|MarketingSystem/B2BService/Service/SecurityGroup/Resource|CostCenter|Marketing| +|MarketingSystem/B2BService/LB/Resource|CostCenter|Marketing| +|MarketingSystem/B2BService/LB/SecurityGroup/Resource|CostCenter|Marketing| +|MarketingSystem/B2BService/LB/PublicListener/ECSGroup/Resource|CostCenter|Marketing| +|MarketingSystem/B2CService/Service/SecurityGroup/Resource|CostCenter|Marketing| +|MarketingSystem/B2CService/LB/Resource|CostCenter|Marketing| +|MarketingSystem/B2CService/LB/SecurityGroup/Resource|CostCenter|Marketing| +|MarketingSystem/B2CService/LB/PublicListener/ECSGroup/Resource|CostCenter|Marketing| + +As you can see many tags are generated with only a few intent based directives. The CDK does default some additional tags for suggested `Name` keys. If you want to remove those tags you can do so by using the `RemoveTag` aspect, see below: + +```ts +// snip // +const vpc = new ec2.VpcNetwork(marketingStack, 'MarketingVpc', { + maxAZs: 3 // Default is all AZs in region + }); +// override the VPC tags with Platform +// this will tag the VPC, Subnets, Route Tables, IGW, and NatGWs +vpc.apply(new cdk.Tag(COST_CENTER_KEY, 'Platform')); +vpc.apply(new cdk.RemoveTag('Name')); +// snip // +``` + +If you run the above modification you will notice that the Subnets, Route +Tables, and NAT Gateways will still have `Name` tags. Why is that? + +The framework applies aspects starting with the root node. In our example, this +would be the `cdk.App`. For the given node, the aspects are applied in the order +the same order they were added to the `Construct`. We have the following +simplified tree: + +``` + +------+ + | App | + | | + +---+--+ + | +------+ + | |VPC | + +----> | + +---+--+ + | + | +-------+ + | |Subnet | + +---> (6 Instances) + +-------+ +``` + +The nodes have the following aspects for just the Name tags: + +| Node | Aspects | Description | +| ------------- |:-------------:|:-------------| +| VPC | Tag(Name: `VPC Path`) | This is defaulted by the CDK| +| VPC | Remove(Name) | This is defaulted by the CDK| +| Subnet | Tag(Name: `Subnet Path`) | This is defaulted by the CDK on all 6 Subnets| + +The Subnets will see the following order of tag operations: + +1. Subnet set Tag (Name: `VPC Path`) +1. Subnet remove Tag (Name) +1. Subnet set Tag (Name: `Subnet Path`) + +> Note that route tables and NAT Gateways are also children of the subnet and +> receive the pattern as described above. + +If you really want to remove the name tag from subnets you will need to iterate +through subnets from the `vpc` object and apply the Remove Tag aspect. + +#### Tag Options + +Tag Aspects support three options: include, exclude and applyToLaunchInstances. + +##### applyToLaunchInstances + +This property is a boolean that defaults to `true`. When `true` and the aspect +visits an AutoScalingGroup resource the `PropagateAtLaunch` property is set to +true. If false the property is set accordingly. + +##### include + +Include is an array property that contains strings of CloudFormation Resource +Types. As the aspect visits nodes it only takes action if node is one of the +resource types in the array. By default the array is empty and an empty array is +interpreted as apply to any resource type. + +##### exclude + +Exclude is the inverse of include. Exclude is also an array of CloudFormation +Resource Types. As the aspect visit nodes it will not take action if the node is +one of the resource types in the array. By default the array is empty and an +empty array is interpreted to match no resource type. Exclude takes precedence +over include in the event of a collision. From 4ffbd550e2ed9849d1956df7c1cb41a4a0af3907 Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Sun, 27 Jan 2019 10:55:41 -0800 Subject: [PATCH 18/38] refactoring to add tag priorities and minor readme updates, plus name changes from code review --- .../aws-autoscaling/lib/auto-scaling-group.ts | 4 +- .../test/test.auto-scaling-group.ts | 2 +- packages/@aws-cdk/aws-ec2/lib/vpc.ts | 6 +-- packages/@aws-cdk/aws-ec2/test/test.vpc.ts | 2 +- packages/@aws-cdk/cdk/lib/aspects/README.md | 39 +++------------- .../@aws-cdk/cdk/lib/aspects/tag-aspect.ts | 43 ++++++++++-------- packages/@aws-cdk/cdk/lib/core/tag-manager.ts | 44 ++++++++++++++++--- .../cdk/test/aspects/test.tag-aspect.ts | 34 +++++++++++++- .../cdk/test/core/test.tag-manager.ts | 18 +++++++- 9 files changed, 123 insertions(+), 69 deletions(-) diff --git a/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts b/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts index 3c040d65e633a..38098b87d2f85 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts @@ -199,9 +199,7 @@ export class AutoScalingGroup extends cdk.Construct implements IAutoScalingGroup }); this.connections = new ec2.Connections({ securityGroups: [this.securityGroup] }); this.securityGroups.push(this.securityGroup); - this.apply(new cdk.Tag(NAME_TAG, this.node.path, { - applyToLaunchInstances: true, - })); + this.apply(new cdk.Tag(NAME_TAG, this.node.path)); this.role = new iam.Role(this, 'InstanceRole', { assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com') diff --git a/packages/@aws-cdk/aws-autoscaling/test/test.auto-scaling-group.ts b/packages/@aws-cdk/aws-autoscaling/test/test.auto-scaling-group.ts index 2fb458f47c391..74e7bf8576414 100644 --- a/packages/@aws-cdk/aws-autoscaling/test/test.auto-scaling-group.ts +++ b/packages/@aws-cdk/aws-autoscaling/test/test.auto-scaling-group.ts @@ -381,7 +381,7 @@ export = { }, }); asg.apply( new cdk.Tag('superfood', 'acai')); - asg.apply( new cdk.Tag('notsuper', 'caramel', { applyToLaunchInstances: false })); + asg.apply( new cdk.Tag('notsuper', 'caramel', { applyToLaunchedInstances: false })); // THEN expect(stack).to(haveResource("AWS::AutoScaling::AutoScalingGroup", { diff --git a/packages/@aws-cdk/aws-ec2/lib/vpc.ts b/packages/@aws-cdk/aws-ec2/lib/vpc.ts index cccde9be67550..9444b272ac660 100644 --- a/packages/@aws-cdk/aws-ec2/lib/vpc.ts +++ b/packages/@aws-cdk/aws-ec2/lib/vpc.ts @@ -450,9 +450,9 @@ export class VpcNetwork extends VpcNetworkBase { } // These values will be used to recover the config upon provider import - const include = [CfnSubnet.resourceTypeName]; - subnet.apply(new cdk.Tag(SUBNETNAME_TAG, subnetConfig.name, {include})); - subnet.apply(new cdk.Tag(SUBNETTYPE_TAG, subnetTypeTagValue(subnetConfig.subnetType), {include})); + const includeResourceTypes = [CfnSubnet.resourceTypeName]; + subnet.apply(new cdk.Tag(SUBNETNAME_TAG, subnetConfig.name, {includeResourceTypes})); + subnet.apply(new cdk.Tag(SUBNETTYPE_TAG, subnetTypeTagValue(subnetConfig.subnetType), {includeResourceTypes})); }); } } diff --git a/packages/@aws-cdk/aws-ec2/test/test.vpc.ts b/packages/@aws-cdk/aws-ec2/test/test.vpc.ts index d5d57dd68161b..694eefe0a0573 100644 --- a/packages/@aws-cdk/aws-ec2/test/test.vpc.ts +++ b/packages/@aws-cdk/aws-ec2/test/test.vpc.ts @@ -349,7 +349,7 @@ export = { const vpc = new VpcNetwork(stack, 'TheVPC'); // overwrite to set propagate - vpc.apply(new Tag('BusinessUnit', 'Marketing', {include: [CfnVPC.resourceTypeName]})); + vpc.apply(new Tag('BusinessUnit', 'Marketing', {includeResourceTypes: [CfnVPC.resourceTypeName]})); vpc.apply(new Tag('VpcType', 'Good')); expect(stack).to(haveResource("AWS::EC2::VPC", hasTags(toCfnTags(allTags)))); const taggables = ['Subnet', 'InternetGateway', 'NatGateway', 'RouteTable']; diff --git a/packages/@aws-cdk/cdk/lib/aspects/README.md b/packages/@aws-cdk/cdk/lib/aspects/README.md index 28d576bc1977f..b76b382baa9aa 100644 --- a/packages/@aws-cdk/cdk/lib/aspects/README.md +++ b/packages/@aws-cdk/cdk/lib/aspects/README.md @@ -1,9 +1,7 @@ ## Aspects -Aspects are a mechanism to extend the CDK without having to direclty impact the -class heirarchy directly. This is a form of [Aspect-oriented -programming](https://en.wikipedia.org/wiki/Aspect-oriented_programming) which we -have implemented using the [Visitor +Aspects are a mechanism to extend the CDK without having to directly impact the +class hierarchy directly. We have implemented Aspects using the [Visitor Pattern](https://en.wikipedia.org/wiki/Aspect-oriented_programming). ### Aspects in the CDK @@ -20,7 +18,7 @@ export interface IAspect { */ readonly visitType: AspectVisitType; /** - * All aspects can visit a IConstruct + * All aspects can visit an IConstruct */ visit(node: IConstruct): void; } @@ -151,54 +149,27 @@ new ecs.LoadBalancedFargateService(marketingStack, 'B2CService', { The resulting tags are as follows: +> We are omitting the default tags for VPC components. + | Construct Path | Tag Key | Tag Value | | ----------|:---------|:-----| |MarketingSystem/MarketingVpc|CostCenter|Platform| -|MarketingSystem/MarketingVpc|Name|MarketingSystem/MarketingVpc| -|MarketingSystem/MarketingVpc/PublicSubnet1| Name | Platform|MarketingSystem/MarketingVpc/PublicSubnet1| -|MarketingSystem/MarketingVpc/PublicSubnet1| aws-cdk:subnet-name | Public| -|MarketingSystem/MarketingVpc/PublicSubnet1| aws-cdk:subnet-type | Public| |MarketingSystem/MarketingVpc/PublicSubnet1| CostCenter | Platform| |MarketingSystem/MarketingVpc/PublicSubnet1/RouteTable| CostCenter | Platform| -|MarketingSystem/MarketingVpc/PublicSubnet1/RouteTable|Name| MarketingSystem/MarketingVpc/PublicSubnet1| |MarketingSystem/MarketingVpc/PublicSubnet1/NATGateway| CostCenter | Platform| -|MarketingSystem/MarketingVpc/PublicSubnet1/NATGateway|Name|MarketingSystem/MarketingVpc/PublicSubnet1| -|MarketingSystem/MarketingVpc/PublicSubnet2| Name | Platform|MarketingSystem/MarketingVpc/PublicSubnet2| -|MarketingSystem/MarketingVpc/PublicSubnet2| aws-cdk:subnet-name | Public| -|MarketingSystem/MarketingVpc/PublicSubnet2| aws-cdk:subnet-type | Public| |MarketingSystem/MarketingVpc/PublicSubnet2| CostCenter | Platform| |MarketingSystem/MarketingVpc/PublicSubnet2/RouteTable| CostCenter | Platform| -|MarketingSystem/MarketingVpc/PublicSubnet2/RouteTable|Name| MarketingSystem/MarketingVpc/PublicSubnet2| |MarketingSystem/MarketingVpc/PublicSubnet2/NATGateway| CostCenter | Platform| -|MarketingSystem/MarketingVpc/PublicSubnet2/NATGateway|Name|MarketingSystem/MarketingVpc/PublicSubnet2| -|MarketingSystem/MarketingVpc/PublicSubnet3| Name | Platform|MarketingSystem/MarketingVpc/PublicSubnet3| -|MarketingSystem/MarketingVpc/PublicSubnet3| aws-cdk:subnet-name | Public| -|MarketingSystem/MarketingVpc/PublicSubnet3| aws-cdk:subnet-type | Public| |MarketingSystem/MarketingVpc/PublicSubnet3| CostCenter | Platform| |MarketingSystem/MarketingVpc/PublicSubnet3/RouteTable| CostCenter | Platform| -|MarketingSystem/MarketingVpc/PublicSubnet3/RouteTable|Name| MarketingSystem/MarketingVpc/PublicSubnet3| |MarketingSystem/MarketingVpc/PublicSubnet3/NATGateway| CostCenter | Platform| -|MarketingSystem/MarketingVpc/PublicSubnet3/NATGateway|Name|MarketingSystem/MarketingVpc/PublicSubnet3| -|MarketingSystem/MarketingVpc/PrivateSubnet1| Name | Platform|MarketingSystem/MarketingVpc/PrivateSubnet1| -|MarketingSystem/MarketingVpc/PrivateSubnet1| aws-cdk:subnet-name | Private| -|MarketingSystem/MarketingVpc/PrivateSubnet1| aws-cdk:subnet-type | Private| |MarketingSystem/MarketingVpc/PrivateSubnet1| CostCenter | Platform| |MarketingSystem/MarketingVpc/PrivateSubnet1/RouteTable| CostCenter | Platform| -|MarketingSystem/MarketingVpc/PrivateSubnet1/RouteTable|Name| MarketingSystem/MarketingVpc/PrivateSubnet1| -|MarketingSystem/MarketingVpc/PrivateSubnet2| Name | Platform|MarketingSystem/MarketingVpc/PrivateSubnet2| -|MarketingSystem/MarketingVpc/PrivateSubnet2| aws-cdk:subnet-name | Private| -|MarketingSystem/MarketingVpc/PrivateSubnet2| aws-cdk:subnet-type | Private| |MarketingSystem/MarketingVpc/PrivateSubnet2| CostCenter | Platform| |MarketingSystem/MarketingVpc/PrivateSubnet2/RouteTable| CostCenter | Platform| -|MarketingSystem/MarketingVpc/PrivateSubnet2/RouteTable|Name| MarketingSystem/MarketingVpc/PrivateSubnet2| -|MarketingSystem/MarketingVpc/PrivateSubnet3| Name | Platform|MarketingSystem/MarketingVpc/PrivateSubnet3| -|MarketingSystem/MarketingVpc/PrivateSubnet3| aws-cdk:subnet-name | Private| -|MarketingSystem/MarketingVpc/PrivateSubnet3| aws-cdk:subnet-type | Private| |MarketingSystem/MarketingVpc/PrivateSubnet3| CostCenter | Platform| |MarketingSystem/MarketingVpc/PrivateSubnet3/RouteTable| CostCenter | Platform| -|MarketingSystem/MarketingVpc/PrivateSubnet3/RouteTable|Name| MarketingSystem/MarketingVpc/PrivateSubnet3| |MarketingSystem/MarketingVpc/IGW|CostCenter|Platform| -|MarketingSystem/MarketingVpc/IGW|Name|MarketingSystem/MarketingVpc| |MarketingSystem/B2BService/Service/SecurityGroup/Resource|CostCenter|Marketing| |MarketingSystem/B2BService/LB/Resource|CostCenter|Marketing| |MarketingSystem/B2BService/LB/SecurityGroup/Resource|CostCenter|Marketing| diff --git a/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts b/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts index 2594fc49e734b..d533dbec3ce9a 100644 --- a/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts +++ b/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts @@ -1,13 +1,9 @@ import { ITaggable, Resource } from '../cloudformation/resource'; import { IConstruct } from '../core/construct'; +import { TagProps } from '../core/tag-manager'; import { AspectVisitType, IAspect } from './aspect'; -export interface TagAspectProps { - /** - * This applies specifically to AutoScalingGroup PropagateAtLaunch - */ - applyToLaunchInstances?: boolean; - +export interface TagAspectProps extends TagProps { /** * An array of Resource Types that will receive this tag * @@ -15,7 +11,7 @@ export interface TagAspectProps { * tag only to Resource types that are included in this array. * @default [] */ - include?: string[]; + includeResourceTypes?: string[]; /** * An array of Resource Types that will not receive this tag @@ -25,7 +21,7 @@ export interface TagAspectProps { * this array. * @default [] */ - exclude?: string[]; + excludeResourceTypes?: string[]; } /** @@ -39,14 +35,13 @@ export abstract class TagBase implements IAspect { public readonly key: string; public readonly visitType: AspectVisitType = AspectVisitType.Single; - private readonly include: string[]; - private readonly exclude: string[]; + private readonly includeResourceTypes: string[]; + private readonly excludeResourceTypes: string[]; constructor(key: string, props: TagAspectProps = {}) { this.key = key; - - this.include = props.include || []; - this.exclude = props.exclude || []; + this.includeResourceTypes = props.includeResourceTypes || []; + this.excludeResourceTypes = props.excludeResourceTypes || []; } public visit(construct: IConstruct): void { @@ -55,10 +50,12 @@ export abstract class TagBase implements IAspect { } const resource = construct as Resource; if (Resource.isTaggable(resource)) { - if (this.exclude.length !== 0 && this.exclude.indexOf(resource.resourceType) !== -1) { + if (this.excludeResourceTypes.length !== 0 && + this.excludeResourceTypes.indexOf(resource.resourceType) !== -1) { return; } - if (this.include.length !== 0 && this.include.indexOf(resource.resourceType) === -1) { + if (this.includeResourceTypes.length !== 0 && + this.includeResourceTypes.indexOf(resource.resourceType) === -1) { return; } this.applyTag(resource); @@ -78,11 +75,13 @@ export class Tag extends TagBase { */ public readonly value: string; - private readonly applyToLaunchInstances: boolean; + private readonly applyToLaunchedInstances: boolean; + private readonly priority: number; constructor(key: string, value: string, props: TagAspectProps = {}) { super(key, {...props}); - this.applyToLaunchInstances = props.applyToLaunchInstances !== false; + this.applyToLaunchedInstances = props.applyToLaunchedInstances !== false; + this.priority = props.priority === undefined ? 0 : props.priority; if (value === undefined) { throw new Error('Tag must have a value'); } @@ -90,7 +89,10 @@ export class Tag extends TagBase { } protected applyTag(resource: ITaggable) { - resource.tags.setTag(this.key, this.value!, {applyToLaunchInstances: this.applyToLaunchInstances}); + resource.tags.setTag(this.key, this.value!, { + applyToLaunchedInstances: this.applyToLaunchedInstances, + priority: this.priority, + }); } } @@ -99,12 +101,15 @@ export class Tag extends TagBase { */ export class RemoveTag extends TagBase { + private readonly priority: number; + constructor(key: string, props: TagAspectProps = {}) { super(key, props); + this.priority = props.priority === undefined ? 1 : props.priority; } protected applyTag(resource: ITaggable): void { - resource.tags.removeTag(this.key); + resource.tags.removeTag(this.key, this.priority); return; } } diff --git a/packages/@aws-cdk/cdk/lib/core/tag-manager.ts b/packages/@aws-cdk/cdk/lib/core/tag-manager.ts index f4983e5e5faf9..009135bba7859 100644 --- a/packages/@aws-cdk/cdk/lib/core/tag-manager.ts +++ b/packages/@aws-cdk/cdk/lib/core/tag-manager.ts @@ -12,7 +12,12 @@ export interface TagProps { /** * Handles AutoScalingGroup PropagateAtLaunch property */ - applyToLaunchInstances: boolean; + applyToLaunchedInstances?: boolean; + + /** + * + */ + priority?: number; } /** @@ -22,6 +27,8 @@ export class TagManager { private readonly tags: Tags = {}; + private readonly removedTags: {[key: string]: number} = {}; + constructor(private readonly tagType: TagType) { } /** @@ -31,8 +38,18 @@ export class TagManager { * @param value The value value of the tag * @param props A `TagProps` defaulted to applyToLaunchInstances true */ - public setTag(key: string, value: string, props: TagProps = {applyToLaunchInstances: true}): void { - this.tags[key] = { value, props }; + public setTag(key: string, value: string, props?: TagProps): void { + const tagProps: TagProps = props || {}; + tagProps.priority = tagProps.priority === undefined ? 0 : tagProps.priority; + + if (!this.hasHigherPriority(key, tagProps.priority)) { + // tag is blocked by a remove + return; + } + tagProps.applyToLaunchedInstances = tagProps.applyToLaunchedInstances !== false; + this.tags[key] = { value, props: tagProps }; + // ensure nothing is left in removeTags + delete this.removedTags[key]; } /** @@ -40,8 +57,13 @@ export class TagManager { * * @param key The key of the tag to remove */ - public removeTag(key: string): void { + public removeTag(key: string, priority: number = 0): void { + if (!this.hasHigherPriority(key, priority)) { + // tag is blocked by a remove + return; + } delete this.tags[key]; + this.removedTags[key] = priority; } /** @@ -63,7 +85,7 @@ export class TagManager { tags.push({ key, value: this.tags[key].value, - propagateAtLaunch: this.tags[key].props.applyToLaunchInstances} + propagateAtLaunch: this.tags[key].props.applyToLaunchedInstances !== false} ); } return tags.length === 0 ? undefined : tags; @@ -80,4 +102,16 @@ export class TagManager { } } } + + private hasHigherPriority(key: string, priority: number): boolean { + if (this.tags[key]) { + if (this.tags[key].props.priority !== undefined) { + return priority >= this.tags[key].props.priority!; + } + } + if (this.removedTags[key]) { + return priority >= this.removedTags[key]; + } + return true; + } } diff --git a/packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts b/packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts index 3b27b90e867f6..f4d6e7ff1f597 100644 --- a/packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts +++ b/packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts @@ -115,7 +115,7 @@ export = { const map = new MapTaggableResource(res, 'MapFakeResource', { type: 'AWS::Fake::Map', }); - res.apply(new Tag('foo', 'bar', {include: ['AWS::Fake::Asg']})); + res.apply(new Tag('foo', 'bar', {includeResourceTypes: ['AWS::Fake::Asg']})); root.node.prepareTree(); test.deepEqual(res.tags.renderTags(), undefined); test.deepEqual(map.tags.renderTags(), undefined); @@ -124,6 +124,36 @@ export = { test.deepEqual(map.testProperties().tags, undefined); test.done(); }, + 'removeTag Aspects by default will override child Tag Aspects'(test: Test) { + const root = new Stack(); + const res = new TaggableResource(root, 'FakeResource', { + type: 'AWS::Fake::Thing', + }); + const res2 = new TaggableResource(res, 'FakeResource', { + type: 'AWS::Fake::Thing', + }); + res.apply(new RemoveTag('key')); + res2.apply(new Tag('key', 'value')); + root.node.prepareTree(); + test.deepEqual(res.tags.renderTags(), undefined); + test.deepEqual(res2.tags.renderTags(), undefined); + test.done(); + }, + 'removeTag Aspects with priority 0 will not override child Tag Aspects'(test: Test) { + const root = new Stack(); + const res = new TaggableResource(root, 'FakeResource', { + type: 'AWS::Fake::Thing', + }); + const res2 = new TaggableResource(res, 'FakeResource', { + type: 'AWS::Fake::Thing', + }); + res.apply(new RemoveTag('key', {priority: 0})); + res2.apply(new Tag('key', 'value')); + root.node.prepareTree(); + test.deepEqual(res.tags.renderTags(), undefined); + test.deepEqual(res2.tags.renderTags(), [{key: 'key', value: 'value'}]); + test.done(); + }, 'exclude prevents tag application to resource types in the list'(test: Test) { const root = new Stack(); const res = new TaggableResource(root, 'FakeResource', { @@ -139,7 +169,7 @@ export = { const map = new MapTaggableResource(res, 'MapFakeResource', { type: 'AWS::Fake::Map', }); - res.apply(new Tag('foo', 'bar', {exclude: ['AWS::Fake::Asg']})); + res.apply(new Tag('foo', 'bar', {excludeResourceTypes: ['AWS::Fake::Asg']})); root.node.prepareTree(); test.deepEqual(res.tags.renderTags(), [{key: 'foo', value: 'bar'}]); test.deepEqual(res2.tags.renderTags(), [{key: 'foo', value: 'bar'}]); diff --git a/packages/@aws-cdk/cdk/test/core/test.tag-manager.ts b/packages/@aws-cdk/cdk/test/core/test.tag-manager.ts index 4c8a913b831d2..cb8dc9c5479e0 100644 --- a/packages/@aws-cdk/cdk/test/core/test.tag-manager.ts +++ b/packages/@aws-cdk/cdk/test/core/test.tag-manager.ts @@ -55,7 +55,7 @@ export = { tagged.push(mapper); for (const res of tagged) { res.setTag('foo', 'bar'); - res.setTag('asg', 'only', {applyToLaunchInstances: false}); + res.setTag('asg', 'only', {applyToLaunchedInstances: false}); } test.deepEqual(standard.renderTags(), [ {key: 'foo', value: 'bar'}, @@ -71,4 +71,20 @@ export = { }); test.done(); }, + 'tags with higher or equal priority always take precedence'(test: Test) { + const mgr = new TagManager(TagType.Standard); + mgr.setTag('key', 'myVal', { + priority: 2, + }); + mgr.setTag('key', 'newVal', { + priority: 1, + }); + mgr.removeTag('key', 1); + test.deepEqual(mgr.renderTags(), [ + {key: 'key', value: 'myVal'}, + ]); + mgr.removeTag('key', 2); + test.deepEqual(mgr.renderTags(), undefined); + test.done(); + }, }; From 46e2f70646fcbb4c9fdf133979d639baa9b14b9f Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Sun, 27 Jan 2019 11:28:24 -0800 Subject: [PATCH 19/38] Adding a Tag Readme section more focused on end user tagging and less on the aspect --- packages/@aws-cdk/cdk/lib/aspects/README.md | 93 +++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/packages/@aws-cdk/cdk/lib/aspects/README.md b/packages/@aws-cdk/cdk/lib/aspects/README.md index b76b382baa9aa..c092deb9a4d23 100644 --- a/packages/@aws-cdk/cdk/lib/aspects/README.md +++ b/packages/@aws-cdk/cdk/lib/aspects/README.md @@ -1,3 +1,96 @@ +## Tagging with the CDK + +Tags are implemented using `Aspects` for details on the implementation see the +detailed sections below. + +### Tag Quickstart + +Tags can be applied to any `Construct` in the CDK. By default the Tag will +propagate to any resource that is a child of the construct and supports tagging. + +For example, if you create a stack and want anything in the stack to receive a +tag: + +```ts +import cdk = require('@aws-cdk/cdk'); + +const app = new cdk.App(); +const theBestStack = new cdk.Stack(app, 'MarketingSystem'); +theBestStack.apply(new cdk.Tag('StackType', 'TheBest')); + +// any resources added that support tags will get them +``` + +### Advanced Tagging + +#### Resource Type Controls + +If you need to apply a tag, but want to specify which CloudFormation Resource +Types are affected you can include or exclude those in properties. + +```ts +import cdk = require('@aws-cdk/cdk'); +import ec2 = require('@aws-cdk/aws-ec2'); + +const app = new cdk.App(); +const theBestStack = new cdk.Stack(app, 'MarketingSystem'); +theBestStack.apply(new cdk.Tag('StackType', 'TheBest', { + includeResourceTypes: [ec2.CfnVpc.resourceType] + })); +// the only resource with tags will be the VPC + +// or tag all but the vpc with +theBestStack.apply(new cdk.Tag('StackType', 'TheBest', { + excludeResourceTypes: [ec2.CfnVpc.resourceType] + })); + +``` + +#### Removing Tags + +Removing tags is accomplished by applying a `RemoveTag`. The important default +is that remove tags override `Tag` unless specified differently. + +```ts +import cdk = require('@aws-cdk/cdk'); + +const app = new cdk.App(); +const theBestStack = new cdk.Stack(app, 'MarketingSystem'); +theBestStack.apply(new cdk.Tag('StackType', 'TheBest')); +theBestStack.apply(new cdk.RemoveTag('StackType', 'TheBest')); +// now nothing will be tagged +``` + +#### Tag Priority + +The final control you have is to set a priority number. Tags with highest +priority number will always win. If tags have the same priority number the tag +closest in tree distance to the resource will win. + +The default priority of `Tag` is 0. The default priority of `RemoveTag` is 1. + +```ts +import cdk = require('@aws-cdk/cdk'); +import ec2 = require('@aws-cdk/aws-ec2'); + +const app = new cdk.App(); +const theBestStack = new cdk.Stack(app, 'MarketingSystem'); + +theBestStack.apply(new cdk.Tag('StackType', 'TheBest', {prioirty: 1})); + +const vpc = new ec2.VpcNetwork(marketingStack, 'MarketingVpc', { + maxAZs: 3, +}); + +// Tag default is 0, so this has no affect +vpc.apply(new cdk.Tag('StackType', 'TheBestVpc')); +// RemoveTag default is 1, so this removes the tag from the VPC and children of +// VPC (e.g. Subnet, RouteTable, NAT Gateway, etc) +vpc.apply(new cdk.RemoveTag('StackType')); +``` + +You can use priority to control tagging regardless of the hierarchy of the tree. + ## Aspects Aspects are a mechanism to extend the CDK without having to directly impact the From 69401f173714f6af98f070e2243efea2c3856898 Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Sun, 27 Jan 2019 15:46:11 -0800 Subject: [PATCH 20/38] removing multiple aspects --- packages/@aws-cdk/cdk/lib/aspects/README.md | 13 +----- packages/@aws-cdk/cdk/lib/aspects/aspect.ts | 16 ------- .../@aws-cdk/cdk/lib/aspects/tag-aspect.ts | 3 +- packages/@aws-cdk/cdk/lib/core/construct.ts | 24 +++++------ packages/@aws-cdk/cdk/test/test.aspect.ts | 43 ++----------------- 5 files changed, 16 insertions(+), 83 deletions(-) diff --git a/packages/@aws-cdk/cdk/lib/aspects/README.md b/packages/@aws-cdk/cdk/lib/aspects/README.md index c092deb9a4d23..2219ab22d8040 100644 --- a/packages/@aws-cdk/cdk/lib/aspects/README.md +++ b/packages/@aws-cdk/cdk/lib/aspects/README.md @@ -106,10 +106,6 @@ An aspect in the CDK is defined by the interface: * Represents an Aspect */ export interface IAspect { - /** - * Aspects invocation pattern - */ - readonly visitType: AspectVisitType; /** * All aspects can visit an IConstruct */ @@ -117,15 +113,8 @@ export interface IAspect { } ``` -Initially two types of aspects are supported. - -1. Single visit aspects will ensure the aspect visit function is only invoked - one time. -1. Multiple visit aspects will invoke the visit function as many times as - requested - Aspects can be applied to any `Construct`. During the tree -preparation phase the aspect will visit each construct in the tree at least one +preparation phase the aspect will visit each construct in the tree one time. Aspects are invoked in the order they were added to the `Construct`, starting at the `App` (root of the tree) and progressing in order to the leaf nodes (most commonly the CloudFormation Resource). Aspect authors will implement diff --git a/packages/@aws-cdk/cdk/lib/aspects/aspect.ts b/packages/@aws-cdk/cdk/lib/aspects/aspect.ts index 086d1df3148ad..09d6da42694b8 100644 --- a/packages/@aws-cdk/cdk/lib/aspects/aspect.ts +++ b/packages/@aws-cdk/cdk/lib/aspects/aspect.ts @@ -1,25 +1,9 @@ import { IConstruct } from '../core/construct'; -export enum AspectVisitType { - /** - * The aspect will visit each Construct only once - */ - Single = 'Single', - - /** - * The aspect will visit each contruct as manay times as invoked - */ - Multiple = 'Multiple' -} - /** * Represents an Aspect */ export interface IAspect { - /** - * Aspects invocation pattern - */ - readonly visitType: AspectVisitType; /** * All aspects can visit a IConstruct */ diff --git a/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts b/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts index d533dbec3ce9a..f626f63ea56f8 100644 --- a/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts +++ b/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts @@ -1,7 +1,7 @@ import { ITaggable, Resource } from '../cloudformation/resource'; import { IConstruct } from '../core/construct'; import { TagProps } from '../core/tag-manager'; -import { AspectVisitType, IAspect } from './aspect'; +import { IAspect } from './aspect'; export interface TagAspectProps extends TagProps { /** @@ -34,7 +34,6 @@ export abstract class TagBase implements IAspect { */ public readonly key: string; - public readonly visitType: AspectVisitType = AspectVisitType.Single; private readonly includeResourceTypes: string[]; private readonly excludeResourceTypes: string[]; diff --git a/packages/@aws-cdk/cdk/lib/core/construct.ts b/packages/@aws-cdk/cdk/lib/core/construct.ts index a26bdb684142a..cf61f753ea553 100644 --- a/packages/@aws-cdk/cdk/lib/core/construct.ts +++ b/packages/@aws-cdk/cdk/lib/core/construct.ts @@ -1,5 +1,5 @@ import cxapi = require('@aws-cdk/cx-api'); -import { AspectVisitType, IAspect } from '../aspects/aspect'; +import { IAspect } from '../aspects/aspect'; import { CloudFormationJSON } from '../cloudformation/cloudformation-json'; import { makeUniqueId } from '../util/uniqueid'; import { Token, unresolved } from './tokens'; @@ -463,19 +463,15 @@ export class ConstructNode { */ private invokeAspects(): void { const descendants = this.findAll(); - const aspectsToVisit = this.visitableAspects(); - aspectsToVisit.forEach( aspect => { + for (const aspect of this.aspects) { + if (this.invokedAspects.includes(aspect)) { + continue; + } descendants.forEach( member => aspect.visit(member)); this.invokedAspects.push(aspect); - }); + } } - private visitableAspects(): IAspect[] { - return this.aspects.filter( aspect => { - return aspect.visitType === AspectVisitType.Multiple || - !this.invokedAspects.includes(aspect); - }); - } /** * Return the path of components up to but excluding the root */ @@ -631,8 +627,8 @@ export enum ConstructOrder { */ BreadthFirst, - /** - * Depth first - */ - DepthFirst + /** + * Depth first + */ + DepthFirst } diff --git a/packages/@aws-cdk/cdk/test/test.aspect.ts b/packages/@aws-cdk/cdk/test/test.aspect.ts index 92ab6abbe3712..7681b4ccfbac4 100644 --- a/packages/@aws-cdk/cdk/test/test.aspect.ts +++ b/packages/@aws-cdk/cdk/test/test.aspect.ts @@ -1,5 +1,5 @@ import { Test } from 'nodeunit'; -import { AspectVisitType, IAspect } from '../lib/aspects/aspect'; +import { IAspect } from '../lib/aspects/aspect'; import { IConstruct, Root } from '../lib/core/construct'; class MyConstruct extends Root { @@ -9,19 +9,7 @@ class MyConstruct extends Root { public visitCounter: number = 0; } -class VisitMany implements IAspect { - public readonly visitType = AspectVisitType.Multiple; - - public visit(node: IConstruct): void { - if (MyConstruct.IsMyConstruct(node)) { - node.visitCounter += 1; - } - } -} - class VisitOnce implements IAspect { - public readonly visitType = AspectVisitType.Single; - public visit(node: IConstruct): void { if (MyConstruct.IsMyConstruct(node)) { node.visitCounter += 1; @@ -29,36 +17,13 @@ class VisitOnce implements IAspect { } } export = { - 'Aspects with multiple visit type': { - 'are invoked every time'(test: Test) { - const root = new MyConstruct(); - root.apply(new VisitMany()); - root.node.prepareTree(); - test.deepEqual(root.visitCounter, 1); - root.node.prepareTree(); - test.deepEqual(root.visitCounter, 2); - test.done(); - }, - }, - 'Aspects with single visit type': { - 'are invoked only once'(test: Test) { - const root = new MyConstruct(); - root.apply(new VisitOnce()); - root.node.prepareTree(); - test.deepEqual(root.visitCounter, 1); - root.node.prepareTree(); - test.deepEqual(root.visitCounter, 1); - test.done(); - }, - }, - 'A construct can have both Aspect types'(test: Test) { + 'Aspects are invoked only once'(test: Test) { const root = new MyConstruct(); root.apply(new VisitOnce()); - root.apply(new VisitMany()); root.node.prepareTree(); - test.deepEqual(root.visitCounter, 2); + test.deepEqual(root.visitCounter, 1); root.node.prepareTree(); - test.deepEqual(root.visitCounter, 3); + test.deepEqual(root.visitCounter, 1); test.done(); }, }; From 551465008ce413b5a041d67c548e458c7d8f69e6 Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Sun, 27 Jan 2019 15:46:33 -0800 Subject: [PATCH 21/38] visitor pattern link correction --- packages/@aws-cdk/cdk/lib/aspects/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/cdk/lib/aspects/README.md b/packages/@aws-cdk/cdk/lib/aspects/README.md index 2219ab22d8040..acb6e99b34ce0 100644 --- a/packages/@aws-cdk/cdk/lib/aspects/README.md +++ b/packages/@aws-cdk/cdk/lib/aspects/README.md @@ -95,7 +95,7 @@ You can use priority to control tagging regardless of the hierarchy of the tree. Aspects are a mechanism to extend the CDK without having to directly impact the class hierarchy directly. We have implemented Aspects using the [Visitor -Pattern](https://en.wikipedia.org/wiki/Aspect-oriented_programming). +Pattern](https://en.wikipedia.org/wiki/Visitor_pattern). ### Aspects in the CDK From 25b4427b58e54130a307f5bb4ff9762a0cc274f7 Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Sat, 2 Feb 2019 15:40:21 -0800 Subject: [PATCH 22/38] README updates and small comment improvements --- .../hello-cdk-ecs-tags/index.ts | 53 +++ packages/@aws-cdk/cdk/README.md | 146 ++++++++ packages/@aws-cdk/cdk/lib/aspects/README.md | 345 ------------------ packages/@aws-cdk/cdk/lib/aspects/aspect.ts | 2 +- packages/@aws-cdk/cdk/lib/core/tag-manager.ts | 5 + 5 files changed, 205 insertions(+), 346 deletions(-) create mode 100644 examples/cdk-examples-typescript/hello-cdk-ecs-tags/index.ts delete mode 100644 packages/@aws-cdk/cdk/lib/aspects/README.md diff --git a/examples/cdk-examples-typescript/hello-cdk-ecs-tags/index.ts b/examples/cdk-examples-typescript/hello-cdk-ecs-tags/index.ts new file mode 100644 index 0000000000000..998fdc76e751d --- /dev/null +++ b/examples/cdk-examples-typescript/hello-cdk-ecs-tags/index.ts @@ -0,0 +1,53 @@ +import ec2 = require('@aws-cdk/aws-ec2'); +import ecs = require('@aws-cdk/aws-ecs'); +import cdk = require('@aws-cdk/cdk'); + +const COST_CENTER_KEY = 'CostCenter'; + +class MarketingFargate extends cdk.Stack { + constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + // Create VPC and Fargate Cluster + // NOTE: Limit AZs to avoid reaching resource quotas + const vpc = new ec2.VpcNetwork(this, 'MyVpc', { maxAZs: 3 }); + + // override cost center to platform + vpc.apply(new cdk.Tag(COST_CENTER_KEY, 'Platform')); + + const cluster = new ecs.Cluster(this, 'Cluster', { vpc }); + + // Create a load-balanced Fargate service and make it public + const b2b = new ecs.LoadBalancedFargateService(this, 'B2BService', { + cluster: cluster, // Required + cpu: '512', // Default is 256 + desiredCount: 6, // Default is 1 + image: ecs.ContainerImage.fromDockerHub('amazon/amazon-ecs-sample'), // Required + memoryMiB: '2048', // Default is 512 + publicLoadBalancer: true // Default is false + }); + + // Create a load-balanced Fargate service and make it public + const b2c = new ecs.LoadBalancedFargateService(this, 'B2CService', { + cluster: cluster, // Required + cpu: '512', // Default is 256 + desiredCount: 6, // Default is 1 + image: ecs.ContainerImage.fromDockerHub('amazon/amazon-ecs-sample'), // Required + memoryMiB: '2048', // Default is 512 + publicLoadBalancer: true // Default is false + }); + + // Output the DNS where you can access your service + new cdk.Output(this, 'B2BLoadBalancerDNS', { value: b2b.loadBalancer.dnsName }); + new cdk.Output(this, 'B2CLoadBalancerDNS', { value: b2c.loadBalancer.dnsName }); + } +} + +const app = new cdk.App(); + +// by default bill everything to marketing overrides are in the stack +app.apply(new cdk.Tag(COST_CENTER_KEY, 'Marketing')); + +new MarketingFargate(app, 'Bonjour'); + +app.run(); diff --git a/packages/@aws-cdk/cdk/README.md b/packages/@aws-cdk/cdk/README.md index f1dcb5bfd938c..01c199fda05c7 100644 --- a/packages/@aws-cdk/cdk/README.md +++ b/packages/@aws-cdk/cdk/README.md @@ -3,3 +3,149 @@ This library includes the basic building blocks of the [AWS Cloud Development Kit](https://github.com/awslabs/aws-cdk) (AWS CDK). +## Aspects + +Aspects are a mechanism to extend the CDK without having to directly impact the +class hierarchy. We have implemented Aspects using the [Visitor +Pattern](https://en.wikipedia.org/wiki/Visitor_pattern). + +An aspect in the CDK is defined by this [interface](lib/aspects/aspect.ts) + +Aspects can be applied to any construct. During the tree +preparation phase the aspect will visit each construct in the tree one +time. Aspects are invoked in the order they were added to the construct, +starting at the `App` (root of the tree) and progressing in order to the leaf +nodes (most commonly the CloudFormation Resource). Aspect authors will implement +the `visit(IConstruct)` function and can inspect the `Construct` for specific characteristics. +Such as, is this construct a CloudFormation Resource? + +## Tagging + +Tags are implemented using Aspects. + +Tags can be applied to any construct. Tags are inherited, based on the scope. If +you tag construct A, and A contains construct B, construct B inherits the tag. +The Tag API supports: + + * Add (apply) a tag, either to specific resources or all but specific resources + * Remove a tag, again either from specific resources or all but specific resources + +A simple example, if you create a stack and want anything in the stack to receive a +tag: + +```ts +import cdk = require('@aws-cdk/cdk'); + +const app = new cdk.App(); +const theBestStack = new cdk.Stack(app, 'MarketingSystem'); +theBestStack.apply(new cdk.Tag('StackType', 'TheBest')); + +// any resources added that support tags will get them +``` + +The goal was to enable the ability to define tags in one place and have them +applied consistently for all resources that support tagging. In addition +the developer should not have to know if the resource supports tags. The +developer defines the tagging intents for all resources within a path. +If the resources support tags they are added, else no action is taken. + +### Tag Example with ECS + +We are going to use the [ECS example](https://awslabs.github.io/aws-cdk/ecs_example.html) as starting point. + +For the purposes of example, this ECS cluster is for the Marketing Department. +Marketing has two core groups Business to Business (B2B) and Business to Consumer +(B2C). However, the Marketing team relies on the Platform team to help build the +common components across businesses and separates costs to match. The goal here +is tag the Platform team resources, the Marketing Department and then Marketing +groups to enable proper cost allocations. + +We have modified the example and the code is located: +examples/cdk-examples-typescript/hello-cdk-ecs-tags + +When the example is run the following tags are created: + +> We are omitting the default tags for VPC components. + +| Construct Path | Tag Key | Tag Value | +| ----------|:---------|:-----| +|MarketingSystem/MarketingVpc|CostCenter|Platform| +|MarketingSystem/MarketingVpc/PublicSubnet1| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PublicSubnet1/RouteTable| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PublicSubnet1/NATGateway| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PublicSubnet2| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PublicSubnet2/RouteTable| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PublicSubnet2/NATGateway| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PublicSubnet3| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PublicSubnet3/RouteTable| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PublicSubnet3/NATGateway| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PrivateSubnet1| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PrivateSubnet1/RouteTable| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PrivateSubnet2| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PrivateSubnet2/RouteTable| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PrivateSubnet3| CostCenter | Platform| +|MarketingSystem/MarketingVpc/PrivateSubnet3/RouteTable| CostCenter | Platform| +|MarketingSystem/MarketingVpc/IGW|CostCenter|Platform| +|MarketingSystem/B2BService/Service/SecurityGroup/Resource|CostCenter|Marketing| +|MarketingSystem/B2BService/LB/Resource|CostCenter|Marketing| +|MarketingSystem/B2BService/LB/SecurityGroup/Resource|CostCenter|Marketing| +|MarketingSystem/B2BService/LB/PublicListener/ECSGroup/Resource|CostCenter|Marketing| +|MarketingSystem/B2CService/Service/SecurityGroup/Resource|CostCenter|Marketing| +|MarketingSystem/B2CService/LB/Resource|CostCenter|Marketing| +|MarketingSystem/B2CService/LB/SecurityGroup/Resource|CostCenter|Marketing| +|MarketingSystem/B2CService/LB/PublicListener/ECSGroup/Resource|CostCenter|Marketing| + +As you can see many tags are generated with only a few intent based directives. The CDK does default some additional tags for suggested `Name` keys. If you want to remove those tags you can do so by using the `RemoveTag` aspect, see below: + +```ts +// snip // +const vpc = new ec2.VpcNetwork(marketingStack, 'MarketingVpc', { + maxAZs: 3 // Default is all AZs in region + }); +// override the VPC tags with Platform +// this will tag the VPC, Subnets, Route Tables, IGW, and NatGWs +vpc.apply(new cdk.Tag(COST_CENTER_KEY, 'Platform')); +vpc.apply(new cdk.RemoveTag('Name')); +// snip // +``` + +This will remove the name tags from the VPC, subnets, route tables and NAT +gateways. If you've been following closely, this may lead you to ask how does +remove work when the tag is actually applied closer to the resource? The Tag API +has a few features that are covered later to explain how this works. + +### API + +In order to enable additional controls a Tags can specifically include or +exclude a CloudFormation Resource Type, propagate tags for an autoscaling group, +and use priority to override the default precedence. See the `TagAspectProps` +interface for more details. + +#### applyToLaunchedInstances + +This property is a boolean that defaults to `true`. When `true` and the aspect +visits an AutoScalingGroup resource the `PropagateAtLaunch` property is set to +true. If false the property is set accordingly. + +#### includeResourceTypes + +Include is an array property that contains strings of CloudFormation Resource +Types. As the aspect visits nodes it only takes action if node is one of the +resource types in the array. By default the array is empty and an empty array is +interpreted as apply to any resource type. + +#### excludeResourceTypes + +Exclude is the inverse of include. Exclude is also an array of CloudFormation +Resource Types. As the aspect visit nodes it will not take action if the node is +one of the resource types in the array. By default the array is empty and an +empty array is interpreted to match no resource type. Exclude takes precedence +over include in the event of a collision. + +#### priority + +Priority is used to control precedence when the default pattern does not work. +In general users should try to avoid using priority, but in some situations it +is required. In the example above, this is how `RemoveTag` works. The default +setting for removing tags uses a higher priority than the standard tag. + diff --git a/packages/@aws-cdk/cdk/lib/aspects/README.md b/packages/@aws-cdk/cdk/lib/aspects/README.md deleted file mode 100644 index acb6e99b34ce0..0000000000000 --- a/packages/@aws-cdk/cdk/lib/aspects/README.md +++ /dev/null @@ -1,345 +0,0 @@ -## Tagging with the CDK - -Tags are implemented using `Aspects` for details on the implementation see the -detailed sections below. - -### Tag Quickstart - -Tags can be applied to any `Construct` in the CDK. By default the Tag will -propagate to any resource that is a child of the construct and supports tagging. - -For example, if you create a stack and want anything in the stack to receive a -tag: - -```ts -import cdk = require('@aws-cdk/cdk'); - -const app = new cdk.App(); -const theBestStack = new cdk.Stack(app, 'MarketingSystem'); -theBestStack.apply(new cdk.Tag('StackType', 'TheBest')); - -// any resources added that support tags will get them -``` - -### Advanced Tagging - -#### Resource Type Controls - -If you need to apply a tag, but want to specify which CloudFormation Resource -Types are affected you can include or exclude those in properties. - -```ts -import cdk = require('@aws-cdk/cdk'); -import ec2 = require('@aws-cdk/aws-ec2'); - -const app = new cdk.App(); -const theBestStack = new cdk.Stack(app, 'MarketingSystem'); -theBestStack.apply(new cdk.Tag('StackType', 'TheBest', { - includeResourceTypes: [ec2.CfnVpc.resourceType] - })); -// the only resource with tags will be the VPC - -// or tag all but the vpc with -theBestStack.apply(new cdk.Tag('StackType', 'TheBest', { - excludeResourceTypes: [ec2.CfnVpc.resourceType] - })); - -``` - -#### Removing Tags - -Removing tags is accomplished by applying a `RemoveTag`. The important default -is that remove tags override `Tag` unless specified differently. - -```ts -import cdk = require('@aws-cdk/cdk'); - -const app = new cdk.App(); -const theBestStack = new cdk.Stack(app, 'MarketingSystem'); -theBestStack.apply(new cdk.Tag('StackType', 'TheBest')); -theBestStack.apply(new cdk.RemoveTag('StackType', 'TheBest')); -// now nothing will be tagged -``` - -#### Tag Priority - -The final control you have is to set a priority number. Tags with highest -priority number will always win. If tags have the same priority number the tag -closest in tree distance to the resource will win. - -The default priority of `Tag` is 0. The default priority of `RemoveTag` is 1. - -```ts -import cdk = require('@aws-cdk/cdk'); -import ec2 = require('@aws-cdk/aws-ec2'); - -const app = new cdk.App(); -const theBestStack = new cdk.Stack(app, 'MarketingSystem'); - -theBestStack.apply(new cdk.Tag('StackType', 'TheBest', {prioirty: 1})); - -const vpc = new ec2.VpcNetwork(marketingStack, 'MarketingVpc', { - maxAZs: 3, -}); - -// Tag default is 0, so this has no affect -vpc.apply(new cdk.Tag('StackType', 'TheBestVpc')); -// RemoveTag default is 1, so this removes the tag from the VPC and children of -// VPC (e.g. Subnet, RouteTable, NAT Gateway, etc) -vpc.apply(new cdk.RemoveTag('StackType')); -``` - -You can use priority to control tagging regardless of the hierarchy of the tree. - -## Aspects - -Aspects are a mechanism to extend the CDK without having to directly impact the -class hierarchy directly. We have implemented Aspects using the [Visitor -Pattern](https://en.wikipedia.org/wiki/Visitor_pattern). - -### Aspects in the CDK - -An aspect in the CDK is defined by the interface: - -```ts -/** - * Represents an Aspect - */ -export interface IAspect { - /** - * All aspects can visit an IConstruct - */ - visit(node: IConstruct): void; -} -``` - -Aspects can be applied to any `Construct`. During the tree -preparation phase the aspect will visit each construct in the tree one -time. Aspects are invoked in the order they were added to the `Construct`, -starting at the `App` (root of the tree) and progressing in order to the leaf -nodes (most commonly the CloudFormation Resource). Aspect authors will implement -the `visit(IConstruct)` function can inspect the `Construct` for specific characteristics. -Such as, is this construct a CloudFormation Resource? - -### Tag and Remove Tag Aspect - -Tag support is the first use of Aspects. The goal was to enable the ability to -define tags in one place and them have applied consistently for all resources -that support tagging. In addition the developer should not have to know if the -resource supports tags. The developer defines the tagging intents for all -resources within a path. If the resources support tags they are added, else no -action is taken. - -In order to enable additional controls a Tag Aspect can specifically include or -exclude a CloudFormation Resource Type. See the TagAspectProps interface for -more details. - -#### Tag Example with ECS - -We are going to use the ECS example as starting point. - -For the purposes of example, this ECS cluster is for the Marketing Department. -Marketing has two core groups Business to Business (B2B) and Business to Consumer -(B2C). However, the Marketing team relies on the Platform team to help build the -common components across businesses and separates costs to match. The goal here -is tag the Platform team resources, the Marketing Department and then Marketing -groups to enable proper cost allocations. - - -```ts -import cdk = require('@aws-cdk/cdk'); -import ec2 = require('@aws-cdk/aws-ec2'); -import ecs = require('@aws-cdk/aws-ecs'); - -const app = new cdk.App(); -const marketingStack = new cdk.Stack(app, 'MarketingSystem'); - -const vpc = new ec2.VpcNetwork(marketingStack, 'MarketingVpc', { - maxAZs: 3 // Default is all AZs in region -}); - -const cluster = new ecs.Cluster(marketingStack, 'MarketingCluster', { - vpc: vpc -}); - -// Create a load-balanced Fargate service and make it public -new ecs.LoadBalancedFargateService(marketingStack, 'B2BService', { - cluster: cluster, // Required - cpu: '512', // Default is 256 - desiredCount: 6, // Default is 1 - image: ecs.ContainerImage.fromDockerHub('amazon/amazon-ecs-sample'), // Required - memoryMiB: '2048', // Default is 512 - publicLoadBalancer: true // Default is false -}); - -// Create a load-balanced Fargate service and make it public -new ecs.LoadBalancedFargateService(marketingStack, 'B2CService', { - cluster: cluster, // Required - cpu: '512', // Default is 256 - desiredCount: 6, // Default is 1 - image: ecs.ContainerImage.fromDockerHub('amazon/amazon-ecs-sample'), // Required - memoryMiB: '2048', // Default is 512 - publicLoadBalancer: true // Default is false -}); -``` - - -```ts -import cdk = require('@aws-cdk/cdk'); -import ec2 = require('@aws-cdk/aws-ec2'); -import ecs = require('@aws-cdk/aws-ecs'); - -const COST_CENTER_KEY = 'CostCenter'; - -const app = new cdk.App(); -// every resource starts with Marketing -app.apply(new cdk.Tag(COST_CENTER_KEY, 'Marketing')); - -const marketingStack = new cdk.Stack(app, 'MarketingSystem'); - -const vpc = new ec2.VpcNetwork(marketingStack, 'MarketingVpc', { - maxAZs: 3 // Default is all AZs in region -}); -// override the VPC tags with Platform -// this will tag the VPC, Subnets, Route Tables, IGW, and NatGWs -vpc.apply(new cdk.Tag(COST_CENTER_KEY, 'Platform')); - -const cluster = new ecs.Cluster(marketingStack, 'MarketingCluster', { - vpc: vpc -}); - -// Create a load-balanced Fargate service and make it public -const b2b = new ecs.LoadBalancedFargateService(marketingStack, 'B2BService', { - cluster: cluster, // Required - cpu: '512', // Default is 256 - desiredCount: 6, // Default is 1 - image: ecs.ContainerImage.fromDockerHub('amazon/amazon-ecs-sample'), // Required - memoryMiB: '2048', // Default is 512 - publicLoadBalancer: true // Default is false -}); - -// Create a load-balanced Fargate service and make it public -new ecs.LoadBalancedFargateService(marketingStack, 'B2CService', { - cluster: cluster, // Required - cpu: '512', // Default is 256 - desiredCount: 6, // Default is 1 - image: ecs.ContainerImage.fromDockerHub('amazon/amazon-ecs-sample'), // Required - memoryMiB: '2048', // Default is 512 - publicLoadBalancer: true // Default is false -}); -``` - -The resulting tags are as follows: - -> We are omitting the default tags for VPC components. - -| Construct Path | Tag Key | Tag Value | -| ----------|:---------|:-----| -|MarketingSystem/MarketingVpc|CostCenter|Platform| -|MarketingSystem/MarketingVpc/PublicSubnet1| CostCenter | Platform| -|MarketingSystem/MarketingVpc/PublicSubnet1/RouteTable| CostCenter | Platform| -|MarketingSystem/MarketingVpc/PublicSubnet1/NATGateway| CostCenter | Platform| -|MarketingSystem/MarketingVpc/PublicSubnet2| CostCenter | Platform| -|MarketingSystem/MarketingVpc/PublicSubnet2/RouteTable| CostCenter | Platform| -|MarketingSystem/MarketingVpc/PublicSubnet2/NATGateway| CostCenter | Platform| -|MarketingSystem/MarketingVpc/PublicSubnet3| CostCenter | Platform| -|MarketingSystem/MarketingVpc/PublicSubnet3/RouteTable| CostCenter | Platform| -|MarketingSystem/MarketingVpc/PublicSubnet3/NATGateway| CostCenter | Platform| -|MarketingSystem/MarketingVpc/PrivateSubnet1| CostCenter | Platform| -|MarketingSystem/MarketingVpc/PrivateSubnet1/RouteTable| CostCenter | Platform| -|MarketingSystem/MarketingVpc/PrivateSubnet2| CostCenter | Platform| -|MarketingSystem/MarketingVpc/PrivateSubnet2/RouteTable| CostCenter | Platform| -|MarketingSystem/MarketingVpc/PrivateSubnet3| CostCenter | Platform| -|MarketingSystem/MarketingVpc/PrivateSubnet3/RouteTable| CostCenter | Platform| -|MarketingSystem/MarketingVpc/IGW|CostCenter|Platform| -|MarketingSystem/B2BService/Service/SecurityGroup/Resource|CostCenter|Marketing| -|MarketingSystem/B2BService/LB/Resource|CostCenter|Marketing| -|MarketingSystem/B2BService/LB/SecurityGroup/Resource|CostCenter|Marketing| -|MarketingSystem/B2BService/LB/PublicListener/ECSGroup/Resource|CostCenter|Marketing| -|MarketingSystem/B2CService/Service/SecurityGroup/Resource|CostCenter|Marketing| -|MarketingSystem/B2CService/LB/Resource|CostCenter|Marketing| -|MarketingSystem/B2CService/LB/SecurityGroup/Resource|CostCenter|Marketing| -|MarketingSystem/B2CService/LB/PublicListener/ECSGroup/Resource|CostCenter|Marketing| - -As you can see many tags are generated with only a few intent based directives. The CDK does default some additional tags for suggested `Name` keys. If you want to remove those tags you can do so by using the `RemoveTag` aspect, see below: - -```ts -// snip // -const vpc = new ec2.VpcNetwork(marketingStack, 'MarketingVpc', { - maxAZs: 3 // Default is all AZs in region - }); -// override the VPC tags with Platform -// this will tag the VPC, Subnets, Route Tables, IGW, and NatGWs -vpc.apply(new cdk.Tag(COST_CENTER_KEY, 'Platform')); -vpc.apply(new cdk.RemoveTag('Name')); -// snip // -``` - -If you run the above modification you will notice that the Subnets, Route -Tables, and NAT Gateways will still have `Name` tags. Why is that? - -The framework applies aspects starting with the root node. In our example, this -would be the `cdk.App`. For the given node, the aspects are applied in the order -the same order they were added to the `Construct`. We have the following -simplified tree: - -``` - +------+ - | App | - | | - +---+--+ - | +------+ - | |VPC | - +----> | - +---+--+ - | - | +-------+ - | |Subnet | - +---> (6 Instances) - +-------+ -``` - -The nodes have the following aspects for just the Name tags: - -| Node | Aspects | Description | -| ------------- |:-------------:|:-------------| -| VPC | Tag(Name: `VPC Path`) | This is defaulted by the CDK| -| VPC | Remove(Name) | This is defaulted by the CDK| -| Subnet | Tag(Name: `Subnet Path`) | This is defaulted by the CDK on all 6 Subnets| - -The Subnets will see the following order of tag operations: - -1. Subnet set Tag (Name: `VPC Path`) -1. Subnet remove Tag (Name) -1. Subnet set Tag (Name: `Subnet Path`) - -> Note that route tables and NAT Gateways are also children of the subnet and -> receive the pattern as described above. - -If you really want to remove the name tag from subnets you will need to iterate -through subnets from the `vpc` object and apply the Remove Tag aspect. - -#### Tag Options - -Tag Aspects support three options: include, exclude and applyToLaunchInstances. - -##### applyToLaunchInstances - -This property is a boolean that defaults to `true`. When `true` and the aspect -visits an AutoScalingGroup resource the `PropagateAtLaunch` property is set to -true. If false the property is set accordingly. - -##### include - -Include is an array property that contains strings of CloudFormation Resource -Types. As the aspect visits nodes it only takes action if node is one of the -resource types in the array. By default the array is empty and an empty array is -interpreted as apply to any resource type. - -##### exclude - -Exclude is the inverse of include. Exclude is also an array of CloudFormation -Resource Types. As the aspect visit nodes it will not take action if the node is -one of the resource types in the array. By default the array is empty and an -empty array is interpreted to match no resource type. Exclude takes precedence -over include in the event of a collision. diff --git a/packages/@aws-cdk/cdk/lib/aspects/aspect.ts b/packages/@aws-cdk/cdk/lib/aspects/aspect.ts index 09d6da42694b8..e8148bba35c58 100644 --- a/packages/@aws-cdk/cdk/lib/aspects/aspect.ts +++ b/packages/@aws-cdk/cdk/lib/aspects/aspect.ts @@ -5,7 +5,7 @@ import { IConstruct } from '../core/construct'; */ export interface IAspect { /** - * All aspects can visit a IConstruct + * All aspects can visit an IConstruct */ visit(node: IConstruct): void; } diff --git a/packages/@aws-cdk/cdk/lib/core/tag-manager.ts b/packages/@aws-cdk/cdk/lib/core/tag-manager.ts index 009135bba7859..45125ade9a5f4 100644 --- a/packages/@aws-cdk/cdk/lib/core/tag-manager.ts +++ b/packages/@aws-cdk/cdk/lib/core/tag-manager.ts @@ -15,7 +15,12 @@ export interface TagProps { applyToLaunchedInstances?: boolean; /** + * Higher or equal priority tags will take precedence * + * Setting priority will enable the user to control tags when they need to not + * follow the default precedence pattern of last applied and closest to the + * construct in the tree. + * @default 0 for Tag 1 for Remove Tag */ priority?: number; } From 02fb6445b7d49007ae4edd9f518438f74e7172ad Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Sat, 2 Feb 2019 15:59:24 -0800 Subject: [PATCH 23/38] feat(cdk): aspects and tagging #1451 Aspect framework is added. Aspects can be used to affect the construct tree without modifying the class hierarchy. Tagging has been reimplemented to leverage the Aspect framework and simplify the original tag design. Tag Manager still exists, but is no longer intended for use by L2 construct authors. There are two new classes `cdk.Tag` and `cdk.RemoveTag`. Code generation has been modified to add tag support to any CloudFormation Resource that supports tagging, by creating a Tag Manager for that resource as a `tags` attribute. Breaking Change: if you are using TagManager the API for this object has completely changed. You should no longer use TagManager directly, but instead replace this with Tag Aspects. `cdk.Tag` has been renamed to `cdk.CfnTag` to enable `cdk.Tag` to be the Tag Aspect. Fixes #1136. From 12cfe1960580621b2ca15b0e164348a79ea7ab55 Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Sun, 3 Feb 2019 22:46:34 -0800 Subject: [PATCH 24/38] adding cdk.json for tag example --- examples/cdk-examples-typescript/hello-cdk-ecs-tags/cdk.json | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 examples/cdk-examples-typescript/hello-cdk-ecs-tags/cdk.json diff --git a/examples/cdk-examples-typescript/hello-cdk-ecs-tags/cdk.json b/examples/cdk-examples-typescript/hello-cdk-ecs-tags/cdk.json new file mode 100644 index 0000000000000..2f0e44c6fd27b --- /dev/null +++ b/examples/cdk-examples-typescript/hello-cdk-ecs-tags/cdk.json @@ -0,0 +1,3 @@ +{ + "app": "node index" +} From dcc530379dee2861e0034a0aacc1b3851a424c17 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Mon, 4 Feb 2019 12:03:06 +0100 Subject: [PATCH 25/38] feat(aws-ecs): add support for Event Targets (#1571) EC2 task definitions can now be used as CloudWatch event targets. ALSO IN THIS COMMIT * Improve hash calculation of Docker images. * Add `grantPassRole()` method to iam.Role Fixes #1370. --- packages/@aws-cdk/aws-ecs/README.md | 9 + .../aws-ecs/lib/base/task-definition.ts | 14 +- packages/@aws-cdk/aws-ecs/lib/cluster.ts | 24 + .../aws-ecs/lib/ec2/ec2-event-rule-target.ts | 101 ++ packages/@aws-cdk/aws-ecs/lib/index.ts | 1 + packages/@aws-cdk/aws-ecs/package.json | 2 + .../ec2/integ.event-task.lit.expected.json | 1087 +++++++++++++++++ .../aws-ecs/test/ec2/integ.event-task.lit.ts | 54 + .../test/ec2/test.ec2-event-rule-target.ts | 61 + .../test/eventhandler-image/Dockerfile | 5 + .../aws-ecs/test/eventhandler-image/index.py | 6 + packages/@aws-cdk/aws-iam/lib/role.ts | 20 + packages/@aws-cdk/aws-iam/test/test.role.ts | 30 +- packages/aws-cdk/lib/docker.ts | 7 + 14 files changed, 1412 insertions(+), 9 deletions(-) create mode 100644 packages/@aws-cdk/aws-ecs/lib/ec2/ec2-event-rule-target.ts create mode 100644 packages/@aws-cdk/aws-ecs/test/ec2/integ.event-task.lit.expected.json create mode 100644 packages/@aws-cdk/aws-ecs/test/ec2/integ.event-task.lit.ts create mode 100644 packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-event-rule-target.ts create mode 100644 packages/@aws-cdk/aws-ecs/test/eventhandler-image/Dockerfile create mode 100644 packages/@aws-cdk/aws-ecs/test/eventhandler-image/index.py diff --git a/packages/@aws-cdk/aws-ecs/README.md b/packages/@aws-cdk/aws-ecs/README.md index 6b68c2102ea4d..47177d90029ae 100644 --- a/packages/@aws-cdk/aws-ecs/README.md +++ b/packages/@aws-cdk/aws-ecs/README.md @@ -268,6 +268,15 @@ autoScalingGroup.scaleOnCpuUtilization('KeepCpuHalfwayLoaded', { See the `@aws-cdk/aws-autoscaling` library for more autoscaling options you can configure on your instances. +### Integration with CloudWatch Events + +To start an ECS task on an EC2-backed Cluster, instantiate an +`Ec2TaskEventRuleTarget` instead of an `Ec2Service`: + +[example of CloudWatch Events integration](test/ec2/integ.event-task.lit.ts) + +> Note: it is currently not possible to start Fargate tasks in this way. + ### Roadmap - [ ] Service Discovery Integration diff --git a/packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts b/packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts index 71a9077011f60..d1b5afd1509cc 100644 --- a/packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts +++ b/packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts @@ -133,6 +133,13 @@ export class TaskDefinition extends cdk.Construct { */ public compatibility: Compatibility; + /** + * Execution role for this task definition + * + * May not exist, will be created as needed. + */ + public executionRole?: iam.IRole; + /** * All containers */ @@ -143,13 +150,6 @@ export class TaskDefinition extends cdk.Construct { */ private readonly volumes: Volume[] = []; - /** - * Execution role for this task definition - * - * Will be created as needed. - */ - private executionRole?: iam.Role; - /** * Placement constraints for task instances */ diff --git a/packages/@aws-cdk/aws-ecs/lib/cluster.ts b/packages/@aws-cdk/aws-ecs/lib/cluster.ts index dfc6d5cc3fe0f..190284b67165a 100644 --- a/packages/@aws-cdk/aws-ecs/lib/cluster.ts +++ b/packages/@aws-cdk/aws-ecs/lib/cluster.ts @@ -147,6 +147,7 @@ export class Cluster extends cdk.Construct implements ICluster { public export(): ClusterImportProps { return { clusterName: new cdk.Output(this, 'ClusterName', { value: this.clusterName }).makeImportValue().toString(), + clusterArn: this.clusterArn, vpc: this.vpc.export(), securityGroups: this.connections.securityGroups.map(sg => sg.export()), hasEc2Capacity: this.hasEc2Capacity, @@ -233,6 +234,11 @@ export interface ICluster extends cdk.IConstruct { */ readonly clusterName: string; + /** + * The ARN of this cluster + */ + readonly clusterArn: string; + /** * VPC that the cluster instances are running in */ @@ -263,6 +269,13 @@ export interface ClusterImportProps { */ clusterName: string; + /** + * ARN of the cluster + * + * @default Derived from clusterName + */ + clusterArn?: string; + /** * VPC that the cluster instances are running in */ @@ -290,6 +303,11 @@ class ImportedCluster extends cdk.Construct implements ICluster { */ public readonly clusterName: string; + /** + * ARN of the cluster + */ + public readonly clusterArn: string; + /** * VPC that the cluster instances are running in */ @@ -311,6 +329,12 @@ 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({ + service: 'ecs', + resource: 'cluster', + resourceName: props.clusterName, + }); + let i = 1; for (const sgProps of props.securityGroups) { this.connections.addSecurityGroup(ec2.SecurityGroup.import(this, `SecurityGroup${i}`, sgProps)); diff --git a/packages/@aws-cdk/aws-ecs/lib/ec2/ec2-event-rule-target.ts b/packages/@aws-cdk/aws-ecs/lib/ec2/ec2-event-rule-target.ts new file mode 100644 index 0000000000000..90fdee743248e --- /dev/null +++ b/packages/@aws-cdk/aws-ecs/lib/ec2/ec2-event-rule-target.ts @@ -0,0 +1,101 @@ +import events = require ('@aws-cdk/aws-events'); +import iam = require('@aws-cdk/aws-iam'); +import cdk = require('@aws-cdk/cdk'); +import { TaskDefinition } from '../base/task-definition'; +import { ICluster } from '../cluster'; +import { isEc2Compatible } from '../util'; + +/** + * Properties to define an EC2 Event Task + */ +export interface Ec2EventRuleTargetProps { + /** + * Cluster where service will be deployed + */ + cluster: ICluster; + + /** + * Task Definition of the task that should be started + */ + taskDefinition: TaskDefinition; + + /** + * How many tasks should be started when this event is triggered + * + * @default 1 + */ + taskCount?: number; +} + +/** + * Start a service on an EC2 cluster + */ +export class Ec2EventRuleTarget extends cdk.Construct implements events.IEventRuleTarget { + private readonly cluster: ICluster; + private readonly taskDefinition: TaskDefinition; + private readonly taskCount: number; + + constructor(scope: cdk.Construct, id: string, props: Ec2EventRuleTargetProps) { + super(scope, id); + + if (!isEc2Compatible(props.taskDefinition.compatibility)) { + throw new Error('Supplied TaskDefinition is not configured for compatibility with EC2'); + } + + this.cluster = props.cluster; + this.taskDefinition = props.taskDefinition; + this.taskCount = props.taskCount !== undefined ? props.taskCount : 1; + } + + /** + * Allows using containers as target of CloudWatch events + */ + public asEventRuleTarget(_ruleArn: string, _ruleUniqueId: string): events.EventRuleTargetProps { + const role = this.eventsRole; + + role.addToPolicy(new iam.PolicyStatement() + .addAction('ecs:RunTask') + .addResource(this.taskDefinition.taskDefinitionArn) + .addCondition('ArnEquals', { "ecs:cluster": this.cluster.clusterArn })); + + return { + id: this.node.id, + arn: this.cluster.clusterArn, + roleArn: role.roleArn, + ecsParameters: { + taskCount: this.taskCount, + taskDefinitionArn: this.taskDefinition.taskDefinitionArn + } + }; + } + + /** + * Create or get the IAM Role used to start this Task Definition. + * + * We create it under the TaskDefinition object so that if we have multiple EventTargets + * they can reuse the same role. + */ + public get eventsRole(): iam.IRole { + let role = this.taskDefinition.node.tryFindChild('EventsRole') as iam.IRole; + if (role === undefined) { + role = new iam.Role(this.taskDefinition, 'EventsRole', { + assumedBy: new iam.ServicePrincipal('events.amazonaws.com') + }); + } + + return role; + } + + /** + * Prepare the Event Rule Target + */ + protected prepare() { + // If it so happens that a Task Execution Role was created for the TaskDefinition, + // then the CloudWatch Events Role must have permissions to pass it (otherwise it doesn't). + // + // It never needs permissions to the Task Role. + if (this.taskDefinition.executionRole !== undefined) { + this.taskDefinition.taskRole.grantPassRole(this.eventsRole); + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecs/lib/index.ts b/packages/@aws-cdk/aws-ecs/lib/index.ts index 15efbab29225d..c1b0a0f6c98f9 100644 --- a/packages/@aws-cdk/aws-ecs/lib/index.ts +++ b/packages/@aws-cdk/aws-ecs/lib/index.ts @@ -8,6 +8,7 @@ export * from './cluster'; export * from './ec2/ec2-service'; export * from './ec2/ec2-task-definition'; +export * from './ec2/ec2-event-rule-target'; export * from './fargate/fargate-service'; export * from './fargate/fargate-task-definition'; diff --git a/packages/@aws-cdk/aws-ecs/package.json b/packages/@aws-cdk/aws-ecs/package.json index d7d4c2daf22ec..da5c7fbc9edb6 100644 --- a/packages/@aws-cdk/aws-ecs/package.json +++ b/packages/@aws-cdk/aws-ecs/package.json @@ -71,6 +71,7 @@ "@aws-cdk/aws-cloudwatch": "^0.22.0", "@aws-cdk/aws-ec2": "^0.22.0", "@aws-cdk/aws-ecr": "^0.22.0", + "@aws-cdk/aws-events": "^0.22.0", "@aws-cdk/aws-elasticloadbalancing": "^0.22.0", "@aws-cdk/aws-elasticloadbalancingv2": "^0.22.0", "@aws-cdk/aws-iam": "^0.22.0", @@ -90,6 +91,7 @@ "@aws-cdk/aws-cloudwatch": "^0.22.0", "@aws-cdk/aws-ec2": "^0.22.0", "@aws-cdk/aws-ecr": "^0.22.0", + "@aws-cdk/aws-events": "^0.22.0", "@aws-cdk/aws-elasticloadbalancing": "^0.22.0", "@aws-cdk/aws-elasticloadbalancingv2": "^0.22.0", "@aws-cdk/aws-iam": "^0.22.0", 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 new file mode 100644 index 0000000000000..c3b9d654c394a --- /dev/null +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.event-task.lit.expected.json @@ -0,0 +1,1087 @@ +{ + "Resources": { + "Vpc8378EB38": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-ecs/Vpc" + } + ] + } + }, + "VpcPublicSubnet1Subnet5C2D37C4": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.0.0/17", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-ecs/Vpc/PublicSubnet1" + }, + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + } + ] + } + }, + "VpcPublicSubnet1RouteTable6C95E38E": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-ecs/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1RouteTableAssociation97140677": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + } + } + }, + "VpcPublicSubnet1DefaultRoute3DA9E72A": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VpcIGWD7BA715C" + } + }, + "DependsOn": [ + "VpcVPCGWBF912B6E" + ] + }, + "VpcPublicSubnet1EIPD7E02669": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc" + } + }, + "VpcPublicSubnet1NATGateway4D7517AA": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet1EIPD7E02669", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-ecs/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPrivateSubnet1Subnet536B997A": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.128.0/17", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-ecs/Vpc/PrivateSubnet1" + }, + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + } + ] + } + }, + "VpcPrivateSubnet1RouteTableB2C5B500": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-ecs/Vpc/PrivateSubnet1" + } + ] + } + }, + "VpcPrivateSubnet1RouteTableAssociation70C59FA6": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "SubnetId": { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + } + }, + "VpcPrivateSubnet1DefaultRouteBE02A9ED": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VpcPublicSubnet1NATGateway4D7517AA" + } + } + }, + "VpcIGWD7BA715C": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-ecs/Vpc" + } + ] + } + }, + "VpcVPCGWBF912B6E": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "InternetGatewayId": { + "Ref": "VpcIGWD7BA715C" + } + } + }, + "EcsCluster97242B84": { + "Type": "AWS::ECS::Cluster" + }, + "EcsClusterDefaultAutoScalingGroupInstanceSecurityGroup912E1231": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "aws-ecs-integ-ecs/EcsCluster/DefaultAutoScalingGroup/InstanceSecurityGroup", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "SecurityGroupIngress": [], + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-ecs/EcsCluster/DefaultAutoScalingGroup" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "EcsClusterDefaultAutoScalingGroupInstanceRole3C026863": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ec2.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "EcsClusterDefaultAutoScalingGroupInstanceRoleDefaultPolicy04DC6C80": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ecs:CreateCluster", + "ecs:DeregisterContainerInstance", + "ecs:DiscoverPollEndpoint", + "ecs:Poll", + "ecs:RegisterContainerInstance", + "ecs:StartTelemetrySession", + "ecs:Submit*", + "ecr:GetAuthorizationToken", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "EcsClusterDefaultAutoScalingGroupInstanceRoleDefaultPolicy04DC6C80", + "Roles": [ + { + "Ref": "EcsClusterDefaultAutoScalingGroupInstanceRole3C026863" + } + ] + } + }, + "EcsClusterDefaultAutoScalingGroupInstanceProfile2CE606B3": { + "Type": "AWS::IAM::InstanceProfile", + "Properties": { + "Roles": [ + { + "Ref": "EcsClusterDefaultAutoScalingGroupInstanceRole3C026863" + } + ] + } + }, + "EcsClusterDefaultAutoScalingGroupLaunchConfigB7E376C1": { + "Type": "AWS::AutoScaling::LaunchConfiguration", + "Properties": { + "ImageId": "ami-1234", + "InstanceType": "t2.micro", + "IamInstanceProfile": { + "Ref": "EcsClusterDefaultAutoScalingGroupInstanceProfile2CE606B3" + }, + "SecurityGroups": [ + { + "Fn::GetAtt": [ + "EcsClusterDefaultAutoScalingGroupInstanceSecurityGroup912E1231", + "GroupId" + ] + } + ], + "UserData": { + "Fn::Base64": { + "Fn::Join": [ + "", + [ + "#!/bin/bash\necho ECS_CLUSTER=", + { + "Ref": "EcsCluster97242B84" + }, + " >> /etc/ecs/ecs.config\nsudo iptables --insert FORWARD 1 --in-interface docker+ --destination 169.254.169.254/32 --jump DROP\nsudo service iptables save\necho ECS_AWSVPC_BLOCK_IMDS=true >> /etc/ecs/ecs.config" + ] + ] + } + } + }, + "DependsOn": [ + "EcsClusterDefaultAutoScalingGroupInstanceRole3C026863", + "EcsClusterDefaultAutoScalingGroupInstanceRoleDefaultPolicy04DC6C80" + ] + }, + "EcsClusterDefaultAutoScalingGroupASGC1A785DB": { + "Type": "AWS::AutoScaling::AutoScalingGroup", + "Properties": { + "MaxSize": "1", + "MinSize": "1", + "DesiredCapacity": "1", + "LaunchConfigurationName": { + "Ref": "EcsClusterDefaultAutoScalingGroupLaunchConfigB7E376C1" + }, + "Tags": [ + { + "Key": "Name", + "PropagateAtLaunch": true, + "Value": "aws-ecs-integ-ecs/EcsCluster/DefaultAutoScalingGroup" + } + ], + "VPCZoneIdentifier": [ + { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + ] + }, + "UpdatePolicy": { + "AutoScalingReplacingUpdate": { + "WillReplace": true + }, + "AutoScalingScheduledAction": { + "IgnoreUnmodifiedGroupSizeProperties": true + } + } + }, + "EcsClusterDefaultAutoScalingGroupDrainECSHookTopicC705BD25": { + "Type": "AWS::SNS::Topic" + }, + "EcsClusterDefaultAutoScalingGroupDrainECSHookTopicFunctionSubscription4313BD38": { + "Type": "AWS::SNS::Subscription", + "Properties": { + "Endpoint": { + "Fn::GetAtt": [ + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionE17A5F5E", + "Arn" + ] + }, + "Protocol": "lambda", + "TopicArn": { + "Ref": "EcsClusterDefaultAutoScalingGroupDrainECSHookTopicC705BD25" + } + } + }, + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRoleDefaultPolicyA45BF396": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "autoscaling:CompleteLifecycleAction", + "ec2:DescribeInstances", + "ec2:DescribeInstanceAttribute", + "ec2:DescribeInstanceStatus", + "ec2:DescribeHosts" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "ecs:ListContainerInstances", + "ecs:SubmitContainerStateChange", + "ecs:SubmitTaskStateChange", + "ecs:DescribeContainerInstances", + "ecs:UpdateContainerInstancesState", + "ecs:ListTasks", + "ecs:DescribeTasks" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRoleDefaultPolicyA45BF396", + "Roles": [ + { + "Ref": "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA" + } + ] + } + }, + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionE17A5F5E": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "import boto3, json, os, time\n\necs = boto3.client('ecs')\nautoscaling = boto3.client('autoscaling')\n\n\ndef lambda_handler(event, context):\n print(json.dumps(event))\n cluster = os.environ['CLUSTER']\n snsTopicArn = event['Records'][0]['Sns']['TopicArn']\n lifecycle_event = json.loads(event['Records'][0]['Sns']['Message'])\n instance_id = lifecycle_event.get('EC2InstanceId')\n if not instance_id:\n print('Got event without EC2InstanceId: %s', json.dumps(event))\n return\n\n instance_arn = container_instance_arn(cluster, instance_id)\n print('Instance %s has container instance ARN %s' % (lifecycle_event['EC2InstanceId'], instance_arn))\n\n if not instance_arn:\n return\n\n while has_tasks(cluster, instance_arn):\n time.sleep(10)\n\n try:\n print('Terminating instance %s' % instance_id)\n autoscaling.complete_lifecycle_action(\n LifecycleActionResult='CONTINUE',\n **pick(lifecycle_event, 'LifecycleHookName', 'LifecycleActionToken', 'AutoScalingGroupName'))\n except Exception as e:\n # Lifecycle action may have already completed.\n print(str(e))\n\n\ndef container_instance_arn(cluster, instance_id):\n \"\"\"Turn an instance ID into a container instance ARN.\"\"\"\n arns = ecs.list_container_instances(cluster=cluster, filter='ec2InstanceId==' + instance_id)['containerInstanceArns']\n if not arns:\n return None\n return arns[0]\n\n\ndef has_tasks(cluster, instance_arn):\n \"\"\"Return True if the instance is running tasks for the given cluster.\"\"\"\n instances = ecs.describe_container_instances(cluster=cluster, containerInstances=[instance_arn])['containerInstances']\n if not instances:\n return False\n instance = instances[0]\n\n if instance['status'] == 'ACTIVE':\n # Start draining, then try again later\n set_container_instance_to_draining(cluster, instance_arn)\n return True\n\n tasks = instance['runningTasksCount'] + instance['pendingTasksCount']\n print('Instance %s has %s tasks' % (instance_arn, tasks))\n\n return tasks > 0\n\n\ndef set_container_instance_to_draining(cluster, instance_arn):\n ecs.update_container_instances_state(\n cluster=cluster,\n containerInstances=[instance_arn], status='DRAINING')\n\n\ndef pick(dct, *keys):\n \"\"\"Pick a subset of a dict.\"\"\"\n return {k: v for k, v in dct.items() if k in keys}\n" + }, + "Handler": "index.lambda_handler", + "Role": { + "Fn::GetAtt": [ + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA", + "Arn" + ] + }, + "Runtime": "python3.6", + "Environment": { + "Variables": { + "CLUSTER": { + "Ref": "EcsCluster97242B84" + } + } + }, + "Timeout": 310 + }, + "DependsOn": [ + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA", + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRoleDefaultPolicyA45BF396" + ] + }, + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionTopicE6B1EBA6": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionE17A5F5E" + }, + "Principal": "sns.amazonaws.com", + "SourceArn": { + "Ref": "EcsClusterDefaultAutoScalingGroupDrainECSHookTopicC705BD25" + } + } + }, + "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleA38EC83B": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "autoscaling.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleDefaultPolicy75002F88": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sns:Publish", + "Effect": "Allow", + "Resource": { + "Ref": "EcsClusterDefaultAutoScalingGroupDrainECSHookTopicC705BD25" + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleDefaultPolicy75002F88", + "Roles": [ + { + "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleA38EC83B" + } + ] + } + }, + "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookFFA63029": { + "Type": "AWS::AutoScaling::LifecycleHook", + "Properties": { + "AutoScalingGroupName": { + "Ref": "EcsClusterDefaultAutoScalingGroupASGC1A785DB" + }, + "LifecycleTransition": "autoscaling:EC2_INSTANCE_TERMINATING", + "DefaultResult": "CONTINUE", + "HeartbeatTimeout": 300, + "NotificationTargetARN": { + "Ref": "EcsClusterDefaultAutoScalingGroupDrainECSHookTopicC705BD25" + }, + "RoleARN": { + "Fn::GetAtt": [ + "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleA38EC83B", + "Arn" + ] + } + }, + "DependsOn": [ + "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleA38EC83B", + "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleDefaultPolicy75002F88" + ] + }, + "TaskDefTaskRole1EDB4A67": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "TaskDef54694570": { + "Type": "AWS::ECS::TaskDefinition", + "Properties": { + "ContainerDefinitions": [ + { + "Essential": true, + "Image": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + ":", + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ecr:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":repository/", + { + "Fn::GetAtt": [ + "EventImageAdoptRepositoryDFAAC242", + "RepositoryName" + ] + } + ] + ] + } + ] + } + ] + }, + ".dkr.ecr.", + { + "Fn::Select": [ + 3, + { + "Fn::Split": [ + ":", + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ecr:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":repository/", + { + "Fn::GetAtt": [ + "EventImageAdoptRepositoryDFAAC242", + "RepositoryName" + ] + } + ] + ] + } + ] + } + ] + }, + ".amazonaws.com/", + { + "Fn::GetAtt": [ + "EventImageAdoptRepositoryDFAAC242", + "RepositoryName" + ] + }, + ":", + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + ":", + { + "Ref": "EventImageImageNameE972A8B1" + } + ] + } + ] + } + ] + ] + }, + "Links": [], + "LinuxParameters": { + "Capabilities": { + "Add": [], + "Drop": [] + }, + "Devices": [], + "Tmpfs": [] + }, + "LogConfiguration": { + "LogDriver": "awslogs", + "Options": { + "awslogs-group": { + "Ref": "TaskLoggingLogGroupC7E938D4" + }, + "awslogs-stream-prefix": "EventDemo", + "awslogs-region": { + "Ref": "AWS::Region" + } + } + }, + "Memory": 256, + "MountPoints": [], + "Name": "TheContainer", + "PortMappings": [], + "Ulimits": [], + "VolumesFrom": [] + } + ], + "ExecutionRoleArn": { + "Fn::GetAtt": [ + "TaskDefExecutionRoleB4775C97", + "Arn" + ] + }, + "Family": "awsecsintegecsTaskDef8DD0C801", + "NetworkMode": "bridge", + "PlacementConstraints": [], + "RequiresCompatibilities": [ + "EC2" + ], + "TaskRoleArn": { + "Fn::GetAtt": [ + "TaskDefTaskRole1EDB4A67", + "Arn" + ] + }, + "Volumes": [] + } + }, + "TaskDefExecutionRoleB4775C97": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "TaskDefExecutionRoleDefaultPolicy0DBB737A": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ecr:BatchCheckLayerAvailability", + "ecr:GetDownloadUrlForLayer", + "ecr:BatchGetImage" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ecr:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":repository/", + { + "Fn::GetAtt": [ + "EventImageAdoptRepositoryDFAAC242", + "RepositoryName" + ] + } + ] + ] + } + }, + { + "Action": [ + "ecr:GetAuthorizationToken", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "TaskLoggingLogGroupC7E938D4", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "TaskDefExecutionRoleDefaultPolicy0DBB737A", + "Roles": [ + { + "Ref": "TaskDefExecutionRoleB4775C97" + } + ] + } + }, + "TaskDefEventsRoleFB3B67B8": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "events.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "TaskDefEventsRoleDefaultPolicyA124E85B": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "ecs:RunTask", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + }, + "Effect": "Allow", + "Resource": { + "Ref": "TaskDef54694570" + } + }, + { + "Action": "iam:PassRole", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "TaskDefTaskRole1EDB4A67", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "TaskDefEventsRoleDefaultPolicyA124E85B", + "Roles": [ + { + "Ref": "TaskDefEventsRoleFB3B67B8" + } + ] + } + }, + "EventImageAdoptRepositoryDFAAC242": { + "Type": "Custom::ECRAdoptedRepository", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c52BE89E9", + "Arn" + ] + }, + "RepositoryName": { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + ":", + { + "Ref": "EventImageImageNameE972A8B1" + } + ] + } + ] + } + } + }, + "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ecr:GetRepositoryPolicy", + "ecr:SetRepositoryPolicy", + "ecr:DeleteRepository", + "ecr:ListImages", + "ecr:BatchDeleteImage" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ecr:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":repository/", + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + ":", + { + "Ref": "EventImageImageNameE972A8B1" + } + ] + } + ] + } + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", + "Roles": [ + { + "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" + } + ] + } + }, + "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c52BE89E9": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3Bucket92AB06B6" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276" + } + ] + } + ] + } + ] + ] + } + }, + "Handler": "handler.handler", + "Role": { + "Fn::GetAtt": [ + "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17", + "Arn" + ] + }, + "Runtime": "nodejs8.10", + "Timeout": 300 + }, + "DependsOn": [ + "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17", + "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C" + ] + }, + "TaskLoggingLogGroupC7E938D4": { + "Type": "AWS::Logs::LogGroup", + "Properties": { + "RetentionInDays": 365 + }, + "DeletionPolicy": "Retain" + }, + "Rule4C995B7F": { + "Type": "AWS::Events::Rule", + "Properties": { + "ScheduleExpression": "rate(1 minute)", + "State": "ENABLED", + "Targets": [ + { + "Arn": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + }, + "EcsParameters": { + "TaskCount": 1, + "TaskDefinitionArn": { + "Ref": "TaskDef54694570" + } + }, + "Id": "EventTarget", + "InputTransformer": { + "InputTemplate": "{\"containerOverrides\":[{\"name\":\"TheContainer\",\"environment\":[{\"name\":\"I_WAS_TRIGGERED\",\"value\":\"From CloudWatch Events\"}]}]}" + }, + "RoleArn": { + "Fn::GetAtt": [ + "TaskDefEventsRoleFB3B67B8", + "Arn" + ] + } + } + ] + } + } + }, + "Parameters": { + "EventImageImageNameE972A8B1": { + "Type": "String", + "Description": "ECR repository name and tag asset \"aws-ecs-integ-ecs/EventImage\"" + }, + "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3Bucket92AB06B6": { + "Type": "String", + "Description": "S3 bucket for asset \"aws-ecs-integ-ecs/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" + }, + "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276": { + "Type": "String", + "Description": "S3 key for asset version \"aws-ecs-integ-ecs/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.event-task.lit.ts b/packages/@aws-cdk/aws-ecs/test/ec2/integ.event-task.lit.ts new file mode 100644 index 0000000000000..0683524d92eb7 --- /dev/null +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.event-task.lit.ts @@ -0,0 +1,54 @@ +import ec2 = require('@aws-cdk/aws-ec2'); +import events = require('@aws-cdk/aws-events'); +import cdk = require('@aws-cdk/cdk'); +import ecs = require('../../lib'); + +const app = new cdk.App(); + +class EventStack extends cdk.Stack { + constructor(scope: cdk.App, id: string) { + super(scope, id); + + const vpc = new ec2.VpcNetwork(this, 'Vpc', { maxAZs: 1 }); + + const cluster = new ecs.Cluster(this, 'EcsCluster', { vpc }); + cluster.addDefaultAutoScalingGroupCapacity({ + instanceType: new ec2.InstanceType('t2.micro') + }); + + /// !show + // Create a Task Definition for the container to start + const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef'); + taskDefinition.addContainer('TheContainer', { + image: ecs.ContainerImage.fromAsset(this, 'EventImage', { directory: 'eventhandler-image' }), + memoryLimitMiB: 256, + logging: new ecs.AwsLogDriver(this, 'TaskLogging', { streamPrefix: 'EventDemo' }) + }); + + // An EventRule that describes the event trigger (in this case a scheduled run) + const rule = new events.EventRule(this, 'Rule', { + scheduleExpression: 'rate(1 minute)', + }); + + // Use Ec2TaskEventRuleTarget as the target of the EventRule + const target = new ecs.Ec2EventRuleTarget(this, 'EventTarget', { + cluster, + taskDefinition, + taskCount: 1 + }); + + // Pass an environment variable to the container 'TheContainer' in the task + rule.addTarget(target, { + jsonTemplate: JSON.stringify({ + containerOverrides: [{ + name: 'TheContainer', + environment: [{ name: 'I_WAS_TRIGGERED', value: 'From CloudWatch Events' }] + }] + }) + }); + /// !hide + } +} + +new EventStack(app, 'aws-ecs-integ-ecs'); +app.run(); diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-event-rule-target.ts b/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-event-rule-target.ts new file mode 100644 index 0000000000000..cafb405ffd62e --- /dev/null +++ b/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-event-rule-target.ts @@ -0,0 +1,61 @@ +import { expect, haveResource } from '@aws-cdk/assert'; +import ec2 = require('@aws-cdk/aws-ec2'); +import events = require('@aws-cdk/aws-events'); +import cdk = require('@aws-cdk/cdk'); +import { Test } from 'nodeunit'; +import ecs = require('../../lib'); + +export = { + "Can use EC2 taskdef as EventRule target"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.VpcNetwork(stack, 'Vpc', { maxAZs: 1 }); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + cluster.addDefaultAutoScalingGroupCapacity({ + instanceType: new ec2.InstanceType('t2.micro') + }); + + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef'); + taskDefinition.addContainer('TheContainer', { + image: ecs.ContainerImage.fromDockerHub('henk'), + memoryLimitMiB: 256 + }); + + const rule = new events.EventRule(stack, 'Rule', { + scheduleExpression: 'rate(1 minute)', + }); + + // WHEN + const target = new ecs.Ec2EventRuleTarget(stack, 'EventTarget', { + cluster, + taskDefinition, + taskCount: 1 + }); + + rule.addTarget(target, { + jsonTemplate: { + argument: 'hello' + } + }); + + // THEN + expect(stack).to(haveResource('AWS::Events::Rule', { + Targets: [ + { + Arn: { "Fn::GetAtt": ["EcsCluster97242B84", "Arn"] }, + EcsParameters: { + TaskCount: 1, + TaskDefinitionArn: { Ref: "TaskDef54694570" } + }, + Id: "EventTarget", + InputTransformer: { + InputTemplate: "{\"argument\":\"hello\"}" + }, + RoleArn: { "Fn::GetAtt": ["TaskDefEventsRoleFB3B67B8", "Arn"] } + } + ] + })); + + test.done(); + } +}; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecs/test/eventhandler-image/Dockerfile b/packages/@aws-cdk/aws-ecs/test/eventhandler-image/Dockerfile new file mode 100644 index 0000000000000..123b5670febc8 --- /dev/null +++ b/packages/@aws-cdk/aws-ecs/test/eventhandler-image/Dockerfile @@ -0,0 +1,5 @@ +FROM python:3.6 +EXPOSE 8000 +WORKDIR /src +ADD . /src +CMD python3 index.py diff --git a/packages/@aws-cdk/aws-ecs/test/eventhandler-image/index.py b/packages/@aws-cdk/aws-ecs/test/eventhandler-image/index.py new file mode 100644 index 0000000000000..c4cab119afc2d --- /dev/null +++ b/packages/@aws-cdk/aws-ecs/test/eventhandler-image/index.py @@ -0,0 +1,6 @@ +#!/usr/bin/python +import os +import pprint + +print('Hello from ECS!') +pprint.pprint(dict(os.environ)) diff --git a/packages/@aws-cdk/aws-iam/lib/role.ts b/packages/@aws-cdk/aws-iam/lib/role.ts index 3f83f67c401e4..234871891fb5b 100644 --- a/packages/@aws-cdk/aws-iam/lib/role.ts +++ b/packages/@aws-cdk/aws-iam/lib/role.ts @@ -206,6 +206,26 @@ export class Role extends Construct implements IRole { this.attachedPolicies.attach(policy); policy.attachToRole(this); } + + /** + * Grant the actions defined in actions to the identity Principal on this resource. + */ + public grant(identity?: IPrincipal, ...actions: string[]) { + if (!identity) { + return; + } + + identity.addToPolicy(new PolicyStatement() + .addResource(this.roleArn) + .addActions(...actions)); + } + + /** + * Grant permissions to the given principal to pass this role. + */ + public grantPassRole(identity?: IPrincipal) { + this.grant(identity, 'iam:PassRole'); + } } /** diff --git a/packages/@aws-cdk/aws-iam/test/test.role.ts b/packages/@aws-cdk/aws-iam/test/test.role.ts index 73f92671383ee..ee4801db0ec78 100644 --- a/packages/@aws-cdk/aws-iam/test/test.role.ts +++ b/packages/@aws-cdk/aws-iam/test/test.role.ts @@ -1,7 +1,7 @@ -import { expect, haveResource } from '@aws-cdk/assert'; +import { expect, haveResource, haveResourceLike } from '@aws-cdk/assert'; import { Resource, Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; -import { ArnPrincipal, CompositePrincipal, FederatedPrincipal, PolicyStatement, Role, ServicePrincipal } from '../lib'; +import { ArnPrincipal, CompositePrincipal, FederatedPrincipal, PolicyStatement, Role, ServicePrincipal, User } from '../lib'; export = { 'default role'(test: Test) { @@ -24,6 +24,32 @@ export = { test.done(); }, + 'a role can grant PassRole permissions'(test: Test) { + // GIVEN + const stack = new Stack(); + const role = new Role(stack, 'Role', { assumedBy: new ServicePrincipal('henk.amazonaws.com') }); + const user = new User(stack, 'User'); + + // WHEN + role.grantPassRole(user); + + // THEN + expect(stack).to(haveResourceLike('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: "iam:PassRole", + Effect: "Allow", + Resource: { "Fn::GetAtt": [ "Role1ABCC5F0", "Arn" ] } + } + ], + Version: "2012-10-17" + }, + })); + + test.done(); + }, + 'can supply externalId'(test: Test) { // GIVEN const stack = new Stack(); diff --git a/packages/aws-cdk/lib/docker.ts b/packages/aws-cdk/lib/docker.ts index 18f8a7bb0019c..811ada8baed1b 100644 --- a/packages/aws-cdk/lib/docker.ts +++ b/packages/aws-cdk/lib/docker.ts @@ -102,6 +102,13 @@ async function calculateImageFingerprint(imageId: string) { // We're not interested in the Docker version used to create this image delete manifest.DockerVersion; + // On some Docker versions Metadata contains a LastTagTime which updates + // on every push, causing us to miss all cache hits. + delete manifest.Metadata; + + // GraphDriver is about running the image, not about the image itself. + delete manifest.GraphDriver; + return crypto.createHash('sha256').update(JSON.stringify(manifest)).digest('hex'); } From 2cbd2c016cfb600ff41ebaa9b4e9516bab7ece9e Mon Sep 17 00:00:00 2001 From: "Michael S. Fischer" Date: Mon, 4 Feb 2019 04:59:10 -0800 Subject: [PATCH 26/38] feat(app): add source map support to TS app template (#1581) Add source map support to the TypeScript app entry point template. This won't retroactively fix existing CDK apps, but it will ensure that apps generated in the future via `cdk init --app` will have it enabled. Fixes #1579 --- .../lib/init-templates/app/typescript/bin/%name%.template.ts | 1 + .../lib/init-templates/app/typescript/package.template.json | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/aws-cdk/lib/init-templates/app/typescript/bin/%name%.template.ts b/packages/aws-cdk/lib/init-templates/app/typescript/bin/%name%.template.ts index 985ea7add8f96..4e712351b19ce 100644 --- a/packages/aws-cdk/lib/init-templates/app/typescript/bin/%name%.template.ts +++ b/packages/aws-cdk/lib/init-templates/app/typescript/bin/%name%.template.ts @@ -1,4 +1,5 @@ #!/usr/bin/env node +import 'source-map-support/register'; import cdk = require('@aws-cdk/cdk'); import { %name.PascalCased%Stack } from '../lib/%name%-stack'; diff --git a/packages/aws-cdk/lib/init-templates/app/typescript/package.template.json b/packages/aws-cdk/lib/init-templates/app/typescript/package.template.json index a31c23f15a814..c705b158b8883 100644 --- a/packages/aws-cdk/lib/init-templates/app/typescript/package.template.json +++ b/packages/aws-cdk/lib/init-templates/app/typescript/package.template.json @@ -15,6 +15,7 @@ "aws-cdk": "^%cdk-version%" }, "dependencies": { - "@aws-cdk/cdk": "^%cdk-version%" + "@aws-cdk/cdk": "^%cdk-version%", + "source-map-support": "^0.5.9" } } From e3f5ad3887bb1ebcce2eb60a2953e27c31baabfd Mon Sep 17 00:00:00 2001 From: Romain Marcadier-Muller Date: Mon, 4 Feb 2019 14:43:19 +0100 Subject: [PATCH 27/38] fix(apig): Move 'selectionPattern` to `integrationResponses` (#1636) The property was modeled in the wrong place, rendering it unusable. Moved it to the correct location instead, and added a validation test. Fixes #1608 --- .../aws-apigateway/lib/integration.ts | 20 ++++---- .../aws-apigateway/test/test.method.ts | 46 +++++++++++++++++++ 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/packages/@aws-cdk/aws-apigateway/lib/integration.ts b/packages/@aws-cdk/aws-apigateway/lib/integration.ts index 3214f931da67a..1f47868e186f1 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/integration.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/integration.ts @@ -86,15 +86,6 @@ export interface IntegrationOptions { */ integrationResponses?: IntegrationResponse[]; - /** - * The templates that are used to transform the integration response body. - * Specify templates as key-value pairs (string-to-string mappings), with a - * content type as the key and a template as the value. - * - * @see http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html - */ - selectionPattern?: string; - /** * The type of network connection to the integration endpoint. * @default ConnectionType.Internet @@ -243,6 +234,17 @@ export enum ConnectionType { } export interface IntegrationResponse { + /** + * Specifies the regular expression (regex) pattern used to choose an integration response based on the response from + * the back end. For example, if the success response returns nothing and the error response returns some string, you + * could use the ``.+`` regex to match error response. However, make sure that the error response does not contain any + * newline (``\n``) character in such cases. If the back end is an AWS Lambda function, the AWS Lambda function error + * header is matched. For all other HTTP and AWS back ends, the HTTP status code is matched. + * + * @see https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-integration-settings-integration-response.html + */ + selectionPattern?: string; + /** * The status code that API Gateway uses to map the integration response to * a MethodResponse status code. diff --git a/packages/@aws-cdk/aws-apigateway/test/test.method.ts b/packages/@aws-cdk/aws-apigateway/test/test.method.ts index 645d1f9f9f398..c1e0ee124b1d3 100644 --- a/packages/@aws-cdk/aws-apigateway/test/test.method.ts +++ b/packages/@aws-cdk/aws-apigateway/test/test.method.ts @@ -302,5 +302,51 @@ export = { // THEN test.throws(() => api.root.addMethod('GET', integration), /cannot set 'vpcLink' where 'connectionType' is INTERNET/); test.done(); + }, + + 'multiple integration responses can be used'(test: Test) { // @see https://github.com/awslabs/aws-cdk/issues/1608 + // GIVEN + const stack = new cdk.Stack(); + const api = new apigateway.RestApi(stack, 'test-api', { deploy: false }); + + // WHEN + api.root.addMethod('GET', new apigateway.AwsIntegration({ + service: 'foo-service', + action: 'BarAction', + options: { + integrationResponses: [ + { + statusCode: '200', + responseTemplates: { 'application/json': JSON.stringify({ success: true }) }, + }, + { + selectionPattern: 'Invalid', + statusCode: '503', + responseTemplates: { 'application/json': JSON.stringify({ success: false, message: 'Invalid Request' }) }, + } + ], + } + })); + + // THEN + expect(stack).to(haveResource('AWS::ApiGateway::Method', { + Integration: { + IntegrationHttpMethod: 'POST', + IntegrationResponses: [ + { + ResponseTemplates: { 'application/json': '{"success":true}' }, + StatusCode: '200', + }, + { + ResponseTemplates: { 'application/json': '{"success":false,"message":"Invalid Request"}' }, + SelectionPattern: 'Invalid', + StatusCode: '503', + } + ], + Type: 'AWS', + Uri: { 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':apigateway:', { Ref: 'AWS::Region' }, ':foo-service:action/BarAction']]} + } + })); + test.done(); } }; From f706c3dd09a5299730e71c92dd739db3ea4f537c Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Mon, 4 Feb 2019 14:46:47 +0100 Subject: [PATCH 28/38] feat(core): generalization of dependencies (#1583) Constructs can now take a dependency on any other construct. Before, only `Resource`s could take dependencies, and they would depend on `IDependable` which had to be implemented explicitly. In this change we generalize the concept of dependencies from construct trees to other construct trees; all constructs now take dependencies and also implement `IDependable`. The semantics are that any resource in the depending tree will depend on all resources in the depended tree. Dependencies are cross-stack aware If you take a dependency on a construct in another stack, the dependency does not get rendered in the template, but is instead added as a dependency between stacks. Fixes #1568, fixes #95. BREAKING CHANGE: `resource.addDependency()` has been moved onto `ConstructNode`. You now write `resource.node.addDependency()`. VPC's `internetDependency` has been moved to the subnets as `internetConnectivityEstablished`. Target Group's `loadBalancerAssociationDependencies` has been renamed to `loadBalancerAttached`. --- .../custom-resource/index.ts | 6 +- .../resource-overrides/index.ts | 2 +- .../@aws-cdk/aws-apigateway/lib/deployment.ts | 20 +-- .../@aws-cdk/aws-apigateway/lib/method.ts | 2 +- .../@aws-cdk/aws-apigateway/lib/resource.ts | 2 +- .../@aws-cdk/aws-apigateway/lib/restapi.ts | 18 +- packages/@aws-cdk/aws-apigateway/lib/stage.ts | 4 +- .../test/integ.restapi.books.expected.json | 8 +- .../test/integ.restapi.defaults.expected.json | 4 +- .../test/integ.restapi.expected.json | 14 +- .../aws-apigateway/test/test.deployment.ts | 2 +- .../aws-apigateway/test/test.restapi.ts | 169 +++++++----------- .../aws-autoscaling/lib/auto-scaling-group.ts | 6 +- .../aws-autoscaling/lib/lifecycle-hook.ts | 8 +- .../lib/target-tracking-scaling-policy.ts | 17 +- packages/@aws-cdk/aws-autoscaling/lib/util.ts | 12 -- ...g.asg-w-classic-loadbalancer.expected.json | 6 +- .../test/integ.asg-w-elbv2.expected.json | 8 +- .../test/test.pipeline-actions.ts | 1 + packages/@aws-cdk/aws-cloudtrail/lib/index.ts | 2 +- .../test/integ.deployment-group.expected.json | 6 +- .../@aws-cdk/aws-codepipeline/lib/pipeline.ts | 2 +- .../test/integ.lambda-pipeline.expected.json | 4 +- packages/@aws-cdk/aws-ec2/lib/vpc-ref.ts | 53 ++---- packages/@aws-cdk/aws-ec2/lib/vpc.ts | 33 ++-- .../@aws-cdk/aws-ecs/lib/base/base-service.ts | 12 +- .../test/ec2/integ.lb-awsvpc-nw.expected.json | 8 +- .../test/ec2/integ.lb-bridge-nw.expected.json | 8 +- .../fargate/integ.asset-image.expected.json | 8 +- .../test/fargate/integ.l3.expected.json | 8 +- .../fargate/integ.lb-awsvpc-nw.expected.json | 8 +- .../lib/load-balancer.ts | 2 +- .../test/integ.elb.expected.json | 4 +- .../alb/application-listener-certificate.ts | 11 +- .../lib/alb/application-listener-rule.ts | 8 +- .../lib/alb/application-listener.ts | 3 +- .../lib/alb/application-target-group.ts | 14 +- .../lib/nlb/network-listener.ts | 4 +- .../lib/nlb/network-target-group.ts | 7 +- .../lib/shared/base-listener.ts | 4 +- .../lib/shared/base-load-balancer.ts | 2 +- .../lib/shared/base-target-group.ts | 31 ++-- .../lib/shared/imported.ts | 10 +- .../lib/shared/util.ts | 13 -- .../test/alb/test.listener.ts | 13 +- .../test/alb/test.load-balancer.ts | 6 +- .../test/integ.alb-alias-target.expected.json | 5 +- .../test/integ.alb.expected.json | 5 +- .../test/integ.nlb.expected.json | 9 +- .../test/integ.nlb.ts | 2 +- .../test/nlb/test.listener.ts | 21 ++- packages/@aws-cdk/aws-iam/lib/lazy-role.ts | 7 - packages/@aws-cdk/aws-iam/lib/policy.ts | 10 +- packages/@aws-cdk/aws-iam/lib/role.ts | 12 +- packages/@aws-cdk/aws-iam/test/test.role.ts | 13 +- packages/@aws-cdk/aws-lambda/lib/lambda.ts | 2 +- packages/@aws-cdk/aws-rds/lib/cluster.ts | 10 +- .../aws-rds/test/integ.cluster.expected.json | 12 +- .../notifications-resource.ts | 2 +- .../aws-s3/test/test.notifications.ts | 1 + packages/@aws-cdk/aws-sns/lib/policy.ts | 13 +- packages/@aws-cdk/aws-sqs/lib/policy.ts | 13 +- .../integ.bucket-notifications.expected.json | 4 +- .../cdk/lib/cloudformation/resource.ts | 67 +++---- .../cdk/lib/cloudformation/stack-element.ts | 23 +-- .../@aws-cdk/cdk/lib/cloudformation/stack.ts | 48 ++++- packages/@aws-cdk/cdk/lib/core/construct.ts | 63 ++++++- packages/@aws-cdk/cdk/lib/core/dependency.ts | 43 +++++ packages/@aws-cdk/cdk/lib/core/util.ts | 2 +- packages/@aws-cdk/cdk/lib/index.ts | 1 + .../test/cloudformation/test.logical-id.ts | 3 +- .../cdk/test/cloudformation/test.resource.ts | 77 ++++---- .../cloudformation-diff/lib/diff/index.ts | 1 + 73 files changed, 498 insertions(+), 564 deletions(-) delete mode 100644 packages/@aws-cdk/aws-autoscaling/lib/util.ts create mode 100644 packages/@aws-cdk/cdk/lib/core/dependency.ts diff --git a/examples/cdk-examples-typescript/custom-resource/index.ts b/examples/cdk-examples-typescript/custom-resource/index.ts index 2414e1a968a4a..29a1656d7ffce 100644 --- a/examples/cdk-examples-typescript/custom-resource/index.ts +++ b/examples/cdk-examples-typescript/custom-resource/index.ts @@ -16,8 +16,7 @@ interface DemoResourceProps { failCreate?: boolean; } -class DemoResource extends cdk.Construct implements cdk.IDependable { - public readonly dependencyElements: cdk.IDependable[]; +class DemoResource extends cdk.Construct { public readonly response: string; constructor(scope: cdk.Construct, id: string, props: DemoResourceProps) { @@ -36,7 +35,6 @@ class DemoResource extends cdk.Construct implements cdk.IDependable { }); this.response = resource.getAtt('Response').toString(); - this.dependencyElements = [resource]; } } @@ -91,7 +89,7 @@ class FailAfterCreatingStack extends cdk.Stack { }); // Make sure the rollback gets triggered only after the custom resource has been fully created. - bucket.addDependency(resource); + bucket.node.addDependency(resource); } } diff --git a/examples/cdk-examples-typescript/resource-overrides/index.ts b/examples/cdk-examples-typescript/resource-overrides/index.ts index 35fa7067a138e..6b054bd6bd533 100644 --- a/examples/cdk-examples-typescript/resource-overrides/index.ts +++ b/examples/cdk-examples-typescript/resource-overrides/index.ts @@ -31,7 +31,7 @@ class ResourceOverridesExample extends cdk.Stack { // This is how to specify resource options such as dependencies, metadata, update policy // - bucketResource.addDependency(otherBucket.node.findChild('Resource') as cdk.Resource); + bucketResource.node.addDependency(otherBucket.node.findChild('Resource') as cdk.Resource); bucketResource.options.metadata = { MetadataKey: 'MetadataValue' }; bucketResource.options.updatePolicy = { autoScalingRollingUpdate: { diff --git a/packages/@aws-cdk/aws-apigateway/lib/deployment.ts b/packages/@aws-cdk/aws-apigateway/lib/deployment.ts index 4aef0dd03306a..49c31a5a0804b 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/deployment.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/deployment.ts @@ -51,18 +51,13 @@ export interface DeploymentProps { * Furthermore, since a deployment does not reference any of the REST API * resources and methods, CloudFormation will likely provision it before these * resources are created, which means that it will represent a "half-baked" - * model. Use the `addDependency(dep)` method to circumvent that. This is done + * model. Use the `node.addDependency(dep)` method to circumvent that. This is done * automatically for the `restApi.latestDeployment` deployment. */ -export class Deployment extends cdk.Construct implements cdk.IDependable { +export class Deployment extends cdk.Construct { public readonly deploymentId: string; public readonly api: IRestApi; - /** - * Allows taking a dependency on this construct. - */ - public readonly dependencyElements = new Array(); - private readonly resource: LatestDeploymentResource; constructor(scope: cdk.Construct, id: string, props: DeploymentProps) { @@ -79,15 +74,6 @@ export class Deployment extends cdk.Construct implements cdk.IDependable { this.api = props.api; this.deploymentId = new cdk.Token(() => this.resource.deploymentId).toString(); - this.dependencyElements.push(this.resource); - } - - /** - * Adds a dependency for this deployment. Should be called by all resources and methods - * so they are provisioned before this Deployment. - */ - public addDependency(dep: cdk.IDependable) { - this.resource.addDependency(dep); } /** @@ -182,5 +168,7 @@ class LatestDeploymentResource extends CfnDeployment { this.lazyLogicalId = this.originalLogicalId + md5.digest("hex"); } + + super.prepare(); } } diff --git a/packages/@aws-cdk/aws-apigateway/lib/method.ts b/packages/@aws-cdk/aws-apigateway/lib/method.ts index cb263bc5083cc..1e76106409f23 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/method.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/method.ts @@ -104,7 +104,7 @@ export class Method extends cdk.Construct { const deployment = props.resource.resourceApi.latestDeployment; if (deployment) { - deployment.addDependency(resource); + deployment.node.addDependency(resource); deployment.addToLogicalId({ method: methodProps }); } } diff --git a/packages/@aws-cdk/aws-apigateway/lib/resource.ts b/packages/@aws-cdk/aws-apigateway/lib/resource.ts index 18280598ffaee..77f4a438a8fba 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/resource.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/resource.ts @@ -118,7 +118,7 @@ export class Resource extends cdk.Construct implements IRestApiResource { const deployment = props.parent.resourceApi.latestDeployment; if (deployment) { - deployment.addDependency(resource); + deployment.node.addDependency(resource); deployment.addToLogicalId({ resource: resourceProps }); } diff --git a/packages/@aws-cdk/aws-apigateway/lib/restapi.ts b/packages/@aws-cdk/aws-apigateway/lib/restapi.ts index ea6ed72213356..852e0eb8a0ad4 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/restapi.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/restapi.ts @@ -154,7 +154,7 @@ export interface RestApiProps extends ResourceOptions { * By default, the API will automatically be deployed and accessible from a * public endpoint. */ -export class RestApi extends cdk.Construct implements cdk.IDependable, IRestApi { +export class RestApi extends cdk.Construct implements IRestApi { /** * Imports an existing REST API resource. * @param parent Parent construct @@ -177,11 +177,6 @@ export class RestApi extends cdk.Construct implements cdk.IDependable, IRestApi */ public latestDeployment?: Deployment; - /** - * Allows taking a dependency on this construct. - */ - public readonly dependencyElements = new Array(); - /** * API Gateway stage that points to the latest deployment (if defined). * @@ -226,16 +221,9 @@ export class RestApi extends cdk.Construct implements cdk.IDependable, IRestApi this.configureCloudWatchRole(resource); } - this.dependencyElements.push(resource); - if (this.latestDeployment) { - this.dependencyElements.push(this.latestDeployment); - } - if (this.deploymentStage) { - this.dependencyElements.push(this.deploymentStage); - } - // configure the "root" resource this.root = { + get dependencyRoots() { return [this]; }, node: this.node, addResource: (pathPart: string, options?: ResourceOptions) => { return new Resource(this, pathPart, { parent: this.root, pathPart, ...options }); @@ -372,7 +360,7 @@ export class RestApi extends cdk.Construct implements cdk.IDependable, IRestApi cloudWatchRoleArn: role.roleArn }); - resource.addDependency(apiResource); + resource.node.addDependency(apiResource); } } diff --git a/packages/@aws-cdk/aws-apigateway/lib/stage.ts b/packages/@aws-cdk/aws-apigateway/lib/stage.ts index 45bad2898c1eb..5907d12f435f1 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/stage.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/stage.ts @@ -133,9 +133,8 @@ export interface MethodDeploymentOptions { cacheDataEncrypted?: boolean; } -export class Stage extends cdk.Construct implements cdk.IDependable { +export class Stage extends cdk.Construct { public readonly stageName: string; - public readonly dependencyElements = new Array(); private readonly restApi: IRestApi; @@ -170,7 +169,6 @@ export class Stage extends cdk.Construct implements cdk.IDependable { this.stageName = resource.ref; this.restApi = props.deployment.api; - this.dependencyElements.push(resource); } /** 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 acdcd09664d43..7ffe53ec51c08 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 @@ -530,8 +530,8 @@ "booksapibooksGETA776447A", "booksapibooksPOSTF6C6559D", "booksapibooksbookid5264BCA2", - "booksapibooksbookidGETCCE21986", - "booksapibooksbookidDELETE214F4059" + "booksapibooksbookidDELETE214F4059", + "booksapibooksbookidGETCCE21986" ] }, "booksapiDeploymentStageprod55D8E03E": { @@ -837,7 +837,9 @@ "Ref": "AWS::Region" }, ".", - { "Ref": "AWS::URLSuffix" }, + { + "Ref": "AWS::URLSuffix" + }, "/", { "Ref": "booksapiDeploymentStageprod55D8E03E" diff --git a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.defaults.expected.json b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.defaults.expected.json index 888fa29a306be..59d30b58265f6 100644 --- a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.defaults.expected.json +++ b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.defaults.expected.json @@ -110,7 +110,9 @@ "Ref": "AWS::Region" }, ".", - { "Ref": "AWS::URLSuffix" }, + { + "Ref": "AWS::URLSuffix" + }, "/", { "Ref": "myapiDeploymentStageprod298F01AF" 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 3af25751a5649..6bf7d6bcb6c7f 100644 --- a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.expected.json +++ b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.expected.json @@ -16,15 +16,15 @@ }, "DependsOn": [ "myapiv113487378", - "myapiv1toysA55FCBC4", - "myapiv1toysGET7348114D", - "myapiv1toysPOST55128058", - "myapiv1toysPUT59AFBBC2", "myapiv1appliances507FEFF4", "myapiv1appliancesGET8FE872EC", "myapiv1books1D4BE6C1", "myapiv1booksGETC6B996D0", - "myapiv1booksPOST53E2832E" + "myapiv1booksPOST53E2832E", + "myapiv1toysA55FCBC4", + "myapiv1toysGET7348114D", + "myapiv1toysPOST55128058", + "myapiv1toysPUT59AFBBC2" ], "DeletionPolicy": "Retain" }, @@ -602,7 +602,9 @@ "Ref": "AWS::Region" }, ".", - { "Ref": "AWS::URLSuffix" }, + { + "Ref": "AWS::URLSuffix" + }, "/", { "Ref": "myapiDeploymentStagebeta96434BEB" diff --git a/packages/@aws-cdk/aws-apigateway/test/test.deployment.ts b/packages/@aws-cdk/aws-apigateway/test/test.deployment.ts index aaec885005146..09763db2942e1 100644 --- a/packages/@aws-cdk/aws-apigateway/test/test.deployment.ts +++ b/packages/@aws-cdk/aws-apigateway/test/test.deployment.ts @@ -167,7 +167,7 @@ export = { const dep = new cdk.Resource(stack, 'MyResource', { type: 'foo' }); // WHEN - deployment.addDependency(dep); + deployment.node.addDependency(dep); expect(stack).to(haveResource('AWS::ApiGateway::Deployment', { DependsOn: [ "MyResource" ], diff --git a/packages/@aws-cdk/aws-apigateway/test/test.restapi.ts b/packages/@aws-cdk/aws-apigateway/test/test.restapi.ts index 0810e8938c67b..8af12724e391c 100644 --- a/packages/@aws-cdk/aws-apigateway/test/test.restapi.ts +++ b/packages/@aws-cdk/aws-apigateway/test/test.restapi.ts @@ -19,125 +19,81 @@ export = { expect(stack).toMatch({ Resources: { myapi4C7BF186: { - Type: "AWS::ApiGateway::RestApi", - Properties: { - Name: "my-api" - } + Type: "AWS::ApiGateway::RestApi", + Properties: { + Name: "my-api" + } }, myapiGETF990CE3C: { - Type: "AWS::ApiGateway::Method", - Properties: { - HttpMethod: "GET", - ResourceId: { - "Fn::GetAtt": [ - "myapi4C7BF186", - "RootResourceId" - ] - }, - RestApiId: { - Ref: "myapi4C7BF186" - }, - AuthorizationType: "NONE", - Integration: { - Type: "MOCK" + Type: "AWS::ApiGateway::Method", + Properties: { + HttpMethod: "GET", + ResourceId: { "Fn::GetAtt": [ "myapi4C7BF186", "RootResourceId" ] }, + RestApiId: { Ref: "myapi4C7BF186" }, + AuthorizationType: "NONE", + Integration: { + Type: "MOCK" + } } - } }, myapiDeployment92F2CB49916eaecf87f818f1e175215b8d086029: { - Type: "AWS::ApiGateway::Deployment", - Properties: { - RestApiId: { - Ref: "myapi4C7BF186" + Type: "AWS::ApiGateway::Deployment", + Properties: { + RestApiId: { Ref: "myapi4C7BF186" }, + Description: "Automatically created by the RestApi construct" }, - Description: "Automatically created by the RestApi construct" - }, - DependsOn: [ "myapiGETF990CE3C" ] + DependsOn: ["myapiGETF990CE3C"] }, myapiDeploymentStageprod298F01AF: { - Type: "AWS::ApiGateway::Stage", - Properties: { - RestApiId: { - Ref: "myapi4C7BF186" - }, - DeploymentId: { - Ref: "myapiDeployment92F2CB49916eaecf87f818f1e175215b8d086029" - }, - StageName: "prod" - } + Type: "AWS::ApiGateway::Stage", + Properties: { + RestApiId: { Ref: "myapi4C7BF186" }, + DeploymentId: { Ref: "myapiDeployment92F2CB49916eaecf87f818f1e175215b8d086029" }, + StageName: "prod" + } }, myapiCloudWatchRole095452E5: { - Type: "AWS::IAM::Role", - Properties: { - AssumeRolePolicyDocument: { - Statement: [ - { - Action: "sts:AssumeRole", - Effect: "Allow", - Principal: { - Service: "apigateway.amazonaws.com" - } - } - ], - Version: "2012-10-17" - }, - ManagedPolicyArns: [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - Ref: "AWS::Partition" - }, - ":iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs" - ] + Type: "AWS::IAM::Role", + Properties: { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: "sts:AssumeRole", + Effect: "Allow", + Principal: { Service: "apigateway.amazonaws.com" } + } + ], + Version: "2012-10-17" + }, + ManagedPolicyArns: [ + { "Fn::Join": [ "", [ "arn:", { Ref: "AWS::Partition" }, ":iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs" ] ] } ] } - ] - } }, myapiAccountEC421A0A: { - Type: "AWS::ApiGateway::Account", - Properties: { - CloudWatchRoleArn: { - "Fn::GetAtt": [ - "myapiCloudWatchRole095452E5", - "Arn" - ] - } - }, - DependsOn: [ - "myapi4C7BF186" - ] + Type: "AWS::ApiGateway::Account", + Properties: { + CloudWatchRoleArn: { "Fn::GetAtt": [ "myapiCloudWatchRole095452E5", "Arn" ] } + }, + DependsOn: [ "myapi4C7BF186" ] } }, Outputs: { myapiEndpoint3628AFE3: { - Value: { - "Fn::Join": [ - "", - [ - "https://", - { - Ref: "myapi4C7BF186" - }, - ".execute-api.", - { - Ref: "AWS::Region" - }, - ".", - { Ref: "AWS::URLSuffix" }, - "/", - { - Ref: "myapiDeploymentStageprod298F01AF" - }, - "/" - ] - ] - }, - Export: { - Name: "myapiEndpoint3628AFE3" - } + Value: { + "Fn::Join": [ "", [ + "https://", + { Ref: "myapi4C7BF186" }, + ".execute-api.", + { Ref: "AWS::Region" }, + ".", + { Ref: "AWS::URLSuffix" }, + "/", + { Ref: "myapiDeploymentStageprod298F01AF" }, + "/" + ]] + }, + Export: { Name: "myapiEndpoint3628AFE3" } } } }); @@ -565,14 +521,17 @@ export = { const resource = new cdk.Resource(stack, 'DependsOnRestApi', { type: 'My::Resource' }); // WHEN - resource.addDependency(api); + resource.node.addDependency(api); // THEN expect(stack).to(haveResource('My::Resource', { DependsOn: [ - 'myapi162F20B8', // api - 'myapiDeploymentB7EF8EB75c091a668064a3f3a1f6d68a3fb22cf9', // deployment - 'myapiDeploymentStageprod329F21FF' // stage + "myapiDeploymentB7EF8EB75c091a668064a3f3a1f6d68a3fb22cf9", + "myapi162F20B8", + "myapiAccountC3A4750C", + "myapiCloudWatchRoleEB425128", + "myapiDeploymentStageprod329F21FF", + "myapiGET9B7CD29E" ] }, ResourcePart.CompleteDefinition)); diff --git a/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts b/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts index cb75fb79f50aa..ed9bd575c7144 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts @@ -232,7 +232,7 @@ export class AutoScalingGroup extends cdk.Construct implements IAutoScalingGroup associatePublicIpAddress: props.associatePublicIpAddress, }); - launchConfig.addDependency(this.role); + launchConfig.node.addDependency(this.role); const desiredCapacity = (props.desiredCapacity !== undefined ? props.desiredCapacity : @@ -388,9 +388,7 @@ export class AutoScalingGroup extends cdk.Construct implements IAutoScalingGroup ...props }); - // Target tracking policy can only be created after the load balancer has been - // attached to the targetgroup (because we need its ARN). - policy.addDependency(this.albTargetGroup.loadBalancerDependency()); + policy.node.addDependency(this.albTargetGroup.loadBalancerAttached); return policy; } diff --git a/packages/@aws-cdk/aws-autoscaling/lib/lifecycle-hook.ts b/packages/@aws-cdk/aws-autoscaling/lib/lifecycle-hook.ts index 4192f32636ca5..8db23f124ce66 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/lifecycle-hook.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/lifecycle-hook.ts @@ -3,7 +3,6 @@ import iam = require('@aws-cdk/aws-iam'); import cdk = require('@aws-cdk/cdk'); import { IAutoScalingGroup } from './auto-scaling-group'; import { CfnLifecycleHook } from './autoscaling.generated'; -import { LazyDependable } from './util'; /** * Basic properties for a lifecycle hook @@ -99,10 +98,7 @@ export class LifecycleHook extends cdk.Construct implements api.ILifecycleHook { // A LifecycleHook resource is going to do a permissions test upon creation, // so we have to make sure the role has full permissions before creating the // lifecycle hook. - // The LazyDependable makes sure we take a dependency on anything that gets - // added to the dependencyElements array, even if it happens after this - // point. - resource.addDependency(new LazyDependable(this.role)); + resource.node.addDependency(this.role); this.lifecycleHookName = resource.lifecycleHookName; } @@ -126,4 +122,4 @@ export enum LifecycleTransition { * Execute the hook when an instance is about to be terminated */ InstanceTerminating = 'autoscaling:EC2_INSTANCE_TERMINATING', -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-autoscaling/lib/target-tracking-scaling-policy.ts b/packages/@aws-cdk/aws-autoscaling/lib/target-tracking-scaling-policy.ts index 13b454e4b2b4b..7c9be49f4133d 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/target-tracking-scaling-policy.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/target-tracking-scaling-policy.ts @@ -93,17 +93,12 @@ export interface TargetTrackingScalingPolicyProps extends BasicTargetTrackingSca autoScalingGroup: IAutoScalingGroup; } -export class TargetTrackingScalingPolicy extends cdk.Construct implements cdk.IDependable { +export class TargetTrackingScalingPolicy extends cdk.Construct { /** * ARN of the scaling policy */ public readonly scalingPolicyArn: string; - /** - * Inner objects of this policy - */ - public readonly dependencyElements: cdk.IDependable[]; - /** * The resource object */ @@ -145,14 +140,6 @@ export class TargetTrackingScalingPolicy extends cdk.Construct implements cdk.ID }); this.scalingPolicyArn = this.resource.scalingPolicyArn; - this.dependencyElements = [this.resource]; - } - - /** - * Add a dependency on the given dependenable - */ - public addDependency(...other: cdk.IDependable[]) { - this.resource.addDependency(...other); } } @@ -192,4 +179,4 @@ export enum PredefinedMetric { * Specify the ALB to look at in the `resourceLabel` field. */ ALBRequestCountPerTarget = 'ALBRequestCountPerTarget', -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-autoscaling/lib/util.ts b/packages/@aws-cdk/aws-autoscaling/lib/util.ts deleted file mode 100644 index fc9f504660464..0000000000000 --- a/packages/@aws-cdk/aws-autoscaling/lib/util.ts +++ /dev/null @@ -1,12 +0,0 @@ -import cdk = require('@aws-cdk/cdk'); -/** - * Allow lazy evaluation of a list of dependables - */ -export class LazyDependable implements cdk.IDependable { - constructor(private readonly dependableSource: cdk.IDependable) { - } - - public get dependencyElements(): cdk.IDependable[] { - return this.dependableSource.dependencyElements; - } -} diff --git a/packages/@aws-cdk/aws-autoscaling/test/integ.asg-w-classic-loadbalancer.expected.json b/packages/@aws-cdk/aws-autoscaling/test/integ.asg-w-classic-loadbalancer.expected.json index 7979814d99c10..e585e437d2da5 100644 --- a/packages/@aws-cdk/aws-autoscaling/test/integ.asg-w-classic-loadbalancer.expected.json +++ b/packages/@aws-cdk/aws-autoscaling/test/integ.asg-w-classic-loadbalancer.expected.json @@ -710,8 +710,10 @@ ] }, "DependsOn": [ - "VPCIGWB7E252D3" + "VPCPublicSubnet1DefaultRoute91CEF279", + "VPCPublicSubnet2DefaultRouteB7481BBA", + "VPCPublicSubnet3DefaultRouteA0D29D46" ] } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-autoscaling/test/integ.asg-w-elbv2.expected.json b/packages/@aws-cdk/aws-autoscaling/test/integ.asg-w-elbv2.expected.json index 5088ec82a34c3..162ae8330b348 100644 --- a/packages/@aws-cdk/aws-autoscaling/test/integ.asg-w-elbv2.expected.json +++ b/packages/@aws-cdk/aws-autoscaling/test/integ.asg-w-elbv2.expected.json @@ -544,7 +544,8 @@ } }, "DependsOn": [ - "LBListener49E825B4" + "LBListener49E825B4", + "LBListenerTargetGroupF04FCF6D" ] }, "LB8A12904C": { @@ -571,7 +572,8 @@ "Type": "application" }, "DependsOn": [ - "VPCIGWB7E252D3" + "VPCPublicSubnet1DefaultRoute91CEF279", + "VPCPublicSubnet2DefaultRouteB7481BBA" ] }, "LBSecurityGroup8A41EA2B": { @@ -647,4 +649,4 @@ } } } -} \ No newline at end of file +} 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 65820d877ff1b..fdd4fede38631 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/test.pipeline-actions.ts +++ b/packages/@aws-cdk/aws-cloudformation/test/test.pipeline-actions.ts @@ -309,6 +309,7 @@ class PipelineDouble extends cdk.Construct implements cpapi.IPipeline { } class StageDouble implements cpapi.IStage, cpapi.IInternalStage { + public readonly dependencyRoots: cdk.IConstruct[] = [this]; public readonly name: string; public readonly pipeline: cpapi.IPipeline; public readonly _internal = this; diff --git a/packages/@aws-cdk/aws-cloudtrail/lib/index.ts b/packages/@aws-cdk/aws-cloudtrail/lib/index.ts index 4b0da728f7e57..1b7bedf020848 100644 --- a/packages/@aws-cdk/aws-cloudtrail/lib/index.ts +++ b/packages/@aws-cdk/aws-cloudtrail/lib/index.ts @@ -185,7 +185,7 @@ export class CloudTrail extends cdk.Construct { }); this.cloudTrailArn = trail.trailArn; const s3BucketPolicy = s3bucket.node.findChild("Policy").node.findChild("Resource") as s3.CfnBucketPolicy; - trail.addDependency(s3BucketPolicy); + trail.node.addDependency(s3BucketPolicy); } /** diff --git a/packages/@aws-cdk/aws-codedeploy/test/integ.deployment-group.expected.json b/packages/@aws-cdk/aws-codedeploy/test/integ.deployment-group.expected.json index d8f996ed7cfe8..bb77c733f2016 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/integ.deployment-group.expected.json +++ b/packages/@aws-cdk/aws-codedeploy/test/integ.deployment-group.expected.json @@ -639,8 +639,8 @@ } }, "DependsOn": [ - "ASGInstanceRoleE263A41B", - "ASGInstanceRoleDefaultPolicy7636D8BF" + "ASGInstanceRoleDefaultPolicy7636D8BF", + "ASGInstanceRoleE263A41B" ] }, "ASG46ED3070": { @@ -818,4 +818,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts b/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts index 7c29de60f231a..2dd94c6414baf 100644 --- a/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts +++ b/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts @@ -116,7 +116,7 @@ export class Pipeline extends cdk.Construct implements cpapi.IPipeline { }); // this will produce a DependsOn for both the role and the policy resources. - codePipeline.addDependency(this.role); + codePipeline.node.addDependency(this.role); this.artifactBucket.grantReadWrite(this.role); 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 4ec56bdc8442f..9ce5afc38218f 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 @@ -503,8 +503,8 @@ "Runtime": "nodejs6.10" }, "DependsOn": [ - "LambdaFunServiceRoleF0979767", - "LambdaFunServiceRoleDefaultPolicy217FED83" + "LambdaFunServiceRoleDefaultPolicy217FED83", + "LambdaFunServiceRoleF0979767" ] } } diff --git a/packages/@aws-cdk/aws-ec2/lib/vpc-ref.ts b/packages/@aws-cdk/aws-ec2/lib/vpc-ref.ts index e7c696de6bd26..d4c0f6b618d5b 100644 --- a/packages/@aws-cdk/aws-ec2/lib/vpc-ref.ts +++ b/packages/@aws-cdk/aws-ec2/lib/vpc-ref.ts @@ -1,7 +1,7 @@ import { Construct, IConstruct, IDependable, Stack } from "@aws-cdk/cdk"; import { subnetName } from './util'; -export interface IVpcSubnet extends IConstruct, IDependable { +export interface IVpcSubnet extends IConstruct { /** * The Availability Zone the subnet is located in */ @@ -12,13 +12,18 @@ export interface IVpcSubnet extends IConstruct, IDependable { */ readonly subnetId: string; + /** + * Dependable that can be depended upon to force internet connectivity established on the VPC + */ + readonly internetConnectivityEstablished: IDependable; + /** * Exports this subnet to another stack. */ export(): VpcSubnetImportProps; } -export interface IVpcNetwork extends IConstruct, IDependable { +export interface IVpcNetwork extends IConstruct { /** * Identifier for this VPC */ @@ -49,17 +54,6 @@ export interface IVpcNetwork extends IConstruct, IDependable { */ readonly vpcRegion: string; - /** - * Take a dependency on internet connectivity having been added to this VPC - * - * Take a dependency on this if your constructs need an Internet Gateway - * added to the VPC before they can be constructed. - * - * This method is for construct authors; application builders should not - * need to call this. - */ - internetDependency(): IDependable; - /** * Return the subnets appropriate for the placement strategy */ @@ -180,14 +174,14 @@ export abstract class VpcNetworkBase extends Construct implements IVpcNetwork { public abstract readonly availabilityZones: string[]; /** - * Parts of the VPC that constitute full construction + * Dependencies for internet connectivity */ - public readonly dependencyElements: IDependable[] = []; + public readonly internetDependencies = new Array(); /** - * Dependencies for internet connectivity + * Dependencies for NAT connectivity */ - public readonly internetDependencies = new Array(); + public readonly natDependencies = new Array(); /** * Return the subnets appropriate for the placement strategy @@ -233,19 +227,6 @@ export abstract class VpcNetworkBase extends Construct implements IVpcNetwork { return this.publicSubnets.indexOf(subnet) > -1; } - /** - * Take a dependency on internet connectivity having been added to this VPC - * - * Take a dependency on this if your constructs need an Internet Gateway - * added to the VPC before they can be constructed. - * - * This method is for construct authors; application builders should not - * need to call this. - */ - public internetDependency(): IDependable { - return new DependencyList(this.internetDependencies); - } - /** * The region where this VPC is defined */ @@ -322,16 +303,4 @@ export interface VpcSubnetImportProps { * The subnetId for this particular subnet */ subnetId: string; -} - -/** - * Allows using an array as a list of dependables. - */ -class DependencyList implements IDependable { - constructor(private readonly dependenclyElements: IDependable[]) { - } - - public get dependencyElements(): IDependable[] { - return this.dependenclyElements; - } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2/lib/vpc.ts b/packages/@aws-cdk/aws-ec2/lib/vpc.ts index 9444b272ac660..389a2c492d446 100644 --- a/packages/@aws-cdk/aws-ec2/lib/vpc.ts +++ b/packages/@aws-cdk/aws-ec2/lib/vpc.ts @@ -1,4 +1,5 @@ import cdk = require('@aws-cdk/cdk'); +import { ConcreteDependable, IDependable } from '@aws-cdk/cdk'; import { CfnEIP, CfnInternetGateway, CfnNatGateway, CfnRoute } from './ec2.generated'; import { CfnRouteTable, CfnSubnet, CfnSubnetRouteTableAssociation, CfnVPC, CfnVPCGatewayAttachment } from './ec2.generated'; import { NetworkBuilder } from './network-util'; @@ -307,7 +308,6 @@ export class VpcNetwork extends VpcNetworkBase { this.availabilityZones = this.availabilityZones.slice(0, maxAZs); this.vpcId = this.resource.vpcId; - this.dependencyElements.push(this.resource); this.subnetConfiguration = ifUndefined(props.subnetConfiguration, VpcNetwork.DEFAULT_SUBNETS); // subnetConfiguration and natGateways must be set before calling createSubnets @@ -325,7 +325,6 @@ export class VpcNetwork extends VpcNetworkBase { internetGatewayId: igw.ref, vpcId: this.resource.ref }); - this.dependencyElements.push(igw, att); (this.publicSubnets as VpcPublicSubnet[]).forEach(publicSubnet => { publicSubnet.addDefaultIGWRouteEntry(igw, att); @@ -388,7 +387,9 @@ export class VpcNetwork extends VpcNetworkBase { natSubnets = natSubnets.slice(0, natCount); for (const sub of natSubnets) { - this.natGatewayByAZ[sub.availabilityZone] = sub.addNatGateway(); + const gateway = sub.addNatGateway(); + this.natGatewayByAZ[sub.availabilityZone] = gateway.natGatewayId; + this.natDependencies.push(gateway); } } @@ -499,7 +500,7 @@ export interface VpcSubnetProps { /** * Represents a new VPC subnet resource */ -export class VpcSubnet extends cdk.Construct implements IVpcSubnet, cdk.IDependable { +export class VpcSubnet extends cdk.Construct implements IVpcSubnet { public static import(scope: cdk.Construct, id: string, props: VpcSubnetImportProps): IVpcSubnet { return new ImportedVpcSubnet(scope, id, props); } @@ -524,6 +525,8 @@ export class VpcSubnet extends cdk.Construct implements IVpcSubnet, cdk.IDependa */ private readonly routeTableId: string; + private readonly internetDependencies = new ConcreteDependable(); + constructor(scope: cdk.Construct, id: string, props: VpcSubnetProps) { super(scope, id); this.apply(new cdk.Tag(NAME_TAG, this.node.path)); @@ -542,12 +545,10 @@ export class VpcSubnet extends cdk.Construct implements IVpcSubnet, cdk.IDependa this.routeTableId = table.ref; // Associate the public route table for this subnet, to this subnet - const routeAssoc = new CfnSubnetRouteTableAssociation(this, 'RouteTableAssociation', { + new CfnSubnetRouteTableAssociation(this, 'RouteTableAssociation', { subnetId: this.subnetId, routeTableId: table.ref }); - - this.dependencyElements.push(subnet, table, routeAssoc); } public export(): VpcSubnetImportProps { @@ -557,12 +558,17 @@ export class VpcSubnet extends cdk.Construct implements IVpcSubnet, cdk.IDependa }; } + public get internetConnectivityEstablished(): IDependable { + return this.internetDependencies; + } + protected addDefaultRouteToNAT(natGatewayId: string) { - new CfnRoute(this, `DefaultRoute`, { + const route = new CfnRoute(this, `DefaultRoute`, { routeTableId: this.routeTableId, destinationCidrBlock: '0.0.0.0/0', natGatewayId }); + this.internetDependencies.add(route); } /** @@ -577,7 +583,11 @@ export class VpcSubnet extends cdk.Construct implements IVpcSubnet, cdk.IDependa destinationCidrBlock: '0.0.0.0/0', gatewayId: gateway.ref }); - route.addDependency(gatewayAttachment); + route.node.addDependency(gatewayAttachment); + + // Since the 'route' depends on the gateway attachment, just + // depending on the route is enough. + this.internetDependencies.add(route); } } @@ -585,6 +595,7 @@ export class VpcSubnet extends cdk.Construct implements IVpcSubnet, cdk.IDependa * Represents a public VPC subnet resource */ export class VpcPublicSubnet extends VpcSubnet { + constructor(scope: cdk.Construct, id: string, props: VpcSubnetProps) { super(scope, id, props); } @@ -612,7 +623,7 @@ export class VpcPublicSubnet extends VpcSubnet { domain: 'vpc' }).eipAllocationId, }); - return ngw.natGatewayId; + return ngw; } } @@ -666,9 +677,9 @@ class ImportedVpcNetwork extends VpcNetworkBase { } class ImportedVpcSubnet extends cdk.Construct implements IVpcSubnet { + public readonly internetConnectivityEstablished: cdk.IDependable = new cdk.ConcreteDependable(); public readonly availabilityZone: string; public readonly subnetId: string; - public readonly dependencyElements = new Array(); constructor(scope: cdk.Construct, id: string, private readonly props: VpcSubnetImportProps) { super(scope, id); 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 cc3e5cefd8e48..8784473fb202d 100644 --- a/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts +++ b/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts @@ -56,12 +56,7 @@ export interface BaseServiceProps { * Base class for Ecs and Fargate services */ export abstract class BaseService extends cdk.Construct - implements elbv2.IApplicationLoadBalancerTarget, elbv2.INetworkLoadBalancerTarget, cdk.IDependable { - - /** - * CloudFormation resources generated by this service - */ - public readonly dependencyElements: cdk.IDependable[]; + implements elbv2.IApplicationLoadBalancerTarget, elbv2.INetworkLoadBalancerTarget { /** * Manage allowed network traffic for this service @@ -118,7 +113,6 @@ export abstract class BaseService extends cdk.Construct }); this.serviceArn = this.resource.serviceArn; this.serviceName = this.resource.serviceName; - this.dependencyElements = [this.resource]; this.clusterName = clusterName; } @@ -216,7 +210,9 @@ export abstract class BaseService extends cdk.Construct containerPort: this.taskDefinition.defaultContainer!.containerPort, }); - this.resource.addDependency(targetGroup.loadBalancerDependency()); + // Service creation can only happen after the load balancer has + // been associated with our target group(s), so add ordering dependency. + this.resource.node.addDependency(targetGroup.loadBalancerAttached); const targetType = this.taskDefinition.networkMode === NetworkMode.AwsVpc ? elbv2.TargetType.Ip : elbv2.TargetType.Instance; return { targetType }; 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 cff577e35c37d..0771334798742 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 @@ -809,7 +809,8 @@ "SchedulingStrategy": "REPLICA" }, "DependsOn": [ - "LBPublicListener6E1F3D94" + "LBPublicListener6E1F3D94", + "LBPublicListenerECSGroupD6A32205" ] }, "ServiceSecurityGroupC96ED6A7": { @@ -874,7 +875,8 @@ "Type": "application" }, "DependsOn": [ - "VpcIGWD7BA715C" + "VpcPublicSubnet1DefaultRoute3DA9E72A", + "VpcPublicSubnet2DefaultRoute97F91067" ] }, "LBSecurityGroup8A41EA2B": { @@ -963,4 +965,4 @@ } } } -} \ No newline at end of file +} 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 31e990b4fb5d9..364526f95d1a6 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 @@ -810,7 +810,8 @@ "SchedulingStrategy": "REPLICA" }, "DependsOn": [ - "LBPublicListener6E1F3D94" + "LBPublicListener6E1F3D94", + "LBPublicListenerECSGroupD6A32205" ] }, "LB8A12904C": { @@ -837,7 +838,8 @@ "Type": "application" }, "DependsOn": [ - "VpcIGWD7BA715C" + "VpcPublicSubnet1DefaultRoute3DA9E72A", + "VpcPublicSubnet2DefaultRoute97F91067" ] }, "LBSecurityGroup8A41EA2B": { @@ -926,4 +928,4 @@ } } } -} \ No newline at end of file +} 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 84259177dd0f0..b6dfb75d25df9 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 @@ -838,7 +838,8 @@ } }, "DependsOn": [ - "FargateServiceLBPublicListener4B4929CA" + "FargateServiceLBPublicListener4B4929CA", + "FargateServiceLBPublicListenerECSGroupBE57E081" ] }, "FargateServiceSecurityGroup262B61DD": { @@ -903,7 +904,8 @@ "Type": "application" }, "DependsOn": [ - "VpcIGWD7BA715C" + "VpcPublicSubnet1DefaultRoute3DA9E72A", + "VpcPublicSubnet2DefaultRoute97F91067" ] }, "FargateServiceLBSecurityGroup5F444C78": { @@ -1017,4 +1019,4 @@ } } } -} \ No newline at end of file +} 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 bbbb7fa3627a2..2b672a5dc4ec3 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,7 +525,8 @@ } }, "DependsOn": [ - "L3LBPublicListener156FFC0F" + "L3LBPublicListener156FFC0F", + "L3LBPublicListenerECSGroup648EEA11" ] }, "L3ServiceSecurityGroup677B0897": { @@ -590,7 +591,8 @@ "Type": "application" }, "DependsOn": [ - "VpcIGWD7BA715C" + "VpcPublicSubnet1DefaultRoute3DA9E72A", + "VpcPublicSubnet2DefaultRoute97F91067" ] }, "L3LBSecurityGroupEDE61198": { @@ -679,4 +681,4 @@ } } } -} \ No newline at end of file +} 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 5cdc9fd00b4bf..1c06d537a4645 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,7 +454,8 @@ } }, "DependsOn": [ - "LBPublicListener6E1F3D94" + "LBPublicListener6E1F3D94", + "LBPublicListenerFargateGroup5EE2FBAF" ] }, "ServiceSecurityGroupC96ED6A7": { @@ -583,7 +584,8 @@ "Type": "application" }, "DependsOn": [ - "VpcIGWD7BA715C" + "VpcPublicSubnet1DefaultRoute3DA9E72A", + "VpcPublicSubnet2DefaultRoute97F91067" ] }, "LBSecurityGroup8A41EA2B": { @@ -672,4 +674,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-elasticloadbalancing/lib/load-balancer.ts b/packages/@aws-cdk/aws-elasticloadbalancing/lib/load-balancer.ts index 118504c686501..5ad70f90fd8cb 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancing/lib/load-balancer.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancing/lib/load-balancer.ts @@ -222,7 +222,7 @@ export class LoadBalancer extends cdk.Construct implements IConnectable, codedep healthCheck: props.healthCheck && healthCheckToJSON(props.healthCheck), }); if (props.internetFacing) { - this.elb.addDependency(props.vpc.internetDependency()); + this.elb.node.addDependency(...subnets.map(s => s.internetConnectivityEstablished)); } ifUndefined(props.listeners, []).forEach(b => this.addListener(b)); diff --git a/packages/@aws-cdk/aws-elasticloadbalancing/test/integ.elb.expected.json b/packages/@aws-cdk/aws-elasticloadbalancing/test/integ.elb.expected.json index 38baffc69786a..e880c454c6447 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancing/test/integ.elb.expected.json +++ b/packages/@aws-cdk/aws-elasticloadbalancing/test/integ.elb.expected.json @@ -251,8 +251,8 @@ ] }, "DependsOn": [ - "VPCIGWB7E252D3" + "VPCPublicSubnet1DefaultRoute91CEF279" ] } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener-certificate.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener-certificate.ts index 45fd4924f4cc4..383b32541e9ce 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener-certificate.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener-certificate.ts @@ -22,20 +22,13 @@ export interface ApplicationListenerCertificateProps { /** * Add certificates to a listener */ -export class ApplicationListenerCertificate extends cdk.Construct implements cdk.IDependable { - /** - * The elements of this resou rce to add ordering dependencies on - */ - public readonly dependencyElements: cdk.IDependable[] = []; - +export class ApplicationListenerCertificate extends cdk.Construct { constructor(scope: cdk.Construct, id: string, props: ApplicationListenerCertificateProps) { super(scope, id); - const resource = new CfnListenerCertificate(this, 'Resource', { + new CfnListenerCertificate(this, 'Resource', { listenerArn: props.listener.listenerArn, certificates: props.certificateArns.map(certificateArn => ({ certificateArn })), }); - - this.dependencyElements.push(resource); } } diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener-rule.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener-rule.ts index b6857f0fdbfa0..d0125d276aeec 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener-rule.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener-rule.ts @@ -57,17 +57,12 @@ export interface ApplicationListenerRuleProps extends BaseApplicationListenerRul /** * Define a new listener rule */ -export class ApplicationListenerRule extends cdk.Construct implements cdk.IDependable { +export class ApplicationListenerRule extends cdk.Construct { /** * The ARN of this rule */ public readonly listenerRuleArn: string; - /** - * The elements of this rule to add ordering dependencies on - */ - public readonly dependencyElements: cdk.IDependable[] = []; - private readonly conditions: {[key: string]: string[] | undefined} = {}; private readonly actions: any[] = []; @@ -98,7 +93,6 @@ export class ApplicationListenerRule extends cdk.Construct implements cdk.IDepen (props.targetGroups || []).forEach(this.addTargetGroup.bind(this)); - this.dependencyElements.push(resource); this.listenerRuleArn = resource.ref; } diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener.ts index eb32e0264f6b1..540b88e0973df 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener.ts @@ -258,7 +258,7 @@ export class ApplicationListener extends BaseListener implements IApplicationLis /** * Properties to reference an existing listener */ -export interface IApplicationListener extends cdk.IConstruct, ec2.IConnectable, cdk.IDependable { +export interface IApplicationListener extends cdk.IConstruct, ec2.IConnectable { /** * ARN of the listener */ @@ -324,7 +324,6 @@ export interface ApplicationListenerImportProps { } class ImportedApplicationListener extends cdk.Construct implements IApplicationListener { - public readonly dependencyElements: cdk.IDependable[] = []; public readonly connections: ec2.Connections; /** diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-target-group.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-target-group.ts index 58cef9303b9a0..9c00b275b06e3 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-target-group.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-target-group.ts @@ -5,7 +5,7 @@ import { BaseTargetGroupProps, ITargetGroup, loadBalancerNameFromListenerArn, Lo TargetGroupBase, TargetGroupImportProps } from '../shared/base-target-group'; import { ApplicationProtocol } from '../shared/enums'; import { ImportedTargetGroupBase } from '../shared/imported'; -import { determineProtocolAndPort, LazyDependable } from '../shared/util'; +import { determineProtocolAndPort } from '../shared/util'; import { IApplicationListener } from './application-listener'; import { HttpCodeTarget } from './application-load-balancer'; @@ -140,14 +140,14 @@ export class ApplicationTargetGroup extends TargetGroupBase { * * Don't call this directly. It will be called by listeners. */ - public registerListener(listener: IApplicationListener, dependable?: cdk.IDependable) { + public registerListener(listener: IApplicationListener, associatingConstruct?: cdk.IConstruct) { // Notify this listener of all connectables that we know about. // Then remember for new connectables that might get added later. for (const member of this.connectableMembers) { listener.registerConnectable(member.connectable, member.portRange); } this.listeners.push(listener); - this.loadBalancerAssociationDependencies.push(dependable || listener); + this.loadBalancerAttachedDependencies.add(associatingConstruct || listener); } /** @@ -324,20 +324,16 @@ export interface IApplicationTargetGroup extends ITargetGroup { * * Don't call this directly. It will be called by listeners. */ - registerListener(listener: IApplicationListener, dependable?: cdk.IDependable): void; + registerListener(listener: IApplicationListener, associatingConstruct?: cdk.IConstruct): void; } /** * An imported application target group */ class ImportedApplicationTargetGroup extends ImportedTargetGroupBase implements IApplicationTargetGroup { - public registerListener(_listener: IApplicationListener, _dependable?: cdk.IDependable) { + public registerListener(_listener: IApplicationListener, _associatingConstruct?: cdk.IConstruct) { // Nothing to do, we know nothing of our members } - - public loadBalancerDependency(): cdk.IDependable { - return new LazyDependable([]); - } } /** diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-listener.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-listener.ts index 63e25e704aedb..b0b2034d9f5db 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-listener.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-listener.ts @@ -114,7 +114,7 @@ export class NetworkListener extends BaseListener implements INetworkListener { /** * Properties to reference an existing listener */ -export interface INetworkListener extends cdk.IConstruct, cdk.IDependable { +export interface INetworkListener extends cdk.IConstruct { /** * ARN of the listener */ @@ -140,8 +140,6 @@ export interface NetworkListenerImportProps { * An imported Network Listener */ class ImportedNetworkListener extends cdk.Construct implements INetworkListener { - public readonly dependencyElements: cdk.IDependable[] = []; - /** * ARN of the listener */ diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-target-group.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-target-group.ts index 579009605f69e..6c7a3ba27b5c9 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-target-group.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-target-group.ts @@ -3,7 +3,6 @@ import { BaseTargetGroupProps, ITargetGroup, loadBalancerNameFromListenerArn, Lo TargetGroupBase, TargetGroupImportProps } from '../shared/base-target-group'; import { Protocol } from '../shared/enums'; import { ImportedTargetGroupBase } from '../shared/imported'; -import { LazyDependable } from '../shared/util'; import { INetworkListener } from './network-listener'; /** @@ -76,7 +75,7 @@ export class NetworkTargetGroup extends TargetGroupBase { * Don't call this directly. It will be called by listeners. */ public registerListener(listener: INetworkListener) { - this.loadBalancerAssociationDependencies.push(listener); + this.loadBalancerAttachedDependencies.add(listener); this.listeners.push(listener); } @@ -111,10 +110,6 @@ class ImportedNetworkTargetGroup extends ImportedTargetGroupBase implements INet public registerListener(_listener: INetworkListener) { // Nothing to do, we know nothing of our members } - - public loadBalancerDependency(): cdk.IDependable { - return new LazyDependable([]); - } } /** diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-listener.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-listener.ts index 44b8299d5cb9b..74680dcd74787 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-listener.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-listener.ts @@ -5,8 +5,7 @@ import { ITargetGroup } from './base-target-group'; /** * Base class for listeners */ -export abstract class BaseListener extends cdk.Construct implements cdk.IDependable { - public readonly dependencyElements: cdk.IDependable[]; +export abstract class BaseListener extends cdk.Construct { public readonly listenerArn: string; private readonly defaultActions: any[] = []; @@ -18,7 +17,6 @@ export abstract class BaseListener extends cdk.Construct implements cdk.IDependa defaultActions: new cdk.Token(() => this.defaultActions), }); - this.dependencyElements = [resource]; this.listenerArn = resource.ref; } diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-load-balancer.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-load-balancer.ts index 0e98adc7123d0..4ed43405deacf 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-load-balancer.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-load-balancer.ts @@ -111,7 +111,7 @@ export abstract class BaseLoadBalancer extends cdk.Construct implements route53. ...additionalProps }); if (internetFacing) { - resource.addDependency(this.vpc.internetDependency()); + resource.node.addDependency(...subnets.map(s => s.internetConnectivityEstablished)); } if (baseProps.deletionProtection) { this.setAttribute('deletion_protection.enabled', 'true'); } diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-target-group.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-target-group.ts index 2b71ef19d39d1..5932132991260 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-target-group.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-target-group.ts @@ -3,7 +3,7 @@ import ec2 = require('@aws-cdk/aws-ec2'); import cdk = require('@aws-cdk/cdk'); import { CfnTargetGroup } from '../elasticloadbalancingv2.generated'; import { Protocol, TargetType } from './enums'; -import { Attributes, LazyDependable, renderAttributes } from './util'; +import { Attributes, renderAttributes } from './util'; /** * Basic properties of both Application and Network Target Groups @@ -178,9 +178,9 @@ export abstract class TargetGroupBase extends cdk.Construct implements ITargetGr protected readonly defaultPort: string; /** - * List of dependables that need to be depended on to ensure the TargetGroup is associated to a load balancer + * Configurable dependable with all resources that lead to load balancer attachment */ - protected readonly loadBalancerAssociationDependencies = new Array(); + protected readonly loadBalancerAttachedDependencies = new cdk.ConcreteDependable(); /** * Attributes of this target group @@ -242,6 +242,13 @@ export abstract class TargetGroupBase extends cdk.Construct implements ITargetGr this.defaultPort = `${additionalProps.port}`; } + /** + * List of constructs that need to be depended on to ensure the TargetGroup is associated to a load balancer + */ + public get loadBalancerAttached(): cdk.IDependable { + return this.loadBalancerAttachedDependencies; + } + /** * Set/replace the target group's health check */ @@ -268,13 +275,6 @@ export abstract class TargetGroupBase extends cdk.Construct implements ITargetGr }; } - /** - * Add a dependency between this target group and the indicated resources - */ - public addDependency(...other: cdk.IDependable[]) { - this.resource.addDependency(...other); - } - public asCodeDeployLoadBalancer(): codedeploy.ILoadBalancerProps { return { generation: codedeploy.LoadBalancerGeneration.Second, @@ -282,13 +282,6 @@ export abstract class TargetGroupBase extends cdk.Construct implements ITargetGr }; } - /** - * Return an object to depend on this TargetGroup being attached to a load balancer - */ - public loadBalancerDependency(): cdk.IDependable { - return new LazyDependable(this.loadBalancerAssociationDependencies); - } - /** * Register the given load balancing target as part of this group */ @@ -341,7 +334,7 @@ export interface ITargetGroup extends cdk.IConstruct { /** * Return an object to depend on the listeners added to this target group */ - loadBalancerDependency(): cdk.IDependable; + readonly loadBalancerAttached: cdk.IDependable; /** * Export this target group @@ -381,4 +374,4 @@ export interface LoadBalancerTargetProps { export function loadBalancerNameFromListenerArn(listenerArn: string) { const arnParts = cdk.Fn.split('/', listenerArn); return `${cdk.Fn.select(1, arnParts)}/${cdk.Fn.select(2, arnParts)}/${cdk.Fn.select(3, arnParts)}`; -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/imported.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/imported.ts index bf2a663ebc337..96f33d4d76645 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/imported.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/imported.ts @@ -15,6 +15,11 @@ export abstract class ImportedTargetGroupBase extends cdk.Construct implements I */ public readonly loadBalancerArns: string; + /** + * Return an object to depend on the listeners added to this target group + */ + public readonly loadBalancerAttached: cdk.IDependable = new cdk.ConcreteDependable(); + constructor(scope: cdk.Construct, id: string, private readonly props: TargetGroupImportProps) { super(scope, id); @@ -22,11 +27,6 @@ export abstract class ImportedTargetGroupBase extends cdk.Construct implements I this.loadBalancerArns = props.loadBalancerArns || new cdk.AwsNoValue().toString(); } - /** - * Return an object to depend on the listeners added to this target group - */ - public abstract loadBalancerDependency(): cdk.IDependable; - public export() { return this.props; } diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/util.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/util.ts index 808a8f0e2c33e..d992d156a3b2f 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/util.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/util.ts @@ -1,4 +1,3 @@ -import cdk = require('@aws-cdk/cdk'); import { ApplicationProtocol } from "./enums"; export type Attributes = {[key: string]: string | undefined}; @@ -68,15 +67,3 @@ export function determineProtocolAndPort(protocol: ApplicationProtocol | undefin export function ifUndefined(x: T | undefined, def: T) { return x !== undefined ? x : def; } - -/** - * Allow lazy evaluation of a list of dependables - */ -export class LazyDependable implements cdk.IDependable { - constructor(private readonly depList: cdk.IDependable[]) { - } - - public get dependencyElements(): cdk.IDependable[] { - return this.depList; - } -} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/test.listener.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/test.listener.ts index 2311081a79f3a..dcf2c62c84ec1 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/test.listener.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/test.listener.ts @@ -365,8 +365,7 @@ export = { const group = new elbv2.ApplicationTargetGroup(stack, 'TargetGroup', { vpc, port: 80 }); // WHEN - const resource = new cdk.Resource(stack, 'SomeResource', { type: 'Test::Resource' }); - resource.addDependency(group.loadBalancerDependency()); + new ResourceWithLBDependency(stack, 'SomeResource', group); loadBalancer.addListener('Listener', { port: 80, @@ -443,8 +442,7 @@ export = { }); // WHEN - const resource = new cdk.Resource(stack, 'SomeResource', { type: 'Test::Resource' }); - resource.addDependency(group2.loadBalancerDependency()); + new ResourceWithLBDependency(stack, 'SomeResource', group2); listener.addTargetGroups('SecondGroup', { pathPattern: '/bla', @@ -465,3 +463,10 @@ export = { test.done(); }, }; + +class ResourceWithLBDependency extends cdk.Resource { + constructor(scope: cdk.Construct, id: string, targetGroup: elbv2.ITargetGroup) { + super(scope, id, { type: 'Test::Resource' }); + this.node.addDependency(targetGroup.loadBalancerAttached); + } +} diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/test.load-balancer.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/test.load-balancer.ts index 6a566df599ec3..7b474d1fb7bc8 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/test.load-balancer.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/test.load-balancer.ts @@ -45,7 +45,11 @@ export = { // THEN expect(stack).to(haveResource('AWS::ElasticLoadBalancingV2::LoadBalancer', { - DependsOn: ['StackIGW2F0A1126'] + DependsOn: [ + 'StackPublicSubnet1DefaultRoute16154E3D', + 'StackPublicSubnet2DefaultRoute0319539B', + 'StackPublicSubnet3DefaultRouteBC0DA152' + ] }, ResourcePart.CompleteDefinition)); test.done(); diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/integ.alb-alias-target.expected.json b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/integ.alb-alias-target.expected.json index 9e233a1989206..a4885733fe40d 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/integ.alb-alias-target.expected.json +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/integ.alb-alias-target.expected.json @@ -367,7 +367,8 @@ "Type": "application" }, "DependsOn": [ - "VPCIGWB7E252D3" + "VPCPublicSubnet1DefaultRoute91CEF279", + "VPCPublicSubnet2DefaultRouteB7481BBA" ] }, "LBSecurityGroup8A41EA2B": { @@ -420,4 +421,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/integ.alb.expected.json b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/integ.alb.expected.json index daad87f5eee6f..cfd25d4739769 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/integ.alb.expected.json +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/integ.alb.expected.json @@ -367,7 +367,8 @@ "Type": "application" }, "DependsOn": [ - "VPCIGWB7E252D3" + "VPCPublicSubnet1DefaultRoute91CEF279", + "VPCPublicSubnet2DefaultRouteB7481BBA" ] }, "LBSecurityGroup8A41EA2B": { @@ -624,4 +625,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/integ.nlb.expected.json b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/integ.nlb.expected.json index 0826aa107b84d..0f86d47273c44 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/integ.nlb.expected.json +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/integ.nlb.expected.json @@ -359,7 +359,8 @@ "Type": "network" }, "DependsOn": [ - "VPCIGWB7E252D3" + "VPCPublicSubnet1DefaultRoute91CEF279", + "VPCPublicSubnet2DefaultRouteB7481BBA" ] }, "LBListener49E825B4": { @@ -397,10 +398,8 @@ "TargetType": "ip" }, "DependsOn": [ - "VPCB9E5F0B4", - "VPCIGWB7E252D3", - "VPCVPCGW99B986DC" + "VPCIGWB7E252D3" ] } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/integ.nlb.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/integ.nlb.ts index bf2b1769bc9c9..2dd2ee2c30948 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/integ.nlb.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/integ.nlb.ts @@ -24,7 +24,7 @@ const group = listener.addTargets('Target', { targets: [new elbv2.IpTarget('10.0.1.1')] }); -group.addDependency(vpc); +group.node.addDependency(...vpc.internetDependencies); // The target's security group must allow being routed by the LB and the clients. 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 f96e0bca0b131..6b08d52acbb51 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/test.listener.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/test.listener.ts @@ -125,17 +125,19 @@ export = { }); // WHEN - const resource = new cdk.Resource(stack, 'MyResource', { - type: 'SomeResource', - }); - resource.addDependency(group.loadBalancerDependency()); + new ResourceWithLBDependency(stack, 'MyResource', group); // THEN expect(stack).toMatch({ Resources: { MyResource: { - Type: "SomeResource", - DependsOn: [ "LBListener49E825B4" ] + Type: "Test::Resource", + DependsOn: [ + "LBListener49E825B4", + // 2nd dependency is there because of the structure of the construct tree. + // It does not harm. + "LBListenerGroupGroup79B304FF" + ] } } }, MatchStyle.SUPERSET); @@ -143,3 +145,10 @@ export = { test.done(); }, }; + +class ResourceWithLBDependency extends cdk.Resource { + constructor(scope: cdk.Construct, id: string, targetGroup: elbv2.ITargetGroup) { + super(scope, id, { type: 'Test::Resource' }); + this.node.addDependency(targetGroup.loadBalancerAttached); + } +} diff --git a/packages/@aws-cdk/aws-iam/lib/lazy-role.ts b/packages/@aws-cdk/aws-iam/lib/lazy-role.ts index fcf8307fadb39..ec48896d22553 100644 --- a/packages/@aws-cdk/aws-iam/lib/lazy-role.ts +++ b/packages/@aws-cdk/aws-iam/lib/lazy-role.ts @@ -63,13 +63,6 @@ export class LazyRole extends cdk.Construct implements IRole { } } - /** - * Returns the role. - */ - public get dependencyElements(): cdk.IDependable[] { - return this.instantiate().dependencyElements; - } - /** * Returns the ARN of this role. */ diff --git a/packages/@aws-cdk/aws-iam/lib/policy.ts b/packages/@aws-cdk/aws-iam/lib/policy.ts index 2121441970a85..bafc0c5d3f340 100644 --- a/packages/@aws-cdk/aws-iam/lib/policy.ts +++ b/packages/@aws-cdk/aws-iam/lib/policy.ts @@ -1,4 +1,4 @@ -import { Construct, IDependable, Token } from '@aws-cdk/cdk'; +import { Construct, Token } from '@aws-cdk/cdk'; import { Group } from './group'; import { CfnPolicy } from './iam.generated'; import { PolicyDocument, PolicyPrincipal, PolicyStatement } from './policy-document'; @@ -83,7 +83,7 @@ export interface PolicyProps { * Policies](http://docs.aws.amazon.com/IAM/latest/UserGuide/policies_overview.html) * in the IAM User Guide guide. */ -export class Policy extends Construct implements IDependable { +export class Policy extends Construct { /** * The policy document. */ @@ -94,11 +94,6 @@ export class Policy extends Construct implements IDependable { */ public readonly policyName: string; - /** - * Lists all the elements consumers should "depend-on". - */ - public readonly dependencyElements: IDependable[]; - private readonly roles = new Array(); private readonly users = new Array(); private readonly groups = new Array(); @@ -118,7 +113,6 @@ export class Policy extends Construct implements IDependable { // policy names are limited to 128. the last 8 chars are a stack-unique hash, so // that shouod be sufficient to ensure uniqueness within a principal. this.policyName = props.policyName || generatePolicyName(resource.logicalId); - this.dependencyElements = [ resource ]; if (props.users) { props.users.forEach(u => this.attachToUser(u)); diff --git a/packages/@aws-cdk/aws-iam/lib/role.ts b/packages/@aws-cdk/aws-iam/lib/role.ts index 234871891fb5b..528f3fc5db761 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, IDependable, Output, Stack } from '@aws-cdk/cdk'; +import { Construct, IConstruct, Output, Stack } from '@aws-cdk/cdk'; import { CfnRole } from './iam.generated'; import { IPrincipal, Policy } from './policy'; import { ArnPrincipal, PolicyDocument, PolicyPrincipal, PolicyStatement } from './policy-document'; @@ -124,11 +124,6 @@ export class Role extends Construct implements IRole { */ public readonly principal: PolicyPrincipal; - /** - * Returns the role. - */ - public readonly dependencyElements: IDependable[]; - private defaultPolicy?: Policy; private readonly managedPolicyArns: string[]; private readonly attachedPolicies = new AttachedPolicies(); @@ -154,7 +149,6 @@ export class Role extends Construct implements IRole { this.roleArn = role.roleArn; this.principal = new ArnPrincipal(this.roleArn); this.roleName = role.roleName; - this.dependencyElements = [ role ]; function _flatten(policies?: { [name: string]: PolicyDocument }) { if (policies == null || Object.keys(policies).length === 0) { @@ -185,7 +179,6 @@ export class Role extends Construct implements IRole { if (!this.defaultPolicy) { this.defaultPolicy = new Policy(this, 'DefaultPolicy'); this.attachInlinePolicy(this.defaultPolicy); - this.dependencyElements.push(this.defaultPolicy); } this.defaultPolicy.addStatement(statement); } @@ -231,7 +224,7 @@ export class Role extends Construct implements IRole { /** * A Role object */ -export interface IRole extends IConstruct, IPrincipal, IDependable { +export interface IRole extends IConstruct, IPrincipal { /** * Returns the ARN of this role. */ @@ -302,7 +295,6 @@ export interface RoleImportProps { class ImportedRole extends Construct implements IRole { public readonly roleArn: string; public readonly principal: PolicyPrincipal; - public readonly dependencyElements: IDependable[] = []; private readonly _roleId?: string; diff --git a/packages/@aws-cdk/aws-iam/test/test.role.ts b/packages/@aws-cdk/aws-iam/test/test.role.ts index ee4801db0ec78..e053583797715 100644 --- a/packages/@aws-cdk/aws-iam/test/test.role.ts +++ b/packages/@aws-cdk/aws-iam/test/test.role.ts @@ -1,5 +1,5 @@ import { expect, haveResource, haveResourceLike } from '@aws-cdk/assert'; -import { Resource, Stack } from '@aws-cdk/cdk'; +import { Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import { ArnPrincipal, CompositePrincipal, FederatedPrincipal, PolicyStatement, Role, ServicePrincipal, User } from '../lib'; @@ -137,17 +137,6 @@ export = { test.done(); }, - 'role implements IDependable to allow resources to depend on it'(test: Test) { - const stack = new Stack(); - const role = new Role(stack, 'MyRole', { assumedBy: new ServicePrincipal('foo') }); - - test.equal(role.dependencyElements.length, 1); - - const roleResource = role.dependencyElements[0] as Resource; - test.equal(roleResource.resourceType, 'AWS::IAM::Role'); - test.done(); - }, - 'federated principal can change AssumeRoleAction'(test: Test) { const stack = new Stack(); const cognitoPrincipal = new FederatedPrincipal( diff --git a/packages/@aws-cdk/aws-lambda/lib/lambda.ts b/packages/@aws-cdk/aws-lambda/lib/lambda.ts index 3bd09a52c14e4..cc7d9b1b4c878 100644 --- a/packages/@aws-cdk/aws-lambda/lib/lambda.ts +++ b/packages/@aws-cdk/aws-lambda/lib/lambda.ts @@ -364,7 +364,7 @@ export class Function extends FunctionBase { reservedConcurrentExecutions: props.reservedConcurrentExecutions }); - resource.addDependency(this.role); + resource.node.addDependency(this.role); this.functionName = resource.ref; this.functionArn = resource.functionArn; diff --git a/packages/@aws-cdk/aws-rds/lib/cluster.ts b/packages/@aws-cdk/aws-rds/lib/cluster.ts index aca08f27055c0..e87c5340209cb 100644 --- a/packages/@aws-cdk/aws-rds/lib/cluster.ts +++ b/packages/@aws-cdk/aws-rds/lib/cluster.ts @@ -207,13 +207,9 @@ export class DatabaseCluster extends cdk.Construct implements IDatabaseCluster { dbSubnetGroupName: subnetGroup.ref, }); - if (publiclyAccessible) { - // We must have a dependency on the NAT gateway provider here to - // create things in the right order. To be safe (and because we - // cannot express it differently), take a dependency on the - // whole VPC. - instance.addDependency(props.instanceProps.vpc); - } + // We must have a dependency on the NAT gateway provider here to create + // things in the right order. + instance.node.addDependency(...subnets.map(s => s.internetConnectivityEstablished)); this.instanceIdentifiers.push(instance.ref); this.instanceEndpoints.push(new Endpoint(instance.dbInstanceEndpointAddress, instance.dbInstanceEndpointPort)); diff --git a/packages/@aws-cdk/aws-rds/test/integ.cluster.expected.json b/packages/@aws-cdk/aws-rds/test/integ.cluster.expected.json index eb14dcf760fbc..d8bb54664a09b 100644 --- a/packages/@aws-cdk/aws-rds/test/integ.cluster.expected.json +++ b/packages/@aws-cdk/aws-rds/test/integ.cluster.expected.json @@ -500,9 +500,8 @@ "PubliclyAccessible": true }, "DependsOn": [ - "VPCB9E5F0B4", - "VPCIGWB7E252D3", - "VPCVPCGW99B986DC" + "VPCPublicSubnet1DefaultRoute91CEF279", + "VPCPublicSubnet2DefaultRouteB7481BBA" ] }, "DatabaseInstance2AA380DEE": { @@ -519,10 +518,9 @@ "PubliclyAccessible": true }, "DependsOn": [ - "VPCB9E5F0B4", - "VPCIGWB7E252D3", - "VPCVPCGW99B986DC" + "VPCPublicSubnet1DefaultRoute91CEF279", + "VPCPublicSubnet2DefaultRouteB7481BBA" ] } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource.ts b/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource.ts index 8173447a4187a..9f924caee4f69 100644 --- a/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource.ts +++ b/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource.ts @@ -63,7 +63,7 @@ export class BucketNotifications extends cdk.Construct { // for example, the SNS topic policy must be created /before/ the notification resource. // otherwise, S3 won't be able to confirm the subscription. if (targetProps.dependencies) { - resource.addDependency(...targetProps.dependencies); + resource.node.addDependency(...targetProps.dependencies); } // based on the target type, add the the correct configurations array diff --git a/packages/@aws-cdk/aws-s3/test/test.notifications.ts b/packages/@aws-cdk/aws-s3/test/test.notifications.ts index 50bfa11563aeb..0fc602515138d 100644 --- a/packages/@aws-cdk/aws-s3/test/test.notifications.ts +++ b/packages/@aws-cdk/aws-s3/test/test.notifications.ts @@ -288,6 +288,7 @@ export = { bucket.onObjectCreated(dest); + stack.node.prepareTree(); test.deepEqual(stack.toCloudFormation().Resources.BucketNotifications8F2E257D, { Type: 'Custom::S3BucketNotifications', Properties: { diff --git a/packages/@aws-cdk/aws-sns/lib/policy.ts b/packages/@aws-cdk/aws-sns/lib/policy.ts index 812335f2b83fa..605e3fe36a0e5 100644 --- a/packages/@aws-cdk/aws-sns/lib/policy.ts +++ b/packages/@aws-cdk/aws-sns/lib/policy.ts @@ -1,5 +1,5 @@ import { PolicyDocument } from '@aws-cdk/aws-iam'; -import { Construct, IDependable } from '@aws-cdk/cdk'; +import { Construct } from '@aws-cdk/cdk'; import { CfnTopicPolicy } from './sns.generated'; import { ITopic } from './topic-ref'; @@ -13,25 +13,18 @@ export interface TopicPolicyProps { /** * Applies a policy to SNS topics. */ -export class TopicPolicy extends Construct implements IDependable { +export class TopicPolicy extends Construct { /** * The IAM policy document for this policy. */ public readonly document = new PolicyDocument(); - /** - * Allows topic policy to be added as a dependency. - */ - public readonly dependencyElements = new Array(); - constructor(scope: Construct, id: string, props: TopicPolicyProps) { super(scope, id); - const resource = new CfnTopicPolicy(this, 'Resource', { + new CfnTopicPolicy(this, 'Resource', { policyDocument: this.document, topics: props.topics.map(t => t.topicArn) }); - - this.dependencyElements.push(resource); } } diff --git a/packages/@aws-cdk/aws-sqs/lib/policy.ts b/packages/@aws-cdk/aws-sqs/lib/policy.ts index 4a70f34a5c3a7..9813cb60bc5e6 100644 --- a/packages/@aws-cdk/aws-sqs/lib/policy.ts +++ b/packages/@aws-cdk/aws-sqs/lib/policy.ts @@ -1,5 +1,5 @@ import { PolicyDocument } from '@aws-cdk/aws-iam'; -import { Construct, IDependable } from '@aws-cdk/cdk'; +import { Construct } from '@aws-cdk/cdk'; import { IQueue } from './queue-ref'; import { CfnQueuePolicy } from './sqs.generated'; @@ -13,25 +13,18 @@ export interface QueuePolicyProps { /** * Applies a policy to SQS queues. */ -export class QueuePolicy extends Construct implements IDependable { +export class QueuePolicy extends Construct { /** * The IAM policy document for this policy. */ public readonly document = new PolicyDocument(); - /** - * Allows adding QueuePolicy as a dependency. - */ - public readonly dependencyElements = new Array(); - constructor(scope: Construct, id: string, props: QueuePolicyProps) { super(scope, id); - const resource = new CfnQueuePolicy(this, 'Resource', { + new CfnQueuePolicy(this, 'Resource', { policyDocument: this.document, queues: props.queues.map(q => q.queueUrl) }); - - this.dependencyElements.push(resource); } } diff --git a/packages/@aws-cdk/aws-sqs/test/integ.bucket-notifications.expected.json b/packages/@aws-cdk/aws-sqs/test/integ.bucket-notifications.expected.json index 9419ad8279974..e68d5a4ecf63e 100644 --- a/packages/@aws-cdk/aws-sqs/test/integ.bucket-notifications.expected.json +++ b/packages/@aws-cdk/aws-sqs/test/integ.bucket-notifications.expected.json @@ -43,8 +43,8 @@ } }, "DependsOn": [ - "MyQueuePolicy6BBEDDAC", - "EncryptedQueuePolicy8AEB1708" + "EncryptedQueuePolicy8AEB1708", + "MyQueuePolicy6BBEDDAC" ] }, "MyQueueE6CA6235": { diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/resource.ts b/packages/@aws-cdk/cdk/lib/cloudformation/resource.ts index 9531ae25cf507..263fb57015dfd 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/resource.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/resource.ts @@ -1,11 +1,11 @@ import cxapi = require('@aws-cdk/cx-api'); -import { Construct } from '../core/construct'; +import { Construct, IConstruct } from '../core/construct'; import { TagManager } from '../core/tag-manager'; import { capitalizePropertyNames, ignoreEmpty } from '../core/util'; import { CfnReference } from './cfn-tokens'; import { Condition } from './condition'; import { CreationPolicy, DeletionPolicy, UpdatePolicy } from './resource-policy'; -import { IDependable, Referenceable, StackElement } from './stack-element'; +import { Referenceable } from './stack-element'; export interface ResourceProps { /** @@ -46,17 +46,17 @@ export class Resource extends Referenceable { } /** - * determines if the reosurce is taggable + * Check whether the given construct is a Resource */ - public static isTaggable(resource: any): resource is ITaggable { - return resource.tags !== undefined; + public static isResource(construct: IConstruct): construct is Resource { + return (construct as any).resourceType !== undefined; } /** - * determines if the reosurce is taggable + * Check whether the given construct is Taggable */ - public static isResource(resource: any): resource is Resource { - return resource.resourceType !== undefined; + public static isTaggable(construct: IConstruct): construct is ITaggable { + return (construct as any).tags !== undefined; } /** @@ -93,7 +93,12 @@ export class Resource extends Referenceable { */ private readonly rawOverrides: any = { }; - private dependsOn = new Array(); + /** + * Logical IDs of dependencies. + * + * Is filled during prepare(). + */ + private readonly dependsOn = new Set(); /** * Creates a resource construct. @@ -129,14 +134,6 @@ export class Resource extends Referenceable { return new CfnReference({ 'Fn::GetAtt': [this.logicalId, attributeName] }, `${this.logicalId}.${attributeName}`, this); } - /** - * Adds a dependency on another resource. - * @param other The other resource. - */ - public addDependency(...other: IDependable[]) { - this.dependsOn.push(...other); - } - /** * Adds an override to the synthesized CloudFormation resource. To add a * property override, either use `addPropertyOverride` or prefix `path` with @@ -196,6 +193,10 @@ export class Resource extends Referenceable { this.addPropertyOverride(propertyPath, undefined); } + public addDependsOn(resource: Resource) { + this.dependsOn.add(resource.logicalId); + } + /** * Emits CloudFormation for this resource. */ @@ -213,7 +214,8 @@ export class Resource extends Referenceable { [this.logicalId]: deepMerge({ Type: this.resourceType, Properties: ignoreEmpty(this, properties), - DependsOn: ignoreEmpty(this, this.renderDependsOn()), + // Return a sorted set of dependencies to be consistent across tests + DependsOn: ignoreEmpty(this, sortedSet(this.dependsOn)), CreationPolicy: capitalizePropertyNames(this, this.options.creationPolicy), UpdatePolicy: capitalizePropertyNames(this, this.options.updatePolicy), UpdateReplacePolicy: capitalizePropertyNames(this, this.options.updateReplacePolicy), @@ -239,30 +241,6 @@ export class Resource extends Referenceable { return properties; } - private renderDependsOn() { - const logicalIDs = new Set(); - for (const d of this.dependsOn) { - addDependency(d); - } - - return Array.from(logicalIDs); - - function addDependency(d: IDependable) { - d.dependencyElements.forEach(dep => { - const logicalId = (dep as StackElement).logicalId; - if (logicalId) { - logicalIDs.add(logicalId); - } - }); - - // break if dependencyElements include only 'd', which means we reached a terminal. - if (d.dependencyElements.length === 1 && d.dependencyElements[0] === d) { - return; - } else { - d.dependencyElements.forEach(dep => addDependency(dep)); - } - } - } } export enum TagType { @@ -352,3 +330,8 @@ 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 175c564aa8bfe..e4c1fc26daaec 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/stack-element.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/stack-element.ts @@ -2,21 +2,10 @@ import { Construct, IConstruct, PATH_SEP } from "../core/construct"; const LOGICAL_ID_MD = 'aws:cdk:logicalId'; -/** - * Represents a construct that can be "depended on" via `addDependency`. - */ -export interface IDependable { - /** - * Returns the set of all stack elements (resources, parameters, conditions) - * that should be added when a resource "depends on" this construct. - */ - readonly dependencyElements: IDependable[]; -} - /** * An element of a CloudFormation stack. */ -export abstract class StackElement extends Construct implements IDependable { +export abstract class StackElement extends Construct { /** * Returns `true` if a construct is a stack element (i.e. part of the * synthesized cloudformation template). @@ -93,10 +82,6 @@ export abstract class StackElement extends Construct implements IDependable { return this.node.ancestors(this.stack).map(c => c.node.id).join(PATH_SEP); } - public get dependencyElements(): IDependable[] { - return [ this ]; - } - /** * Returns the CloudFormation 'snippet' for this entity. The snippet will only be merged * at the root level to ensure there are no identity conflicts. @@ -146,9 +131,6 @@ export class Ref extends CfnReference { } } -import { findTokens } from "../core/tokens/resolve"; -import { Stack } from "./stack"; - /** * Base class for referenceable CloudFormation constructs which are not Resources * @@ -167,3 +149,6 @@ export abstract class Referenceable extends StackElement { return new Ref(this).toString(); } } + +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 0c0e76bc64347..f8ed92b837c7d 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/stack.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/stack.ts @@ -4,7 +4,6 @@ import { Construct, IConstruct } from '../core/construct'; import { Environment } from '../environment'; import { CfnReference } from './cfn-tokens'; import { HashedAddressingScheme, IAddressingScheme, LogicalIDs } from './logical-id'; -import { Resource } from './resource'; export interface StackProps { /** @@ -33,14 +32,24 @@ export class Stack extends Construct { * @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; } - - if (curr == null) { - throw new Error(`Cannot find a Stack parent for '${scope.toString()}'`); - } return curr; } @@ -400,13 +409,30 @@ export class Stack extends Construct { * Prepare stack * * Find all CloudFormation references and tell them we're consuming them. + * + * Find all dependencies as well and add the appropriate DependsOn fields. */ protected prepare() { + // References for (const ref of this.node.findReferences()) { if (CfnReference.isCfnReference(ref)) { ref.consumeFromStack(this); } } + + // Resource dependencies + for (const dependency of this.node.findDependencies()) { + const theirStack = Stack.tryFind(dependency.target); + if (theirStack !== undefined && theirStack !== this) { + this.addDependency(theirStack); + } else { + for (const target of findResources([dependency.target])) { + for (const source of findResources([dependency.source])) { + source.addDependsOn(target); + } + } + } + } } /** @@ -510,4 +536,16 @@ function stackElements(node: IConstruct, into: StackElement[] = []): StackElemen // These imports have to be at the end to prevent circular imports import { ArnComponents, arnFromComponents, parseArn } from './arn'; import { Aws } from './pseudo'; +import { Resource } from './resource'; import { StackElement } from './stack-element'; + +/** + * Find all resources in a set of constructs + */ +function findResources(roots: Iterable): Resource[] { + const ret = new Array(); + for (const root of roots) { + ret.push(...root.node.findAll().filter(Resource.isResource)); + } + return ret; +} diff --git a/packages/@aws-cdk/cdk/lib/core/construct.ts b/packages/@aws-cdk/cdk/lib/core/construct.ts index cf61f753ea553..02cc4e5dae16e 100644 --- a/packages/@aws-cdk/cdk/lib/core/construct.ts +++ b/packages/@aws-cdk/cdk/lib/core/construct.ts @@ -2,6 +2,7 @@ import cxapi = require('@aws-cdk/cx-api'); import { IAspect } from '../aspects/aspect'; import { CloudFormationJSON } from '../cloudformation/cloudformation-json'; import { makeUniqueId } from '../util/uniqueid'; +import { IDependable } from './dependency'; import { Token, unresolved } from './tokens'; import { resolve } from './tokens/resolve'; export const PATH_SEP = '/'; @@ -9,7 +10,7 @@ export const PATH_SEP = '/'; /** * Represents a construct. */ -export interface IConstruct { +export interface IConstruct extends IDependable { /** * The construct node in the scope tree. */ @@ -44,6 +45,7 @@ export class ConstructNode { private readonly context: { [key: string]: any } = { }; private readonly _metadata = new Array(); private readonly references = new Set(); + private readonly dependencies = new Set(); /** * If this is set to 'true'. addChild() calls for this construct and any child @@ -458,6 +460,42 @@ export class ConstructNode { return Array.from(ret); } + /** + * Add an ordering dependency on another Construct. + * + * All constructs in the dependency's scope will be deployed before any + * construct in this construct's scope. + */ + public addDependency(...dependencies: IDependable[]) { + for (const dependency of dependencies) { + this.dependencies.add(dependency); + } + } + + /** + * Return all dependencies registered on this node or any of its children + */ + public findDependencies(): Dependency[] { + const found = new Map>(); // Deduplication map + const ret = new Array(); + + for (const source of this.findAll()) { + for (const dependable of source.node.dependencies) { + for (const target of dependable.dependencyRoots) { + let foundTargets = found.get(source); + if (!foundTargets) { found.set(source, foundTargets = new Set()); } + + if (!foundTargets.has(target)) { + ret.push({ source, target }); + foundTargets.add(target); + } + } + } + } + + return ret; + } + /** * Triggers each aspect to invoke visit */ @@ -509,6 +547,14 @@ export class Construct implements IConstruct { */ public readonly node: ConstructNode; + /** + * The set of constructs that form the root of this dependable + * + * All resources under all returned constructs are included in the ordering + * dependency. + */ + public readonly dependencyRoots: IConstruct[] = [this]; + /** * Creates a new construct node. * @@ -632,3 +678,18 @@ export enum ConstructOrder { */ DepthFirst } + +/** + * A single dependency + */ +export interface Dependency { + /** + * Source the dependency + */ + source: IConstruct; + + /** + * Target of the dependency + */ + target: IConstruct; +} diff --git a/packages/@aws-cdk/cdk/lib/core/dependency.ts b/packages/@aws-cdk/cdk/lib/core/dependency.ts new file mode 100644 index 0000000000000..30a2137ae4590 --- /dev/null +++ b/packages/@aws-cdk/cdk/lib/core/dependency.ts @@ -0,0 +1,43 @@ +import { IConstruct } from "./construct"; + +/** + * A set of constructs that can be depended upon + * + * This interface can be used to take an (ordering) dependency on a set of + * constructs. An ordering dependency implies that the resources represented by + * those constructs are deployed before the resources depending ON them are + * deployed. + */ +export interface IDependable { + /** + * The set of constructs that form the root of this dependable + * + * All resources under all returned constructs are included in the ordering + * dependency. + */ + readonly dependencyRoots: IConstruct[]; +} + +/** + * A set of constructs to be used as a dependable + * + * This class can be used when a set of constructs which are disjoint in the + * construct tree needs to be combined to be used as a single dependable. + */ +export class ConcreteDependable implements IDependable { + private readonly _dependencyRoots = new Array(); + + /** + * Add a construct to the dependency roots + */ + public add(construct: IConstruct) { + this._dependencyRoots.push(construct); + } + + /** + * Retrieve the current set of dependency roots + */ + public get dependencyRoots(): IConstruct[] { + return this._dependencyRoots; + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/cdk/lib/core/util.ts b/packages/@aws-cdk/cdk/lib/core/util.ts index e083ddc5fc271..ddda5b59d7708 100644 --- a/packages/@aws-cdk/cdk/lib/core/util.ts +++ b/packages/@aws-cdk/cdk/lib/core/util.ts @@ -47,4 +47,4 @@ export function ignoreEmpty(construct: IConstruct, o: any): any { } return o; -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/cdk/lib/index.ts b/packages/@aws-cdk/cdk/lib/index.ts index c542da89697e6..49ffef7cdb7bf 100644 --- a/packages/@aws-cdk/cdk/lib/index.ts +++ b/packages/@aws-cdk/cdk/lib/index.ts @@ -4,6 +4,7 @@ export * from './aspects/tag-aspect'; export * from './core/construct'; export * from './core/tokens'; export * from './core/tag-manager'; +export * from './core/dependency'; export * from './cloudformation/cloudformation-json'; export * from './cloudformation/cfn-tokens'; 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 df25c9ba31414..7c63ac07ea106 100644 --- a/packages/@aws-cdk/cdk/test/cloudformation/test.logical-id.ts +++ b/packages/@aws-cdk/cdk/test/cloudformation/test.logical-id.ts @@ -198,9 +198,10 @@ const allSchemesTests: {[name: string]: (scheme: IAddressingScheme, test: Test) const ref = new Ref(c1); const c2 = new Resource(stack, 'Construct2', { type: 'R2', properties: { ReferenceToR1: ref } }); - c2.addDependency(c1); + c2.node.addDependency(c1); // THEN + stack.node.prepareTree(); test.deepEqual(stack.toCloudFormation(), { Resources: { [c1.logicalId]: { diff --git a/packages/@aws-cdk/cdk/test/cloudformation/test.resource.ts b/packages/@aws-cdk/cdk/test/cloudformation/test.resource.ts index 5e8724516c3ed..27b08e598343e 100644 --- a/packages/@aws-cdk/cdk/test/cloudformation/test.resource.ts +++ b/packages/@aws-cdk/cdk/test/cloudformation/test.resource.ts @@ -1,8 +1,8 @@ import cxapi = require('@aws-cdk/cx-api'); import { Test } from 'nodeunit'; -import { applyRemovalPolicy, Condition, Construct, DeletionPolicy, - Fn, HashedAddressingScheme, IDependable, - RemovalPolicy, Resource, Root, Stack } from '../../lib'; +import { App, applyRemovalPolicy, Condition, Construct, + DeletionPolicy, Fn, HashedAddressingScheme, RemovalPolicy, + Resource, Root, Stack } from '../../lib'; export = { 'all resources derive from Resource, which derives from Entity'(test: Test) { @@ -127,9 +127,10 @@ export = { const r1 = new Counter(stack, 'Counter1', { Count: 1 }); const r2 = new Counter(stack, 'Counter2', { Count: 1 }); const r3 = new Resource(stack, 'Resource3', { type: 'MyResourceType' }); - r2.addDependency(r1); - r2.addDependency(r3); + r2.node.addDependency(r1); + r2.node.addDependency(r3); + stack.node.prepareTree(); test.deepEqual(stack.toCloudFormation(), { Resources: { Counter1: { @@ -276,55 +277,35 @@ export = { 'addDependency adds all dependencyElements of dependent constructs'(test: Test) { - class C1 extends Construct implements IDependable { + class C1 extends Construct { public readonly r1: Resource; public readonly r2: Resource; - public readonly r3: Resource; constructor(scope: Construct, id: string) { super(scope, id); this.r1 = new Resource(this, 'R1', { type: 'T1' }); this.r2 = new Resource(this, 'R2', { type: 'T2' }); - this.r3 = new Resource(this, 'R3', { type: 'T3' }); - } - - get dependencyElements() { - return [ this.r1, this.r2 ]; } } - class C2 extends Construct implements IDependable { - public readonly r1: Resource; - public readonly r2: Resource; + class C2 extends Construct { public readonly r3: Resource; constructor(scope: Construct, id: string) { super(scope, id); - this.r1 = new Resource(this, 'R1', { type: 'T1' }); - this.r2 = new Resource(this, 'R2', { type: 'T2' }); this.r3 = new Resource(this, 'R3', { type: 'T3' }); } - - get dependencyElements() { - return [ this.r3 ]; - } } // C3 returns [ c2 ] for it's dependency elements // this should result in 'flattening' the list of elements. - class C3 extends Construct implements IDependable { - private readonly c2: C2; - + class C3 extends Construct { constructor(scope: Construct, id: string) { super(scope, id); - this.c2 = new C2(this, 'C2'); - } - - get dependencyElements() { - return [ this.c2 ]; + new C2(this, 'C2'); } } @@ -334,18 +315,14 @@ export = { const c3 = new C3(stack, 'MyC3'); const dependingResource = new Resource(stack, 'MyResource', { type: 'R' }); - dependingResource.addDependency(c1, c2); - dependingResource.addDependency(c3); + dependingResource.node.addDependency(c1, c2); + dependingResource.node.addDependency(c3); + stack.node.prepareTree(); test.deepEqual(stack.toCloudFormation(), { Resources: { MyC1R1FB2A562F: { Type: 'T1' }, MyC1R2AE2B5066: { Type: 'T2' }, - MyC1R374967D02: { Type: 'T3' }, - MyC2R13C9A618D: { Type: 'T1' }, - MyC2R25330F905: { Type: 'T2' }, MyC2R3809EEAD6: { Type: 'T3' }, - MyC3C2R1C64551A7: { Type: 'T1' }, - MyC3C2R2F213BD26: { Type: 'T2' }, MyC3C2R38CE6F9F7: { Type: 'T3' }, MyResource: { Type: 'R', @@ -604,7 +581,33 @@ export = { Metadata: { [cxapi.PATH_METADATA_KEY]: 'Parent/MyResource' } } } }); test.done(); - } + }, + + 'cross-stack construct dependencies are not rendered but turned into stack dependencies'(test: Test) { + // GIVEN + const app = new App(); + const stackA = new Stack(app, 'StackA'); + const resA = new Resource(stackA, 'Resource', { type: 'R' }); + const stackB = new Stack(app, 'StackB'); + const resB = new Resource(stackB, 'Resource', { type: 'R' }); + + // WHEN + resB.node.addDependency(resA); + + // THEN + app.node.prepareTree(); + test.deepEqual(stackB.toCloudFormation(), { + Resources: { + Resource: { + Type: 'R' + // Notice absence of 'DependsOn' + } + } + }); + test.deepEqual(stackB.dependencies().map(s => s.node.id), ['StackA']); + + test.done(); + }, }; interface CounterProps { diff --git a/packages/@aws-cdk/cloudformation-diff/lib/diff/index.ts b/packages/@aws-cdk/cloudformation-diff/lib/diff/index.ts index 1bcfd6d470970..14ef243f69c7b 100644 --- a/packages/@aws-cdk/cloudformation-diff/lib/diff/index.ts +++ b/packages/@aws-cdk/cloudformation-diff/lib/diff/index.ts @@ -41,6 +41,7 @@ export function diffResource(oldValue?: types.Resource, newValue?: types.Resourc propertyDiffs = diffKeyedEntities(oldValue!.Properties, newValue!.Properties, (oldVal, newVal, key) => _diffProperty(oldVal, newVal, key, impl)); + otherDiffs = diffKeyedEntities(oldValue, newValue, _diffOther); delete otherDiffs.Properties; } From fec19b65cdca498adb5228aff1737c125ad3c83e Mon Sep 17 00:00:00 2001 From: Romain Marcadier-Muller Date: Mon, 4 Feb 2019 15:32:38 +0100 Subject: [PATCH 29/38] v0.23.0 (#1665) See CHANGELOG --- CHANGELOG.md | 52 +++++++++++++ examples/cdk-examples-java/package.json | 6 +- examples/cdk-examples-typescript/package.json | 44 +++++------ lerna.json | 2 +- packages/@aws-cdk/alexa-ask/package.json | 20 ++--- packages/@aws-cdk/app-delivery/package.json | 34 ++++----- packages/@aws-cdk/applet-js/package.json | 8 +- packages/@aws-cdk/assert/package.json | 12 +-- packages/@aws-cdk/assets-docker/package.json | 34 ++++----- packages/@aws-cdk/assets/package.json | 26 +++---- packages/@aws-cdk/aws-amazonmq/package.json | 14 ++-- packages/@aws-cdk/aws-apigateway/package.json | 32 ++++---- .../aws-applicationautoscaling/package.json | 24 +++--- packages/@aws-cdk/aws-appstream/package.json | 14 ++-- packages/@aws-cdk/aws-appsync/package.json | 14 ++-- packages/@aws-cdk/aws-athena/package.json | 14 ++-- .../@aws-cdk/aws-autoscaling-api/package.json | 18 ++--- .../aws-autoscaling-common/package.json | 18 ++--- .../@aws-cdk/aws-autoscaling/package.json | 46 +++++------ .../aws-autoscalingplans/package.json | 14 ++-- packages/@aws-cdk/aws-batch/package.json | 14 ++-- packages/@aws-cdk/aws-budgets/package.json | 14 ++-- .../aws-certificatemanager/package.json | 16 ++-- packages/@aws-cdk/aws-cloud9/package.json | 14 ++-- .../@aws-cdk/aws-cloudformation/package.json | 34 ++++----- packages/@aws-cdk/aws-cloudfront/package.json | 30 ++++---- packages/@aws-cdk/aws-cloudtrail/package.json | 24 +++--- packages/@aws-cdk/aws-cloudwatch/package.json | 20 ++--- packages/@aws-cdk/aws-codebuild/package.json | 60 +++++++-------- packages/@aws-cdk/aws-codecommit/package.json | 28 +++---- .../@aws-cdk/aws-codedeploy-api/package.json | 12 +-- packages/@aws-cdk/aws-codedeploy/package.json | 44 +++++------ .../aws-codepipeline-api/package.json | 22 +++--- .../@aws-cdk/aws-codepipeline/package.json | 54 ++++++------- packages/@aws-cdk/aws-cognito/package.json | 14 ++-- packages/@aws-cdk/aws-config/package.json | 14 ++-- .../@aws-cdk/aws-datapipeline/package.json | 14 ++-- packages/@aws-cdk/aws-dax/package.json | 14 ++-- .../aws-directoryservice/package.json | 14 ++-- packages/@aws-cdk/aws-dlm/package.json | 14 ++-- packages/@aws-cdk/aws-dms/package.json | 14 ++-- packages/@aws-cdk/aws-docdb/package.json | 16 ++-- packages/@aws-cdk/aws-dynamodb/package.json | 24 +++--- packages/@aws-cdk/aws-ec2/package.json | 22 +++--- packages/@aws-cdk/aws-ecr/package.json | 28 +++---- packages/@aws-cdk/aws-ecs/package.json | 76 +++++++++---------- packages/@aws-cdk/aws-efs/package.json | 14 ++-- packages/@aws-cdk/aws-eks/package.json | 14 ++-- .../@aws-cdk/aws-elasticache/package.json | 14 ++-- .../aws-elasticbeanstalk/package.json | 14 ++-- .../aws-elasticloadbalancing/package.json | 24 +++--- .../aws-elasticloadbalancingv2/package.json | 38 +++++----- .../@aws-cdk/aws-elasticsearch/package.json | 14 ++-- packages/@aws-cdk/aws-emr/package.json | 14 ++-- packages/@aws-cdk/aws-events/package.json | 16 ++-- packages/@aws-cdk/aws-gamelift/package.json | 14 ++-- packages/@aws-cdk/aws-glue/package.json | 14 ++-- packages/@aws-cdk/aws-guardduty/package.json | 14 ++-- packages/@aws-cdk/aws-iam/package.json | 16 ++-- packages/@aws-cdk/aws-inspector/package.json | 14 ++-- packages/@aws-cdk/aws-iot/package.json | 14 ++-- packages/@aws-cdk/aws-iot1click/package.json | 14 ++-- .../@aws-cdk/aws-iotanalytics/package.json | 14 ++-- packages/@aws-cdk/aws-kinesis/package.json | 26 +++---- .../aws-kinesisanalytics/package.json | 14 ++-- .../@aws-cdk/aws-kinesisfirehose/package.json | 14 ++-- packages/@aws-cdk/aws-kms/package.json | 20 ++--- .../aws-lambda-event-sources/package.json | 42 +++++----- packages/@aws-cdk/aws-lambda/package.json | 62 +++++++-------- packages/@aws-cdk/aws-logs/package.json | 24 +++--- packages/@aws-cdk/aws-neptune/package.json | 14 ++-- packages/@aws-cdk/aws-opsworks/package.json | 14 ++-- packages/@aws-cdk/aws-opsworkscm/package.json | 16 ++-- .../@aws-cdk/aws-quickstarts/package.json | 20 ++--- packages/@aws-cdk/aws-rds/package.json | 24 +++--- packages/@aws-cdk/aws-redshift/package.json | 14 ++-- packages/@aws-cdk/aws-route53/package.json | 24 +++--- .../@aws-cdk/aws-route53resolver/package.json | 14 ++-- .../@aws-cdk/aws-s3-deployment/package.json | 26 +++---- .../aws-s3-notifications/package.json | 10 +-- packages/@aws-cdk/aws-s3/package.json | 38 +++++----- packages/@aws-cdk/aws-sagemaker/package.json | 14 ++-- packages/@aws-cdk/aws-sdb/package.json | 14 ++-- .../@aws-cdk/aws-secretsmanager/package.json | 14 ++-- packages/@aws-cdk/aws-serverless/package.json | 14 ++-- .../@aws-cdk/aws-servicecatalog/package.json | 14 ++-- .../aws-servicediscovery/package.json | 14 ++-- packages/@aws-cdk/aws-ses/package.json | 14 ++-- packages/@aws-cdk/aws-sns/package.json | 46 +++++------ packages/@aws-cdk/aws-sqs/package.json | 34 ++++----- packages/@aws-cdk/aws-ssm/package.json | 14 ++-- .../@aws-cdk/aws-stepfunctions/package.json | 28 +++---- packages/@aws-cdk/aws-waf/package.json | 14 ++-- .../@aws-cdk/aws-wafregional/package.json | 14 ++-- packages/@aws-cdk/aws-workspaces/package.json | 14 ++-- packages/@aws-cdk/cdk/package.json | 12 +-- packages/@aws-cdk/cfnspec/package.json | 6 +- .../@aws-cdk/cloudformation-diff/package.json | 18 ++--- packages/@aws-cdk/cx-api/package.json | 6 +- packages/@aws-cdk/runtime-values/package.json | 26 +++---- packages/aws-cdk/package.json | 14 ++-- packages/simple-resource-bundler/package.json | 6 +- tools/awslint/package.json | 2 +- tools/cdk-build-tools/package.json | 6 +- tools/cdk-integ-tools/package.json | 12 +-- tools/cfn2ts/package.json | 8 +- tools/pkglint/package.json | 2 +- tools/pkgtools/package.json | 6 +- tools/y-npm/package.json | 6 +- 109 files changed, 1116 insertions(+), 1064 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd5acc5903886..429d01f7e4a31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,58 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [0.23.0](https://github.com/awslabs/aws-cdk/compare/v0.22.0...v0.23.0) (2019-02-04) + + +### Bug Fixes + +* **apig:** Move `selectionPattern` to `integrationResponses` ([#1636](https://github.com/awslabs/aws-cdk/issues/1636)) ([7cdbcec](https://github.com/awslabs/aws-cdk/commit/7cdbcec)), closes [#1608](https://github.com/awslabs/aws-cdk/issues/1608) +* **aws-cdk:** Improvements to IAM diff rendering ([#1542](https://github.com/awslabs/aws-cdk/issues/1542)) ([3270b47](https://github.com/awslabs/aws-cdk/commit/3270b47)), closes [#1458](https://github.com/awslabs/aws-cdk/issues/1458) [#1495](https://github.com/awslabs/aws-cdk/issues/1495) [#1549](https://github.com/awslabs/aws-cdk/issues/1549) +* **aws-cdk:** Java init template works on Windows ([#1503](https://github.com/awslabs/aws-cdk/issues/1503)) ([24f521a](https://github.com/awslabs/aws-cdk/commit/24f521a)) +* **sns:** create subscription object under subscriber ([5c4a9e5](https://github.com/awslabs/aws-cdk/commit/5c4a9e5)), closes [#1643](https://github.com/awslabs/aws-cdk/issues/1643) [#1534](https://github.com/awslabs/aws-cdk/issues/1534) +* Improve error message in SSMParameterProvider ([#1630](https://github.com/awslabs/aws-cdk/issues/1630)) ([6a8e010](https://github.com/awslabs/aws-cdk/commit/6a8e010)), closes [#1621](https://github.com/awslabs/aws-cdk/issues/1621) +* **aws-ec2:** CfnNetworkAclEntry.CidrBlock should be optional ([#1565](https://github.com/awslabs/aws-cdk/issues/1565)) ([4af7c0d](https://github.com/awslabs/aws-cdk/commit/4af7c0d)), closes [#1517](https://github.com/awslabs/aws-cdk/issues/1517) +* **aws-ec2:** change maxAZs default for VPCs to 3 ([#1543](https://github.com/awslabs/aws-cdk/issues/1543)) ([32a4b29](https://github.com/awslabs/aws-cdk/commit/32a4b29)), closes [#996](https://github.com/awslabs/aws-cdk/issues/996) +* **aws-events:** ergonomics improvements to CloudWatch Events ([#1570](https://github.com/awslabs/aws-cdk/issues/1570)) ([5e91a0a](https://github.com/awslabs/aws-cdk/commit/5e91a0a)), closes [#1514](https://github.com/awslabs/aws-cdk/issues/1514) [#1198](https://github.com/awslabs/aws-cdk/issues/1198) [#1275](https://github.com/awslabs/aws-cdk/issues/1275) +* **aws-s3-deployment:** clean up tempfiles after deployment ([#1367](https://github.com/awslabs/aws-cdk/issues/1367)) ([e291d37](https://github.com/awslabs/aws-cdk/commit/e291d37)) +* **dynamodb:** grant also gives access to indexes ([#1564](https://github.com/awslabs/aws-cdk/issues/1564)) ([33c2a6d](https://github.com/awslabs/aws-cdk/commit/33c2a6d)), closes [#1540](https://github.com/awslabs/aws-cdk/issues/1540) +* Report stack metadata in assertions ([#1547](https://github.com/awslabs/aws-cdk/issues/1547)) ([c2d17f5](https://github.com/awslabs/aws-cdk/commit/c2d17f5)) + + +### Features + +* **alexa-ask:** Add deploy action for Alexa ([#1613](https://github.com/awslabs/aws-cdk/issues/1613)) ([0deea61](https://github.com/awslabs/aws-cdk/commit/0deea61)) +* **apigateway:** support function alias in LambdaIntegration ([9f8bfa5](https://github.com/awslabs/aws-cdk/commit/9f8bfa5)) +* **app:** add source map support to TS app template ([#1581](https://github.com/awslabs/aws-cdk/issues/1581)) ([5df22d9](https://github.com/awslabs/aws-cdk/commit/5df22d9)), closes [#1579](https://github.com/awslabs/aws-cdk/issues/1579) +* **autoscaling:** Support AssociatePublicIpAddress ([#1604](https://github.com/awslabs/aws-cdk/issues/1604)) ([23c9afc](https://github.com/awslabs/aws-cdk/commit/23c9afc)), closes [#1603](https://github.com/awslabs/aws-cdk/issues/1603) +* **aws-codepipeline:** support setting a Role for a CFN Action ([#1449](https://github.com/awslabs/aws-cdk/issues/1449)) ([77fe077](https://github.com/awslabs/aws-cdk/commit/77fe077)) +* **aws-ecs:** add additional configuration to Volume ([#1357](https://github.com/awslabs/aws-cdk/issues/1357)) ([ff96f3f](https://github.com/awslabs/aws-cdk/commit/ff96f3f)) +* **aws-ecs:** add support for Event Targets ([#1571](https://github.com/awslabs/aws-cdk/issues/1571)) ([aa68db5](https://github.com/awslabs/aws-cdk/commit/aa68db5)), closes [#1370](https://github.com/awslabs/aws-cdk/issues/1370) +* **aws-ecs:** ECS service scaling on ALB RequestCount ([#1574](https://github.com/awslabs/aws-cdk/issues/1574)) ([2b491d4](https://github.com/awslabs/aws-cdk/commit/2b491d4)) +* **aws-s3:** add the option to not poll to the CodePipeline Action. ([#1260](https://github.com/awslabs/aws-cdk/issues/1260)) ([876b26d](https://github.com/awslabs/aws-cdk/commit/876b26d)) +* **cdk:** Support UpdateReplacePolicy on Resources ([#1610](https://github.com/awslabs/aws-cdk/issues/1610)) ([f49c33b](https://github.com/awslabs/aws-cdk/commit/f49c33b)) +* **cdk:** treat the "fake" CFN intrinsics (Fn::GetArtifactAtt, Fn::GetParam) specially when stringifying JSON. ([#1605](https://github.com/awslabs/aws-cdk/issues/1605)) ([2af2426](https://github.com/awslabs/aws-cdk/commit/2af2426)), closes [#1588](https://github.com/awslabs/aws-cdk/issues/1588) +* **cfnspec:** Upgrade to CFN Resource Specification v2.21.0 ([#1622](https://github.com/awslabs/aws-cdk/issues/1622)) ([21a5529](https://github.com/awslabs/aws-cdk/commit/21a5529)) +* **cloudwatch:** Support 'datapointsToAlarm' on Alarms ([#1631](https://github.com/awslabs/aws-cdk/issues/1631)) ([828ac20](https://github.com/awslabs/aws-cdk/commit/828ac20)), closes [#1626](https://github.com/awslabs/aws-cdk/issues/1626) +* **core:** Generalization of dependencies ([#1583](https://github.com/awslabs/aws-cdk/issues/1583)) ([53e68257](https://github.com/awslabs/aws-cdk/commit/53e68257)) +* **ecs:** environment variables for LoadBalancedXxxService ([#1537](https://github.com/awslabs/aws-cdk/issues/1537)) ([b633505](https://github.com/awslabs/aws-cdk/commit/b633505)) +* **ecs:** VPC link for API Gatweay and ECS services ([#1541](https://github.com/awslabs/aws-cdk/issues/1541)) ([6642ca2](https://github.com/awslabs/aws-cdk/commit/6642ca2)) +* **iam:** Make `roleName` available on `IRole` ([#1589](https://github.com/awslabs/aws-cdk/issues/1589)) ([9128390](https://github.com/awslabs/aws-cdk/commit/9128390)) +* **lambda:** reserved concurrent executions ([#1560](https://github.com/awslabs/aws-cdk/issues/1560)) ([f7469c1](https://github.com/awslabs/aws-cdk/commit/f7469c1)) +* **lambda:** Support AWS Lambda Layers ([#1411](https://github.com/awslabs/aws-cdk/issues/1411)) ([036cfdf](https://github.com/awslabs/aws-cdk/commit/036cfdf)) +* **s3:** Add DeployAction for codepipeline ([#1596](https://github.com/awslabs/aws-cdk/issues/1596)) ([8f1a5e8](https://github.com/awslabs/aws-cdk/commit/8f1a5e8)) +* **s3:** export bucket websiteURL ([#1521](https://github.com/awslabs/aws-cdk/issues/1521)) ([#1544](https://github.com/awslabs/aws-cdk/issues/1544)) ([4e46d3c](https://github.com/awslabs/aws-cdk/commit/4e46d3c)) +* **s3:** imported bucket format option for website URL format ([#1550](https://github.com/awslabs/aws-cdk/issues/1550)) ([28a423d](https://github.com/awslabs/aws-cdk/commit/28a423d)) +* **toolkit:** disable colors if a terminal is not attached to stdout ([#1641](https://github.com/awslabs/aws-cdk/issues/1641)) ([58b4685](https://github.com/awslabs/aws-cdk/commit/58b4685)) + + +### BREAKING CHANGES + +* **aws-codepipeline:** the `role` property in the CloudFormation Actions has been renamed to `deploymentRole`. +* **aws-codepipeline:** the `role` property in the `app-delivery` package has been renamed to `deploymentRole`. + + + ## [0.22.0](https://github.com/awslabs/aws-cdk/compare/v0.21.0...v0.22.0) (2019-01-10) This is a major release with multiple breaking changes in the core layers. diff --git a/examples/cdk-examples-java/package.json b/examples/cdk-examples-java/package.json index 7ec8f9c8d191c..9f0c30ba77657 100644 --- a/examples/cdk-examples-java/package.json +++ b/examples/cdk-examples-java/package.json @@ -1,6 +1,6 @@ { "name": "cdk-examples-java", - "version": "0.22.0", + "version": "0.23.0", "description": "CDK examples in Java", "private": true, "repository": { @@ -22,7 +22,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "aws-cdk": "^0.22.0", - "pkgtools": "^0.22.0" + "aws-cdk": "^0.23.0", + "pkgtools": "^0.23.0" } } diff --git a/examples/cdk-examples-typescript/package.json b/examples/cdk-examples-typescript/package.json index 358e5badf0621..e66851053d7d1 100644 --- a/examples/cdk-examples-typescript/package.json +++ b/examples/cdk-examples-typescript/package.json @@ -1,6 +1,6 @@ { "name": "cdk-examples-typescript", - "version": "0.22.0", + "version": "0.23.0", "description": "A bunch of CDK examples", "private": true, "scripts": { @@ -18,29 +18,29 @@ }, "license": "Apache-2.0", "devDependencies": { - "aws-cdk": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "pkglint": "^0.22.0" + "aws-cdk": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-autoscaling": "^0.22.0", - "@aws-cdk/aws-cloudformation": "^0.22.0", - "@aws-cdk/aws-cognito": "^0.22.0", - "@aws-cdk/aws-dynamodb": "^0.22.0", - "@aws-cdk/aws-ec2": "^0.22.0", - "@aws-cdk/aws-ecs": "^0.22.0", - "@aws-cdk/aws-elasticloadbalancing": "^0.22.0", - "@aws-cdk/aws-elasticloadbalancingv2": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-lambda": "^0.22.0", - "@aws-cdk/aws-neptune": "^0.22.0", - "@aws-cdk/aws-rds": "^0.22.0", - "@aws-cdk/aws-s3": "^0.22.0", - "@aws-cdk/aws-sns": "^0.22.0", - "@aws-cdk/aws-events": "^0.22.0", - "@aws-cdk/aws-sqs": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0", - "@aws-cdk/runtime-values": "^0.22.0" + "@aws-cdk/aws-autoscaling": "^0.23.0", + "@aws-cdk/aws-cloudformation": "^0.23.0", + "@aws-cdk/aws-cognito": "^0.23.0", + "@aws-cdk/aws-dynamodb": "^0.23.0", + "@aws-cdk/aws-ec2": "^0.23.0", + "@aws-cdk/aws-ecs": "^0.23.0", + "@aws-cdk/aws-elasticloadbalancing": "^0.23.0", + "@aws-cdk/aws-elasticloadbalancingv2": "^0.23.0", + "@aws-cdk/aws-events": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-lambda": "^0.23.0", + "@aws-cdk/aws-neptune": "^0.23.0", + "@aws-cdk/aws-rds": "^0.23.0", + "@aws-cdk/aws-s3": "^0.23.0", + "@aws-cdk/aws-sns": "^0.23.0", + "@aws-cdk/aws-sqs": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0", + "@aws-cdk/runtime-values": "^0.23.0" }, "repository": { "url": "https://github.com/awslabs/aws-cdk.git", diff --git a/lerna.json b/lerna.json index f4eb639d2afc1..694009665e691 100644 --- a/lerna.json +++ b/lerna.json @@ -14,5 +14,5 @@ } }, "rejectCycles": "true", - "version": "0.22.0" + "version": "0.23.0" } diff --git a/packages/@aws-cdk/alexa-ask/package.json b/packages/@aws-cdk/alexa-ask/package.json index 6453f3fee4230..fa2eedb88b8d2 100644 --- a/packages/@aws-cdk/alexa-ask/package.json +++ b/packages/@aws-cdk/alexa-ask/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/alexa-ask", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for Alexa::ASK", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -55,20 +55,20 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-codepipeline-api": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-codepipeline-api": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "peerDependencies": { - "@aws-cdk/aws-codepipeline-api": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-codepipeline-api": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/app-delivery/package.json b/packages/@aws-cdk/app-delivery/package.json index 650f20cc14110..3ac67821ec36d 100644 --- a/packages/@aws-cdk/app-delivery/package.json +++ b/packages/@aws-cdk/app-delivery/package.json @@ -1,7 +1,7 @@ { "name": "@aws-cdk/app-delivery", "description": "Continuous Integration / Continuous Delivery for CDK Applications", - "version": "0.22.0", + "version": "0.23.0", "main": "lib/index.js", "types": "lib/index.d.ts", "jsii": { @@ -33,21 +33,21 @@ "awslint": "cdk-awslint" }, "dependencies": { - "@aws-cdk/aws-cloudformation": "^0.22.0", - "@aws-cdk/aws-codebuild": "^0.22.0", - "@aws-cdk/aws-codepipeline-api": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0", - "@aws-cdk/cx-api": "^0.22.0" + "@aws-cdk/aws-cloudformation": "^0.23.0", + "@aws-cdk/aws-codebuild": "^0.23.0", + "@aws-cdk/aws-codepipeline-api": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0", + "@aws-cdk/cx-api": "^0.23.0" }, "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "@aws-cdk/aws-codepipeline": "^0.22.0", - "@aws-cdk/aws-s3": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", + "@aws-cdk/assert": "^0.23.0", + "@aws-cdk/aws-codepipeline": "^0.23.0", + "@aws-cdk/aws-s3": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", "fast-check": "^1.7.0", - "pkglint": "^0.22.0" + "pkglint": "^0.23.0" }, "repository": { "type": "git", @@ -65,10 +65,10 @@ "cdk" ], "peerDependencies": { - "@aws-cdk/aws-cloudformation": "^0.22.0", - "@aws-cdk/aws-codepipeline-api": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-cloudformation": "^0.23.0", + "@aws-cdk/aws-codepipeline-api": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/applet-js/package.json b/packages/@aws-cdk/applet-js/package.json index a2842d91d1cef..9394453d7e2c0 100644 --- a/packages/@aws-cdk/applet-js/package.json +++ b/packages/@aws-cdk/applet-js/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/applet-js", - "version": "0.22.0", + "version": "0.23.0", "description": "Javascript CDK applet host program", "main": "bin/cdk-applet-js.js", "types": "bin/cdk-applet-js.d.ts", @@ -24,11 +24,11 @@ "devDependencies": { "@types/fs-extra": "^5.0.4", "@types/yaml": "^1.0.0", - "cdk-build-tools": "^0.22.0", - "pkglint": "^0.22.0" + "cdk-build-tools": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0", + "@aws-cdk/cdk": "^0.23.0", "fs-extra": "^7.0.0", "source-map-support": "^0.5.6", "yaml": "^1.1.0" diff --git a/packages/@aws-cdk/assert/package.json b/packages/@aws-cdk/assert/package.json index 98ecdb8bf4f44..47c41cc492c16 100644 --- a/packages/@aws-cdk/assert/package.json +++ b/packages/@aws-cdk/assert/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/assert", - "version": "0.22.0", + "version": "0.23.0", "description": "An assertion library for use with CDK Apps", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -23,13 +23,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "cdk-build-tools": "^0.22.0", - "pkglint": "^0.22.0" + "cdk-build-tools": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0", - "@aws-cdk/cloudformation-diff": "^0.22.0", - "@aws-cdk/cx-api": "^0.22.0", + "@aws-cdk/cdk": "^0.23.0", + "@aws-cdk/cloudformation-diff": "^0.23.0", + "@aws-cdk/cx-api": "^0.23.0", "source-map-support": "^0.5.6" }, "repository": { diff --git a/packages/@aws-cdk/assets-docker/package.json b/packages/@aws-cdk/assets-docker/package.json index f44ca1d43c220..3d3decf090032 100644 --- a/packages/@aws-cdk/assets-docker/package.json +++ b/packages/@aws-cdk/assets-docker/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/assets-docker", - "version": "0.22.0", + "version": "0.23.0", "description": "Docker image assets", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -51,29 +51,29 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", + "@aws-cdk/assert": "^0.23.0", "@types/proxyquire": "^1.3.28", - "aws-cdk": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "pkglint": "^0.22.0", + "aws-cdk": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "pkglint": "^0.23.0", "proxyquire": "^2.1.0" }, "dependencies": { - "@aws-cdk/aws-cloudformation": "^0.22.0", - "@aws-cdk/aws-ecr": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-lambda": "^0.22.0", - "@aws-cdk/aws-s3": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0", - "@aws-cdk/cx-api": "^0.22.0" + "@aws-cdk/aws-cloudformation": "^0.23.0", + "@aws-cdk/aws-ecr": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-lambda": "^0.23.0", + "@aws-cdk/aws-s3": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0", + "@aws-cdk/cx-api": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-ecr": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-s3": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-ecr": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-s3": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/assets/package.json b/packages/@aws-cdk/assets/package.json index 80e5eb76a952f..57b5aab45790a 100644 --- a/packages/@aws-cdk/assets/package.json +++ b/packages/@aws-cdk/assets/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/assets", - "version": "0.22.0", + "version": "0.23.0", "description": "Integration of CDK apps with local assets", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -50,23 +50,23 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "aws-cdk": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "aws-cdk": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-s3": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0", - "@aws-cdk/cx-api": "^0.22.0" + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-s3": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0", + "@aws-cdk/cx-api": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-s3": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-s3": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-amazonmq/package.json b/packages/@aws-cdk/aws-amazonmq/package.json index 53a4946e08553..69c2a15fd91d4 100644 --- a/packages/@aws-cdk/aws-amazonmq/package.json +++ b/packages/@aws-cdk/aws-amazonmq/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-amazonmq", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::AmazonMQ", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -55,16 +55,16 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-apigateway/package.json b/packages/@aws-cdk/aws-apigateway/package.json index 036da9b081567..a2c3a3b8c5ed1 100644 --- a/packages/@aws-cdk/aws-apigateway/package.json +++ b/packages/@aws-cdk/aws-apigateway/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-apigateway", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::ApiGateway", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,25 +54,25 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "@aws-cdk/aws-ec2": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "@aws-cdk/aws-ec2": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-lambda": "^0.22.0", - "@aws-cdk/aws-elasticloadbalancingv2": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-elasticloadbalancingv2": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-lambda": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-lambda": "^0.22.0", - "@aws-cdk/aws-elasticloadbalancingv2": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-elasticloadbalancingv2": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-lambda": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" @@ -82,4 +82,4 @@ "resource-attribute:@aws-cdk/aws-apigateway.IRestApi.restApiRootResourceId" ] } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-applicationautoscaling/package.json b/packages/@aws-cdk/aws-applicationautoscaling/package.json index 62062e9237b8c..53e9cde095014 100644 --- a/packages/@aws-cdk/aws-applicationautoscaling/package.json +++ b/packages/@aws-cdk/aws-applicationautoscaling/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-applicationautoscaling", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::ApplicationAutoScaling", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,23 +54,23 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", "fast-check": "^1.7.0", - "pkglint": "^0.22.0" + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-autoscaling-common": "^0.22.0", - "@aws-cdk/aws-cloudwatch": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-autoscaling-common": "^0.23.0", + "@aws-cdk/aws-cloudwatch": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-cloudwatch": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-cloudwatch": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-appstream/package.json b/packages/@aws-cdk/aws-appstream/package.json index 2c5e021a51746..55e4d358584f3 100644 --- a/packages/@aws-cdk/aws-appstream/package.json +++ b/packages/@aws-cdk/aws-appstream/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-appstream", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::AppStream", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -55,16 +55,16 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-appsync/package.json b/packages/@aws-cdk/aws-appsync/package.json index 77a5c55d5bdc7..085c11951e4b7 100644 --- a/packages/@aws-cdk/aws-appsync/package.json +++ b/packages/@aws-cdk/aws-appsync/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-appsync", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::AppSync", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-athena/package.json b/packages/@aws-cdk/aws-athena/package.json index c5aef6b376ae9..9f01626cbb079 100644 --- a/packages/@aws-cdk/aws-athena/package.json +++ b/packages/@aws-cdk/aws-athena/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-athena", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::Athena", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-autoscaling-api/package.json b/packages/@aws-cdk/aws-autoscaling-api/package.json index c0d9c6c877fe1..aa336af49cc2b 100644 --- a/packages/@aws-cdk/aws-autoscaling-api/package.json +++ b/packages/@aws-cdk/aws-autoscaling-api/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-autoscaling-api", - "version": "0.22.0", + "version": "0.23.0", "description": "API package for @aws-cdk/aws-autoscaling", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -60,19 +60,19 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-autoscaling-common/package.json b/packages/@aws-cdk/aws-autoscaling-common/package.json index 8f4d51ea6aba1..f97b13e698d88 100644 --- a/packages/@aws-cdk/aws-autoscaling-common/package.json +++ b/packages/@aws-cdk/aws-autoscaling-common/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-autoscaling-common", - "version": "0.22.0", + "version": "0.23.0", "description": "Common implementation package for @aws-cdk/aws-autoscaling and @aws-cdk/aws-applicationautoscaling", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -50,20 +50,20 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", "fast-check": "^1.7.0", - "pkglint": "^0.22.0" + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-autoscaling/package.json b/packages/@aws-cdk/aws-autoscaling/package.json index b1370103e8380..cfbf20129bbb8 100644 --- a/packages/@aws-cdk/aws-autoscaling/package.json +++ b/packages/@aws-cdk/aws-autoscaling/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-autoscaling", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::AutoScaling", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,33 +54,33 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-autoscaling-api": "^0.22.0", - "@aws-cdk/aws-autoscaling-common": "^0.22.0", - "@aws-cdk/aws-cloudwatch": "^0.22.0", - "@aws-cdk/aws-ec2": "^0.22.0", - "@aws-cdk/aws-elasticloadbalancing": "^0.22.0", - "@aws-cdk/aws-elasticloadbalancingv2": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-sns": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-autoscaling-api": "^0.23.0", + "@aws-cdk/aws-autoscaling-common": "^0.23.0", + "@aws-cdk/aws-cloudwatch": "^0.23.0", + "@aws-cdk/aws-ec2": "^0.23.0", + "@aws-cdk/aws-elasticloadbalancing": "^0.23.0", + "@aws-cdk/aws-elasticloadbalancingv2": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-sns": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-autoscaling-api": "^0.22.0", - "@aws-cdk/aws-cloudwatch": "^0.22.0", - "@aws-cdk/aws-ec2": "^0.22.0", - "@aws-cdk/aws-elasticloadbalancing": "^0.22.0", - "@aws-cdk/aws-elasticloadbalancingv2": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-sns": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-autoscaling-api": "^0.23.0", + "@aws-cdk/aws-cloudwatch": "^0.23.0", + "@aws-cdk/aws-ec2": "^0.23.0", + "@aws-cdk/aws-elasticloadbalancing": "^0.23.0", + "@aws-cdk/aws-elasticloadbalancingv2": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-sns": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-autoscalingplans/package.json b/packages/@aws-cdk/aws-autoscalingplans/package.json index 26138b97eb832..5b9fdec13da5b 100644 --- a/packages/@aws-cdk/aws-autoscalingplans/package.json +++ b/packages/@aws-cdk/aws-autoscalingplans/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-autoscalingplans", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::AutoScalingPlans", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-batch/package.json b/packages/@aws-cdk/aws-batch/package.json index 365c62ce0809f..6cb174d8815b7 100644 --- a/packages/@aws-cdk/aws-batch/package.json +++ b/packages/@aws-cdk/aws-batch/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-batch", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::Batch", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-budgets/package.json b/packages/@aws-cdk/aws-budgets/package.json index a1428b4776f0f..f22f9774255f0 100644 --- a/packages/@aws-cdk/aws-budgets/package.json +++ b/packages/@aws-cdk/aws-budgets/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-budgets", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::Budgets", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-certificatemanager/package.json b/packages/@aws-cdk/aws-certificatemanager/package.json index b95ca2b99caec..8ab015b145a9d 100644 --- a/packages/@aws-cdk/aws-certificatemanager/package.json +++ b/packages/@aws-cdk/aws-certificatemanager/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-certificatemanager", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::CertificateManager", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,18 +54,18 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-cloud9/package.json b/packages/@aws-cdk/aws-cloud9/package.json index 570a0d128b405..7c1a289293b61 100644 --- a/packages/@aws-cdk/aws-cloud9/package.json +++ b/packages/@aws-cdk/aws-cloud9/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-cloud9", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::Cloud9", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-cloudformation/package.json b/packages/@aws-cdk/aws-cloudformation/package.json index 8cd41a7ca1360..7fa075978c0b0 100644 --- a/packages/@aws-cdk/aws-cloudformation/package.json +++ b/packages/@aws-cdk/aws-cloudformation/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-cloudformation", - "version": "0.22.0", + "version": "0.23.0", "description": "CDK Constructs for AWS CloudFormation", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -58,29 +58,29 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "@aws-cdk/aws-events": "^0.22.0", + "@aws-cdk/assert": "^0.23.0", + "@aws-cdk/aws-events": "^0.23.0", "@types/lodash": "^4.14.118", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "cfn2ts": "^0.22.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "cfn2ts": "^0.23.0", "lodash": "^4.17.11", - "pkglint": "^0.22.0" + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-codepipeline-api": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-lambda": "^0.22.0", - "@aws-cdk/aws-sns": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-codepipeline-api": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-lambda": "^0.23.0", + "@aws-cdk/aws-sns": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-codepipeline-api": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-lambda": "^0.22.0", - "@aws-cdk/aws-sns": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-codepipeline-api": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-lambda": "^0.23.0", + "@aws-cdk/aws-sns": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-cloudfront/package.json b/packages/@aws-cdk/aws-cloudfront/package.json index 2cd7ebb56e858..33b1c804a82ad 100644 --- a/packages/@aws-cdk/aws-cloudfront/package.json +++ b/packages/@aws-cdk/aws-cloudfront/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-cloudfront", - "version": "0.22.0", + "version": "0.23.0", "description": "CDK Constructs for AWS CloudFront", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,26 +54,26 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", + "@aws-cdk/assert": "^0.23.0", "aws-sdk": "^2.259.1", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-certificatemanager": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-kms": "^0.22.0", - "@aws-cdk/aws-route53": "^0.22.0", - "@aws-cdk/aws-s3": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-certificatemanager": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-kms": "^0.23.0", + "@aws-cdk/aws-route53": "^0.23.0", + "@aws-cdk/aws-s3": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-route53": "^0.22.0", - "@aws-cdk/aws-s3": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-route53": "^0.23.0", + "@aws-cdk/aws-s3": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-cloudtrail/package.json b/packages/@aws-cdk/aws-cloudtrail/package.json index 2c90c17439283..f64061858bb39 100644 --- a/packages/@aws-cdk/aws-cloudtrail/package.json +++ b/packages/@aws-cdk/aws-cloudtrail/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-cloudtrail", - "version": "0.22.0", + "version": "0.23.0", "description": "CDK Constructs for AWS CloudTrail", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -53,24 +53,24 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", + "@aws-cdk/assert": "^0.23.0", "aws-sdk": "^2.259.1", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", "colors": "^1.2.1", - "pkglint": "^0.22.0" + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-kms": "^0.22.0", - "@aws-cdk/aws-logs": "^0.22.0", - "@aws-cdk/aws-s3": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-kms": "^0.23.0", + "@aws-cdk/aws-logs": "^0.23.0", + "@aws-cdk/aws-s3": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-kms": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-kms": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-cloudwatch/package.json b/packages/@aws-cdk/aws-cloudwatch/package.json index 969dacf8f307d..1ef68524eeff0 100644 --- a/packages/@aws-cdk/aws-cloudwatch/package.json +++ b/packages/@aws-cdk/aws-cloudwatch/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-cloudwatch", - "version": "0.22.0", + "version": "0.23.0", "description": "CDK Constructs for AWS CloudWatch", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,20 +54,20 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-codebuild/package.json b/packages/@aws-cdk/aws-codebuild/package.json index 657ad766b7d78..84622a08065db 100644 --- a/packages/@aws-cdk/aws-codebuild/package.json +++ b/packages/@aws-cdk/aws-codebuild/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-codebuild", - "version": "0.22.0", + "version": "0.23.0", "description": "CDK Constructs for AWS CodeBuild", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -57,41 +57,41 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "@aws-cdk/aws-sns": "^0.22.0", - "@aws-cdk/aws-sqs": "^0.22.0", + "@aws-cdk/assert": "^0.23.0", + "@aws-cdk/aws-sns": "^0.23.0", + "@aws-cdk/aws-sqs": "^0.23.0", "aws-sdk": "^2.259.1", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/assets": "^0.22.0", - "@aws-cdk/assets-docker": "^0.22.0", - "@aws-cdk/aws-cloudwatch": "^0.22.0", - "@aws-cdk/aws-codecommit": "^0.22.0", - "@aws-cdk/aws-codepipeline-api": "^0.22.0", - "@aws-cdk/aws-ecr": "^0.22.0", - "@aws-cdk/aws-events": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-kms": "^0.22.0", - "@aws-cdk/aws-s3": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/assets": "^0.23.0", + "@aws-cdk/assets-docker": "^0.23.0", + "@aws-cdk/aws-cloudwatch": "^0.23.0", + "@aws-cdk/aws-codecommit": "^0.23.0", + "@aws-cdk/aws-codepipeline-api": "^0.23.0", + "@aws-cdk/aws-ecr": "^0.23.0", + "@aws-cdk/aws-events": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-kms": "^0.23.0", + "@aws-cdk/aws-s3": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/assets": "^0.22.0", - "@aws-cdk/assets-docker": "^0.22.0", - "@aws-cdk/aws-cloudwatch": "^0.22.0", - "@aws-cdk/aws-codecommit": "^0.22.0", - "@aws-cdk/aws-codepipeline-api": "^0.22.0", - "@aws-cdk/aws-ecr": "^0.22.0", - "@aws-cdk/aws-events": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-kms": "^0.22.0", - "@aws-cdk/aws-s3": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/assets": "^0.23.0", + "@aws-cdk/assets-docker": "^0.23.0", + "@aws-cdk/aws-cloudwatch": "^0.23.0", + "@aws-cdk/aws-codecommit": "^0.23.0", + "@aws-cdk/aws-codepipeline-api": "^0.23.0", + "@aws-cdk/aws-ecr": "^0.23.0", + "@aws-cdk/aws-events": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-kms": "^0.23.0", + "@aws-cdk/aws-s3": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-codecommit/package.json b/packages/@aws-cdk/aws-codecommit/package.json index 875694c99bdff..b2584e0b0f8fa 100644 --- a/packages/@aws-cdk/aws-codecommit/package.json +++ b/packages/@aws-cdk/aws-codecommit/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-codecommit", - "version": "0.22.0", + "version": "0.23.0", "description": "CDK Constructs for AWS CodeCommit", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -58,25 +58,25 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "@aws-cdk/aws-sns": "^0.22.0", + "@aws-cdk/assert": "^0.23.0", + "@aws-cdk/aws-sns": "^0.23.0", "aws-sdk": "^2.259.1", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-codepipeline-api": "^0.22.0", - "@aws-cdk/aws-events": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-codepipeline-api": "^0.23.0", + "@aws-cdk/aws-events": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-codepipeline-api": "^0.22.0", - "@aws-cdk/aws-events": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-codepipeline-api": "^0.23.0", + "@aws-cdk/aws-events": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-codedeploy-api/package.json b/packages/@aws-cdk/aws-codedeploy-api/package.json index 900ae95113903..f72f5a7dd5da1 100644 --- a/packages/@aws-cdk/aws-codedeploy-api/package.json +++ b/packages/@aws-cdk/aws-codedeploy-api/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-codedeploy-api", - "version": "0.22.0", + "version": "0.23.0", "description": "Load Balancer API for AWS CodeDeploy", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -50,13 +50,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "engines": { diff --git a/packages/@aws-cdk/aws-codedeploy/package.json b/packages/@aws-cdk/aws-codedeploy/package.json index a925017b2b722..31b3c9d4a1252 100644 --- a/packages/@aws-cdk/aws-codedeploy/package.json +++ b/packages/@aws-cdk/aws-codedeploy/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-codedeploy", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::CodeDeploy", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,32 +54,32 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "@aws-cdk/aws-ec2": "^0.22.0", - "@aws-cdk/aws-elasticloadbalancing": "^0.22.0", - "@aws-cdk/aws-elasticloadbalancingv2": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "@aws-cdk/aws-ec2": "^0.23.0", + "@aws-cdk/aws-elasticloadbalancing": "^0.23.0", + "@aws-cdk/aws-elasticloadbalancingv2": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-autoscaling": "^0.22.0", - "@aws-cdk/aws-cloudwatch": "^0.22.0", - "@aws-cdk/aws-codedeploy-api": "^0.22.0", - "@aws-cdk/aws-codepipeline-api": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-s3": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-autoscaling": "^0.23.0", + "@aws-cdk/aws-cloudwatch": "^0.23.0", + "@aws-cdk/aws-codedeploy-api": "^0.23.0", + "@aws-cdk/aws-codepipeline-api": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-s3": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-autoscaling": "^0.22.0", - "@aws-cdk/aws-cloudwatch": "^0.22.0", - "@aws-cdk/aws-codedeploy-api": "^0.22.0", - "@aws-cdk/aws-codepipeline-api": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-autoscaling": "^0.23.0", + "@aws-cdk/aws-cloudwatch": "^0.23.0", + "@aws-cdk/aws-codedeploy-api": "^0.23.0", + "@aws-cdk/aws-codepipeline-api": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-codepipeline-api/package.json b/packages/@aws-cdk/aws-codepipeline-api/package.json index 312ed5295345d..0353a4df906c9 100644 --- a/packages/@aws-cdk/aws-codepipeline-api/package.json +++ b/packages/@aws-cdk/aws-codepipeline-api/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-codepipeline-api", - "version": "0.22.0", + "version": "0.23.0", "description": "Actions API for AWS Code Pipeline", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -53,21 +53,21 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-events": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-events": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-events": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-events": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-codepipeline/package.json b/packages/@aws-cdk/aws-codepipeline/package.json index 6b1a4d7473858..4f8873774b235 100644 --- a/packages/@aws-cdk/aws-codepipeline/package.json +++ b/packages/@aws-cdk/aws-codepipeline/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-codepipeline", - "version": "0.22.0", + "version": "0.23.0", "description": "Better interface to AWS Code Pipeline", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -60,36 +60,36 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/alexa-ask": "^0.22.0", - "@aws-cdk/assert": "^0.22.0", - "@aws-cdk/aws-cloudformation": "^0.22.0", - "@aws-cdk/aws-cloudtrail": "^0.22.0", - "@aws-cdk/aws-codebuild": "^0.22.0", - "@aws-cdk/aws-codecommit": "^0.22.0", - "@aws-cdk/aws-codedeploy": "^0.22.0", - "@aws-cdk/aws-ecr": "^0.22.0", - "@aws-cdk/aws-lambda": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/alexa-ask": "^0.23.0", + "@aws-cdk/assert": "^0.23.0", + "@aws-cdk/aws-cloudformation": "^0.23.0", + "@aws-cdk/aws-cloudtrail": "^0.23.0", + "@aws-cdk/aws-codebuild": "^0.23.0", + "@aws-cdk/aws-codecommit": "^0.23.0", + "@aws-cdk/aws-codedeploy": "^0.23.0", + "@aws-cdk/aws-ecr": "^0.23.0", + "@aws-cdk/aws-lambda": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-codepipeline-api": "^0.22.0", - "@aws-cdk/aws-events": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-s3": "^0.22.0", - "@aws-cdk/aws-sns": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-codepipeline-api": "^0.23.0", + "@aws-cdk/aws-events": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-s3": "^0.23.0", + "@aws-cdk/aws-sns": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-codepipeline-api": "^0.22.0", - "@aws-cdk/aws-events": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-s3": "^0.22.0", - "@aws-cdk/aws-sns": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-codepipeline-api": "^0.23.0", + "@aws-cdk/aws-events": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-s3": "^0.23.0", + "@aws-cdk/aws-sns": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" @@ -100,4 +100,4 @@ "construct-ctor:@aws-cdk/aws-codepipeline.CrossRegionScaffoldStack..params[1]" ] } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cognito/package.json b/packages/@aws-cdk/aws-cognito/package.json index de6592a5f5600..dca22bc36e826 100644 --- a/packages/@aws-cdk/aws-cognito/package.json +++ b/packages/@aws-cdk/aws-cognito/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-cognito", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::Cognito", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-config/package.json b/packages/@aws-cdk/aws-config/package.json index f75b102332131..a0ebbea9faf4e 100644 --- a/packages/@aws-cdk/aws-config/package.json +++ b/packages/@aws-cdk/aws-config/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-config", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::Config", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-datapipeline/package.json b/packages/@aws-cdk/aws-datapipeline/package.json index 91c182c300c58..a2d9459633435 100644 --- a/packages/@aws-cdk/aws-datapipeline/package.json +++ b/packages/@aws-cdk/aws-datapipeline/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-datapipeline", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::DataPipeline", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-dax/package.json b/packages/@aws-cdk/aws-dax/package.json index f4a6bcfef7fb6..d28a9d6270f2e 100644 --- a/packages/@aws-cdk/aws-dax/package.json +++ b/packages/@aws-cdk/aws-dax/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-dax", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::DAX", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-directoryservice/package.json b/packages/@aws-cdk/aws-directoryservice/package.json index 033afbafe6ab3..0d73806585531 100644 --- a/packages/@aws-cdk/aws-directoryservice/package.json +++ b/packages/@aws-cdk/aws-directoryservice/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-directoryservice", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::DirectoryService", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-dlm/package.json b/packages/@aws-cdk/aws-dlm/package.json index f56b5983b7b07..d9d53f157eac5 100644 --- a/packages/@aws-cdk/aws-dlm/package.json +++ b/packages/@aws-cdk/aws-dlm/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-dlm", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::DLM", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -55,16 +55,16 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-dms/package.json b/packages/@aws-cdk/aws-dms/package.json index cefb09c72ea77..9b28f76d584a8 100644 --- a/packages/@aws-cdk/aws-dms/package.json +++ b/packages/@aws-cdk/aws-dms/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-dms", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::DMS", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-docdb/package.json b/packages/@aws-cdk/aws-docdb/package.json index 90a7980b0421b..4af8ffe82cf54 100644 --- a/packages/@aws-cdk/aws-docdb/package.json +++ b/packages/@aws-cdk/aws-docdb/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-docdb", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::DocDB", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -56,18 +56,18 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-dynamodb/package.json b/packages/@aws-cdk/aws-dynamodb/package.json index 9a449fedc49a1..63a50906c062f 100644 --- a/packages/@aws-cdk/aws-dynamodb/package.json +++ b/packages/@aws-cdk/aws-dynamodb/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-dynamodb", - "version": "0.22.0", + "version": "0.23.0", "description": "CDK Constructs for AWS DynamoDB", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,22 +54,22 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-applicationautoscaling": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-applicationautoscaling": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-applicationautoscaling": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-applicationautoscaling": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-ec2/package.json b/packages/@aws-cdk/aws-ec2/package.json index 3032b95da497d..493bd557c3588 100644 --- a/packages/@aws-cdk/aws-ec2/package.json +++ b/packages/@aws-cdk/aws-ec2/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-ec2", - "version": "0.22.0", + "version": "0.23.0", "description": "CDK Constructs for AWS EC2", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,21 +54,21 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0", - "@aws-cdk/cx-api": "^0.22.0" + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0", + "@aws-cdk/cx-api": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0", - "@aws-cdk/cx-api": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0", + "@aws-cdk/cx-api": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-ecr/package.json b/packages/@aws-cdk/aws-ecr/package.json index 3293a5b602b2e..ff1a4503faba3 100644 --- a/packages/@aws-cdk/aws-ecr/package.json +++ b/packages/@aws-cdk/aws-ecr/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-ecr", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::ECR", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,24 +54,24 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-codepipeline-api": "^0.22.0", - "@aws-cdk/aws-events": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-codepipeline-api": "^0.23.0", + "@aws-cdk/aws-events": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-codepipeline-api": "^0.22.0", - "@aws-cdk/aws-events": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-codepipeline-api": "^0.23.0", + "@aws-cdk/aws-events": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-ecs/package.json b/packages/@aws-cdk/aws-ecs/package.json index da5c7fbc9edb6..5b66ff3f3bff6 100644 --- a/packages/@aws-cdk/aws-ecs/package.json +++ b/packages/@aws-cdk/aws-ecs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-ecs", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::ECS", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,50 +54,50 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", + "@aws-cdk/assert": "^0.23.0", "@types/proxyquire": "^1.3.28", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0", "proxyquire": "^2.1.0" }, "dependencies": { - "@aws-cdk/assets-docker": "^0.22.0", - "@aws-cdk/aws-applicationautoscaling": "^0.22.0", - "@aws-cdk/aws-autoscaling": "^0.22.0", - "@aws-cdk/aws-certificatemanager": "^0.22.0", - "@aws-cdk/aws-cloudformation": "^0.22.0", - "@aws-cdk/aws-cloudwatch": "^0.22.0", - "@aws-cdk/aws-ec2": "^0.22.0", - "@aws-cdk/aws-ecr": "^0.22.0", - "@aws-cdk/aws-events": "^0.22.0", - "@aws-cdk/aws-elasticloadbalancing": "^0.22.0", - "@aws-cdk/aws-elasticloadbalancingv2": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-lambda": "^0.22.0", - "@aws-cdk/aws-logs": "^0.22.0", - "@aws-cdk/aws-route53": "^0.22.0", - "@aws-cdk/aws-sns": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0", - "@aws-cdk/cx-api": "^0.22.0" + "@aws-cdk/assets-docker": "^0.23.0", + "@aws-cdk/aws-applicationautoscaling": "^0.23.0", + "@aws-cdk/aws-autoscaling": "^0.23.0", + "@aws-cdk/aws-certificatemanager": "^0.23.0", + "@aws-cdk/aws-cloudformation": "^0.23.0", + "@aws-cdk/aws-cloudwatch": "^0.23.0", + "@aws-cdk/aws-ec2": "^0.23.0", + "@aws-cdk/aws-ecr": "^0.23.0", + "@aws-cdk/aws-elasticloadbalancing": "^0.23.0", + "@aws-cdk/aws-elasticloadbalancingv2": "^0.23.0", + "@aws-cdk/aws-events": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-lambda": "^0.23.0", + "@aws-cdk/aws-logs": "^0.23.0", + "@aws-cdk/aws-route53": "^0.23.0", + "@aws-cdk/aws-sns": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0", + "@aws-cdk/cx-api": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/assets-docker": "^0.22.0", - "@aws-cdk/aws-applicationautoscaling": "^0.22.0", - "@aws-cdk/aws-autoscaling": "^0.22.0", - "@aws-cdk/aws-certificatemanager": "^0.22.0", - "@aws-cdk/aws-cloudwatch": "^0.22.0", - "@aws-cdk/aws-ec2": "^0.22.0", - "@aws-cdk/aws-ecr": "^0.22.0", - "@aws-cdk/aws-events": "^0.22.0", - "@aws-cdk/aws-elasticloadbalancing": "^0.22.0", - "@aws-cdk/aws-elasticloadbalancingv2": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-logs": "^0.22.0", - "@aws-cdk/aws-route53": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/assets-docker": "^0.23.0", + "@aws-cdk/aws-applicationautoscaling": "^0.23.0", + "@aws-cdk/aws-autoscaling": "^0.23.0", + "@aws-cdk/aws-certificatemanager": "^0.23.0", + "@aws-cdk/aws-cloudwatch": "^0.23.0", + "@aws-cdk/aws-ec2": "^0.23.0", + "@aws-cdk/aws-ecr": "^0.23.0", + "@aws-cdk/aws-elasticloadbalancing": "^0.23.0", + "@aws-cdk/aws-elasticloadbalancingv2": "^0.23.0", + "@aws-cdk/aws-events": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-logs": "^0.23.0", + "@aws-cdk/aws-route53": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-efs/package.json b/packages/@aws-cdk/aws-efs/package.json index d1d4c917bafee..67159853a5978 100644 --- a/packages/@aws-cdk/aws-efs/package.json +++ b/packages/@aws-cdk/aws-efs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-efs", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::EFS", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-eks/package.json b/packages/@aws-cdk/aws-eks/package.json index 2528687649fb2..f3e7a00be87bf 100644 --- a/packages/@aws-cdk/aws-eks/package.json +++ b/packages/@aws-cdk/aws-eks/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-eks", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::EKS", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-elasticache/package.json b/packages/@aws-cdk/aws-elasticache/package.json index f4eae49899bc6..83455c2294305 100644 --- a/packages/@aws-cdk/aws-elasticache/package.json +++ b/packages/@aws-cdk/aws-elasticache/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-elasticache", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::ElastiCache", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-elasticbeanstalk/package.json b/packages/@aws-cdk/aws-elasticbeanstalk/package.json index 843af0d10e0e1..15b75f5afcfa1 100644 --- a/packages/@aws-cdk/aws-elasticbeanstalk/package.json +++ b/packages/@aws-cdk/aws-elasticbeanstalk/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-elasticbeanstalk", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::ElasticBeanstalk", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-elasticloadbalancing/package.json b/packages/@aws-cdk/aws-elasticloadbalancing/package.json index f41d0e68b5cd7..e5deda8696b8e 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancing/package.json +++ b/packages/@aws-cdk/aws-elasticloadbalancing/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-elasticloadbalancing", - "version": "0.22.0", + "version": "0.23.0", "description": "CDK Constructs for AWS ElasticLoadBalancing", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,22 +54,22 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-codedeploy-api": "^0.22.0", - "@aws-cdk/aws-ec2": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-codedeploy-api": "^0.23.0", + "@aws-cdk/aws-ec2": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-codedeploy-api": "^0.22.0", - "@aws-cdk/aws-ec2": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-codedeploy-api": "^0.23.0", + "@aws-cdk/aws-ec2": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json b/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json index 180bcdca5e112..5ea9c0f3d1cac 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-elasticloadbalancingv2", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::ElasticLoadBalancingV2", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,29 +54,29 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-cloudwatch": "^0.22.0", - "@aws-cdk/aws-codedeploy-api": "^0.22.0", - "@aws-cdk/aws-ec2": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-route53": "^0.22.0", - "@aws-cdk/aws-s3": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-cloudwatch": "^0.23.0", + "@aws-cdk/aws-codedeploy-api": "^0.23.0", + "@aws-cdk/aws-ec2": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-route53": "^0.23.0", + "@aws-cdk/aws-s3": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-cloudwatch": "^0.22.0", - "@aws-cdk/aws-codedeploy-api": "^0.22.0", - "@aws-cdk/aws-ec2": "^0.22.0", - "@aws-cdk/aws-route53": "^0.22.0", - "@aws-cdk/aws-s3": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-cloudwatch": "^0.23.0", + "@aws-cdk/aws-codedeploy-api": "^0.23.0", + "@aws-cdk/aws-ec2": "^0.23.0", + "@aws-cdk/aws-route53": "^0.23.0", + "@aws-cdk/aws-s3": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-elasticsearch/package.json b/packages/@aws-cdk/aws-elasticsearch/package.json index 9f85132ace727..b1303611df8d3 100644 --- a/packages/@aws-cdk/aws-elasticsearch/package.json +++ b/packages/@aws-cdk/aws-elasticsearch/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-elasticsearch", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::Elasticsearch", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-emr/package.json b/packages/@aws-cdk/aws-emr/package.json index 3edff603a78b9..17df407787802 100644 --- a/packages/@aws-cdk/aws-emr/package.json +++ b/packages/@aws-cdk/aws-emr/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-emr", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::EMR", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-events/package.json b/packages/@aws-cdk/aws-events/package.json index 57b68709bbf07..e4419f4a0a5e2 100644 --- a/packages/@aws-cdk/aws-events/package.json +++ b/packages/@aws-cdk/aws-events/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-events", - "version": "0.22.0", + "version": "0.23.0", "description": "AWS CloudWatch Events Construct Library", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -55,18 +55,18 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-gamelift/package.json b/packages/@aws-cdk/aws-gamelift/package.json index ca58d813de56e..569183313f949 100644 --- a/packages/@aws-cdk/aws-gamelift/package.json +++ b/packages/@aws-cdk/aws-gamelift/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-gamelift", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::GameLift", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-glue/package.json b/packages/@aws-cdk/aws-glue/package.json index 5385391b02fdf..5008b3dc9ab6d 100644 --- a/packages/@aws-cdk/aws-glue/package.json +++ b/packages/@aws-cdk/aws-glue/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-glue", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::Glue", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-guardduty/package.json b/packages/@aws-cdk/aws-guardduty/package.json index d268b6ead172c..17108917b9d23 100644 --- a/packages/@aws-cdk/aws-guardduty/package.json +++ b/packages/@aws-cdk/aws-guardduty/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-guardduty", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::GuardDuty", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-iam/package.json b/packages/@aws-cdk/aws-iam/package.json index 6a72aeba73b75..b4ca0505eee5d 100644 --- a/packages/@aws-cdk/aws-iam/package.json +++ b/packages/@aws-cdk/aws-iam/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-iam", - "version": "0.22.0", + "version": "0.23.0", "description": "CDK routines for easily assigning correct and minimal IAM permissions", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -56,18 +56,18 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-inspector/package.json b/packages/@aws-cdk/aws-inspector/package.json index ad3fea43ef757..cb083c67ceef8 100644 --- a/packages/@aws-cdk/aws-inspector/package.json +++ b/packages/@aws-cdk/aws-inspector/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-inspector", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::Inspector", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-iot/package.json b/packages/@aws-cdk/aws-iot/package.json index 60d089fd859a7..0afafda91f478 100644 --- a/packages/@aws-cdk/aws-iot/package.json +++ b/packages/@aws-cdk/aws-iot/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-iot", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::IoT", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-iot1click/package.json b/packages/@aws-cdk/aws-iot1click/package.json index 686a044e63f09..f8ea7ec2b3e3c 100644 --- a/packages/@aws-cdk/aws-iot1click/package.json +++ b/packages/@aws-cdk/aws-iot1click/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-iot1click", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::IoT1Click", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -55,16 +55,16 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-iotanalytics/package.json b/packages/@aws-cdk/aws-iotanalytics/package.json index 5316f012c5862..e408d9df81a9f 100644 --- a/packages/@aws-cdk/aws-iotanalytics/package.json +++ b/packages/@aws-cdk/aws-iotanalytics/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-iotanalytics", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::IoTAnalytics", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -56,16 +56,16 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-kinesis/package.json b/packages/@aws-cdk/aws-kinesis/package.json index afffb056f8b7b..1c75ea25bf432 100644 --- a/packages/@aws-cdk/aws-kinesis/package.json +++ b/packages/@aws-cdk/aws-kinesis/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-kinesis", - "version": "0.22.0", + "version": "0.23.0", "description": "CDK Constructs for AWS Kinesis", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -53,23 +53,23 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-kms": "^0.22.0", - "@aws-cdk/aws-logs": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-kms": "^0.23.0", + "@aws-cdk/aws-logs": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-kms": "^0.22.0", - "@aws-cdk/aws-logs": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-kms": "^0.23.0", + "@aws-cdk/aws-logs": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-kinesisanalytics/package.json b/packages/@aws-cdk/aws-kinesisanalytics/package.json index 08f6e759bed2a..3acea96dbab13 100644 --- a/packages/@aws-cdk/aws-kinesisanalytics/package.json +++ b/packages/@aws-cdk/aws-kinesisanalytics/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-kinesisanalytics", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::KinesisAnalytics", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-kinesisfirehose/package.json b/packages/@aws-cdk/aws-kinesisfirehose/package.json index 1431c853cf04b..dc04f006c0384 100644 --- a/packages/@aws-cdk/aws-kinesisfirehose/package.json +++ b/packages/@aws-cdk/aws-kinesisfirehose/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-kinesisfirehose", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::KinesisFirehose", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-kms/package.json b/packages/@aws-cdk/aws-kms/package.json index 51e5aa7c6296b..3619ca05a89cf 100644 --- a/packages/@aws-cdk/aws-kms/package.json +++ b/packages/@aws-cdk/aws-kms/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-kms", - "version": "0.22.0", + "version": "0.23.0", "description": "CDK Constructs for AWS KMS", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,20 +54,20 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-lambda-event-sources/package.json b/packages/@aws-cdk/aws-lambda-event-sources/package.json index 6e54db1d37db3..2783c07056a4a 100644 --- a/packages/@aws-cdk/aws-lambda-event-sources/package.json +++ b/packages/@aws-cdk/aws-lambda-event-sources/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-lambda-event-sources", - "version": "0.22.0", + "version": "0.23.0", "description": "Event sources for AWS Lambda", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -50,32 +50,32 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-dynamodb": "^0.22.0", - "@aws-cdk/aws-events": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-kinesis": "^0.22.0", - "@aws-cdk/aws-lambda": "^0.22.0", - "@aws-cdk/aws-s3": "^0.22.0", - "@aws-cdk/aws-sns": "^0.22.0", - "@aws-cdk/aws-sqs": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-dynamodb": "^0.23.0", + "@aws-cdk/aws-events": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-kinesis": "^0.23.0", + "@aws-cdk/aws-lambda": "^0.23.0", + "@aws-cdk/aws-s3": "^0.23.0", + "@aws-cdk/aws-sns": "^0.23.0", + "@aws-cdk/aws-sqs": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-dynamodb": "^0.22.0", - "@aws-cdk/aws-kinesis": "^0.22.0", - "@aws-cdk/aws-lambda": "^0.22.0", - "@aws-cdk/aws-s3": "^0.22.0", - "@aws-cdk/aws-sns": "^0.22.0", - "@aws-cdk/aws-sqs": "^0.22.0" + "@aws-cdk/aws-dynamodb": "^0.23.0", + "@aws-cdk/aws-kinesis": "^0.23.0", + "@aws-cdk/aws-lambda": "^0.23.0", + "@aws-cdk/aws-s3": "^0.23.0", + "@aws-cdk/aws-sns": "^0.23.0", + "@aws-cdk/aws-sqs": "^0.23.0" }, "engines": { "node": ">= 8.10.0" } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json index 4057ad8126434..7f8519f8203e0 100644 --- a/packages/@aws-cdk/aws-lambda/package.json +++ b/packages/@aws-cdk/aws-lambda/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-lambda", - "version": "0.22.0", + "version": "0.23.0", "description": "CDK Constructs for AWS Lambda", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -57,41 +57,41 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/assets": "^0.22.0", - "@aws-cdk/aws-cloudwatch": "^0.22.0", - "@aws-cdk/aws-codepipeline-api": "^0.22.0", - "@aws-cdk/aws-ec2": "^0.22.0", - "@aws-cdk/aws-events": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-logs": "^0.22.0", - "@aws-cdk/aws-s3": "^0.22.0", - "@aws-cdk/aws-s3-notifications": "^0.22.0", - "@aws-cdk/aws-sqs": "^0.22.0", - "@aws-cdk/aws-stepfunctions": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0", - "@aws-cdk/cx-api": "^0.22.0" + "@aws-cdk/assets": "^0.23.0", + "@aws-cdk/aws-cloudwatch": "^0.23.0", + "@aws-cdk/aws-codepipeline-api": "^0.23.0", + "@aws-cdk/aws-ec2": "^0.23.0", + "@aws-cdk/aws-events": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-logs": "^0.23.0", + "@aws-cdk/aws-s3": "^0.23.0", + "@aws-cdk/aws-s3-notifications": "^0.23.0", + "@aws-cdk/aws-sqs": "^0.23.0", + "@aws-cdk/aws-stepfunctions": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0", + "@aws-cdk/cx-api": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/assets": "^0.22.0", - "@aws-cdk/aws-cloudwatch": "^0.22.0", - "@aws-cdk/aws-codepipeline-api": "^0.22.0", - "@aws-cdk/aws-ec2": "^0.22.0", - "@aws-cdk/aws-events": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-logs": "^0.22.0", - "@aws-cdk/aws-s3": "^0.22.0", - "@aws-cdk/aws-s3-notifications": "^0.22.0", - "@aws-cdk/aws-sqs": "^0.22.0", - "@aws-cdk/aws-stepfunctions": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/assets": "^0.23.0", + "@aws-cdk/aws-cloudwatch": "^0.23.0", + "@aws-cdk/aws-codepipeline-api": "^0.23.0", + "@aws-cdk/aws-ec2": "^0.23.0", + "@aws-cdk/aws-events": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-logs": "^0.23.0", + "@aws-cdk/aws-s3": "^0.23.0", + "@aws-cdk/aws-s3-notifications": "^0.23.0", + "@aws-cdk/aws-sqs": "^0.23.0", + "@aws-cdk/aws-stepfunctions": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-logs/package.json b/packages/@aws-cdk/aws-logs/package.json index a89af79aad145..12c8de6e9aeca 100644 --- a/packages/@aws-cdk/aws-logs/package.json +++ b/packages/@aws-cdk/aws-logs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-logs", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::Logs", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,22 +54,22 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-cloudwatch": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-cloudwatch": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-cloudwatch": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-cloudwatch": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-neptune/package.json b/packages/@aws-cdk/aws-neptune/package.json index 44b5f245f09a2..cdc527bf2106d 100644 --- a/packages/@aws-cdk/aws-neptune/package.json +++ b/packages/@aws-cdk/aws-neptune/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-neptune", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::Neptune", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -55,16 +55,16 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-opsworks/package.json b/packages/@aws-cdk/aws-opsworks/package.json index affaf221a2088..c382417aba307 100644 --- a/packages/@aws-cdk/aws-opsworks/package.json +++ b/packages/@aws-cdk/aws-opsworks/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-opsworks", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::OpsWorks", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-opsworkscm/package.json b/packages/@aws-cdk/aws-opsworkscm/package.json index 6cd7813cbf58c..0eb3c43b2ff97 100644 --- a/packages/@aws-cdk/aws-opsworkscm/package.json +++ b/packages/@aws-cdk/aws-opsworkscm/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-opsworkscm", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::OpsWorksCM", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -56,18 +56,18 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-quickstarts/package.json b/packages/@aws-cdk/aws-quickstarts/package.json index 30aeee13a2db4..b14deea862b16 100644 --- a/packages/@aws-cdk/aws-quickstarts/package.json +++ b/packages/@aws-cdk/aws-quickstarts/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-quickstarts", - "version": "0.22.0", + "version": "0.23.0", "description": "AWS Quickstarts for the CDK", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -49,20 +49,20 @@ }, "license": "Apache-2.0", "devDependencies": { - "cdk-build-tools": "^0.22.0", - "pkglint": "^0.22.0" + "cdk-build-tools": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-cloudformation": "^0.22.0", - "@aws-cdk/aws-ec2": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-rds": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-cloudformation": "^0.23.0", + "@aws-cdk/aws-ec2": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-rds": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-ec2": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-ec2": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-rds/package.json b/packages/@aws-cdk/aws-rds/package.json index 936f35715d7e5..dde108d110e8d 100644 --- a/packages/@aws-cdk/aws-rds/package.json +++ b/packages/@aws-cdk/aws-rds/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-rds", - "version": "0.22.0", + "version": "0.23.0", "description": "CDK Constructs for AWS RDS", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,22 +54,22 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-ec2": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-kms": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-ec2": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-kms": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-ec2": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-ec2": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-redshift/package.json b/packages/@aws-cdk/aws-redshift/package.json index cdbe8a3a60b6e..18c54c52e1311 100644 --- a/packages/@aws-cdk/aws-redshift/package.json +++ b/packages/@aws-cdk/aws-redshift/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-redshift", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::Redshift", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-route53/package.json b/packages/@aws-cdk/aws-route53/package.json index 939a34ce98dba..b551136569c01 100644 --- a/packages/@aws-cdk/aws-route53/package.json +++ b/packages/@aws-cdk/aws-route53/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-route53", - "version": "0.22.0", + "version": "0.23.0", "description": "CDK Constructs for AWS Route53", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,23 +54,23 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", + "@aws-cdk/assert": "^0.23.0", "aws-sdk": "^2.259.1", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-ec2": "^0.22.0", - "@aws-cdk/aws-logs": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0", - "@aws-cdk/cx-api": "^0.22.0" + "@aws-cdk/aws-ec2": "^0.23.0", + "@aws-cdk/aws-logs": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0", + "@aws-cdk/cx-api": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-ec2": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-ec2": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-route53resolver/package.json b/packages/@aws-cdk/aws-route53resolver/package.json index a4c4993f30874..233aedf5eb33d 100644 --- a/packages/@aws-cdk/aws-route53resolver/package.json +++ b/packages/@aws-cdk/aws-route53resolver/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-route53resolver", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::Route53Resolver", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -55,16 +55,16 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-s3-deployment/package.json b/packages/@aws-cdk/aws-s3-deployment/package.json index da52a49ce0da4..d79fc4119db0c 100644 --- a/packages/@aws-cdk/aws-s3-deployment/package.json +++ b/packages/@aws-cdk/aws-s3-deployment/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-s3-deployment", - "version": "0.22.0", + "version": "0.23.0", "description": "Constructs for deploying contents to S3 buckets", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -68,23 +68,23 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/assets": "^0.22.0", - "@aws-cdk/aws-cloudformation": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-lambda": "^0.22.0", - "@aws-cdk/aws-s3": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/assets": "^0.23.0", + "@aws-cdk/aws-cloudformation": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-lambda": "^0.23.0", + "@aws-cdk/aws-s3": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-s3": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-s3": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-s3-notifications/package.json b/packages/@aws-cdk/aws-s3-notifications/package.json index a7490d636eb65..5a2d703d0ce23 100644 --- a/packages/@aws-cdk/aws-s3-notifications/package.json +++ b/packages/@aws-cdk/aws-s3-notifications/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-s3-notifications", - "version": "0.22.0", + "version": "0.23.0", "description": "Bucket Notifications API for AWS S3", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -50,15 +50,15 @@ }, "license": "Apache-2.0", "devDependencies": { - "cdk-build-tools": "^0.22.0", - "pkglint": "^0.22.0" + "cdk-build-tools": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-s3/package.json b/packages/@aws-cdk/aws-s3/package.json index d5dade1103486..94876e54ae7ca 100644 --- a/packages/@aws-cdk/aws-s3/package.json +++ b/packages/@aws-cdk/aws-s3/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-s3", - "version": "0.22.0", + "version": "0.23.0", "description": "CDK Constructs for AWS S3", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,28 +54,28 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-codepipeline-api": "^0.22.0", - "@aws-cdk/aws-events": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-kms": "^0.22.0", - "@aws-cdk/aws-s3-notifications": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-codepipeline-api": "^0.23.0", + "@aws-cdk/aws-events": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-kms": "^0.23.0", + "@aws-cdk/aws-s3-notifications": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-codepipeline-api": "^0.22.0", - "@aws-cdk/aws-events": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-kms": "^0.22.0", - "@aws-cdk/aws-s3-notifications": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-codepipeline-api": "^0.23.0", + "@aws-cdk/aws-events": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-kms": "^0.23.0", + "@aws-cdk/aws-s3-notifications": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" @@ -89,4 +89,4 @@ "resource-interface:@aws-cdk/aws-s3.IBucketPolicy" ] } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-sagemaker/package.json b/packages/@aws-cdk/aws-sagemaker/package.json index 4ecfc6d0b4b56..a9610a0846d8f 100644 --- a/packages/@aws-cdk/aws-sagemaker/package.json +++ b/packages/@aws-cdk/aws-sagemaker/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-sagemaker", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::SageMaker", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -55,16 +55,16 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-sdb/package.json b/packages/@aws-cdk/aws-sdb/package.json index 197c5c93edaf4..fa8291744acfa 100644 --- a/packages/@aws-cdk/aws-sdb/package.json +++ b/packages/@aws-cdk/aws-sdb/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-sdb", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::SDB", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-secretsmanager/package.json b/packages/@aws-cdk/aws-secretsmanager/package.json index a00358bd38357..6119309f10f85 100644 --- a/packages/@aws-cdk/aws-secretsmanager/package.json +++ b/packages/@aws-cdk/aws-secretsmanager/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-secretsmanager", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::SecretsManager", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -55,16 +55,16 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-serverless/package.json b/packages/@aws-cdk/aws-serverless/package.json index 44d158034b23d..7f8d0f2742b6d 100644 --- a/packages/@aws-cdk/aws-serverless/package.json +++ b/packages/@aws-cdk/aws-serverless/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-serverless", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::Serverless", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -55,16 +55,16 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-servicecatalog/package.json b/packages/@aws-cdk/aws-servicecatalog/package.json index 0fe0f52022b63..472e80eafc537 100644 --- a/packages/@aws-cdk/aws-servicecatalog/package.json +++ b/packages/@aws-cdk/aws-servicecatalog/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-servicecatalog", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::ServiceCatalog", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-servicediscovery/package.json b/packages/@aws-cdk/aws-servicediscovery/package.json index e933931828a54..48dc72bd6070b 100644 --- a/packages/@aws-cdk/aws-servicediscovery/package.json +++ b/packages/@aws-cdk/aws-servicediscovery/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-servicediscovery", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::ServiceDiscovery", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-ses/package.json b/packages/@aws-cdk/aws-ses/package.json index c7922887770fd..8cd6c81de6f05 100644 --- a/packages/@aws-cdk/aws-ses/package.json +++ b/packages/@aws-cdk/aws-ses/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-ses", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::SES", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-sns/package.json b/packages/@aws-cdk/aws-sns/package.json index 08db238b2e80c..8b84b724e310f 100644 --- a/packages/@aws-cdk/aws-sns/package.json +++ b/packages/@aws-cdk/aws-sns/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-sns", - "version": "0.22.0", + "version": "0.23.0", "description": "CDK Constructs for AWS SNS", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -57,33 +57,33 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "@aws-cdk/aws-s3": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "@aws-cdk/aws-s3": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-autoscaling-api": "^0.22.0", - "@aws-cdk/aws-cloudwatch": "^0.22.0", - "@aws-cdk/aws-events": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-lambda": "^0.22.0", - "@aws-cdk/aws-s3-notifications": "^0.22.0", - "@aws-cdk/aws-sqs": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-autoscaling-api": "^0.23.0", + "@aws-cdk/aws-cloudwatch": "^0.23.0", + "@aws-cdk/aws-events": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-lambda": "^0.23.0", + "@aws-cdk/aws-s3-notifications": "^0.23.0", + "@aws-cdk/aws-sqs": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-autoscaling-api": "^0.22.0", - "@aws-cdk/aws-cloudwatch": "^0.22.0", - "@aws-cdk/aws-events": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-lambda": "^0.22.0", - "@aws-cdk/aws-s3-notifications": "^0.22.0", - "@aws-cdk/aws-sqs": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-autoscaling-api": "^0.23.0", + "@aws-cdk/aws-cloudwatch": "^0.23.0", + "@aws-cdk/aws-events": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-lambda": "^0.23.0", + "@aws-cdk/aws-s3-notifications": "^0.23.0", + "@aws-cdk/aws-sqs": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-sqs/package.json b/packages/@aws-cdk/aws-sqs/package.json index 811e66a343287..548fe7fc7c44b 100644 --- a/packages/@aws-cdk/aws-sqs/package.json +++ b/packages/@aws-cdk/aws-sqs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-sqs", - "version": "0.22.0", + "version": "0.23.0", "description": "CDK Constructs for AWS SQS", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,28 +54,28 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "@aws-cdk/aws-s3": "^0.22.0", + "@aws-cdk/assert": "^0.23.0", + "@aws-cdk/aws-s3": "^0.23.0", "aws-sdk": "^2.259.1", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-autoscaling-api": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-kms": "^0.22.0", - "@aws-cdk/aws-s3-notifications": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-autoscaling-api": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-kms": "^0.23.0", + "@aws-cdk/aws-s3-notifications": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-autoscaling-api": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-kms": "^0.22.0", - "@aws-cdk/aws-s3-notifications": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-autoscaling-api": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-kms": "^0.23.0", + "@aws-cdk/aws-s3-notifications": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-ssm/package.json b/packages/@aws-cdk/aws-ssm/package.json index bba3ee4940a2c..56b6feb976028 100644 --- a/packages/@aws-cdk/aws-ssm/package.json +++ b/packages/@aws-cdk/aws-ssm/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-ssm", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::SSM", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-stepfunctions/package.json b/packages/@aws-cdk/aws-stepfunctions/package.json index ada893e0378e4..e70c8f50c98bc 100644 --- a/packages/@aws-cdk/aws-stepfunctions/package.json +++ b/packages/@aws-cdk/aws-stepfunctions/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-stepfunctions", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::StepFunctions", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,24 +54,24 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-cloudwatch": "^0.22.0", - "@aws-cdk/aws-events": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-cloudwatch": "^0.23.0", + "@aws-cdk/aws-events": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-cloudwatch": "^0.22.0", - "@aws-cdk/aws-events": "^0.22.0", - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-cloudwatch": "^0.23.0", + "@aws-cdk/aws-events": "^0.23.0", + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-waf/package.json b/packages/@aws-cdk/aws-waf/package.json index a074a50bd8f7d..7b34d0c90636b 100644 --- a/packages/@aws-cdk/aws-waf/package.json +++ b/packages/@aws-cdk/aws-waf/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-waf", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::WAF", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-wafregional/package.json b/packages/@aws-cdk/aws-wafregional/package.json index 9fa6618610b53..335a0474f10f6 100644 --- a/packages/@aws-cdk/aws-wafregional/package.json +++ b/packages/@aws-cdk/aws-wafregional/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-wafregional", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::WAFRegional", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-workspaces/package.json b/packages/@aws-cdk/aws-workspaces/package.json index 5084880b39674..63833af32795a 100644 --- a/packages/@aws-cdk/aws-workspaces/package.json +++ b/packages/@aws-cdk/aws-workspaces/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-workspaces", - "version": "0.22.0", + "version": "0.23.0", "description": "The CDK Construct Library for AWS::WorkSpaces", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -54,17 +54,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/cdk/package.json b/packages/@aws-cdk/cdk/package.json index 6376c03863e3e..9173e5572709b 100644 --- a/packages/@aws-cdk/cdk/package.json +++ b/packages/@aws-cdk/cdk/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/cdk", - "version": "0.22.0", + "version": "0.23.0", "description": "AWS Cloud Development Kit Core Library", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -62,14 +62,14 @@ "devDependencies": { "@types/js-base64": "^2.3.1", "@types/lodash": "^4.14.118", - "cdk-build-tools": "^0.22.0", - "cfn2ts": "^0.22.0", + "cdk-build-tools": "^0.23.0", + "cfn2ts": "^0.23.0", "fast-check": "^1.7.0", "lodash": "^4.17.11", - "pkglint": "^0.22.0" + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cx-api": "^0.22.0", + "@aws-cdk/cx-api": "^0.23.0", "js-base64": "^2.4.5", "json-diff": "^0.3.1" }, @@ -79,7 +79,7 @@ ], "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/cx-api": "^0.22.0" + "@aws-cdk/cx-api": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/cfnspec/package.json b/packages/@aws-cdk/cfnspec/package.json index 4dcd7e0432204..09d924f7f2ede 100644 --- a/packages/@aws-cdk/cfnspec/package.json +++ b/packages/@aws-cdk/cfnspec/package.json @@ -1,7 +1,7 @@ { "name": "@aws-cdk/cfnspec", "description": "The CloudFormation resource specification used by @aws-cdk packages", - "version": "0.22.0", + "version": "0.23.0", "scripts": { "update": "cdk-build && /bin/bash build-tools/update.sh", "build": "cdk-build && node build-tools/build", @@ -21,11 +21,11 @@ "devDependencies": { "@types/fs-extra": "^5.0.4", "@types/md5": "^2.1.32", - "cdk-build-tools": "^0.22.0", + "cdk-build-tools": "^0.23.0", "fast-json-patch": "^2.0.6", "fs-extra": "^7.0.0", "json-diff": "^0.3.1", - "pkglint": "^0.22.0", + "pkglint": "^0.23.0", "sort-json": "^2.0.0" }, "dependencies": { diff --git a/packages/@aws-cdk/cloudformation-diff/package.json b/packages/@aws-cdk/cloudformation-diff/package.json index c3789f258552d..10d35709f03ac 100644 --- a/packages/@aws-cdk/cloudformation-diff/package.json +++ b/packages/@aws-cdk/cloudformation-diff/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/cloudformation-diff", - "version": "0.22.0", + "version": "0.23.0", "description": "Utilities to diff CDK stacks against CloudFormation templates", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -23,21 +23,21 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-cdk/cfnspec": "^0.22.0", - "@aws-cdk/cx-api": "^0.22.0", - "string-width": "^2.1.1", - "table": "^5.2.1", + "@aws-cdk/cfnspec": "^0.23.0", + "@aws-cdk/cx-api": "^0.23.0", "colors": "^1.2.1", "diff": "^4.0.1", "fast-deep-equal": "^2.0.1", - "source-map-support": "^0.5.6" + "source-map-support": "^0.5.6", + "string-width": "^2.1.1", + "table": "^5.2.1" }, "devDependencies": { - "@types/table": "^4.0.5", "@types/string-width": "^2.0.0", - "cdk-build-tools": "^0.22.0", + "@types/table": "^4.0.5", + "cdk-build-tools": "^0.23.0", "fast-check": "^1.8.0", - "pkglint": "^0.22.0" + "pkglint": "^0.23.0" }, "repository": { "url": "https://github.com/awslabs/aws-cdk.git", diff --git a/packages/@aws-cdk/cx-api/package.json b/packages/@aws-cdk/cx-api/package.json index b2df53d303564..57f0f8df792d3 100644 --- a/packages/@aws-cdk/cx-api/package.json +++ b/packages/@aws-cdk/cx-api/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/cx-api", - "version": "0.22.0", + "version": "0.23.0", "description": "Cloud executable protocol", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -39,8 +39,8 @@ }, "license": "Apache-2.0", "devDependencies": { - "cdk-build-tools": "^0.22.0", - "pkglint": "^0.22.0" + "cdk-build-tools": "^0.23.0", + "pkglint": "^0.23.0" }, "repository": { "url": "https://github.com/awslabs/aws-cdk.git", diff --git a/packages/@aws-cdk/runtime-values/package.json b/packages/@aws-cdk/runtime-values/package.json index e50ad6a7a87a4..c7b3aaf4e7c2d 100644 --- a/packages/@aws-cdk/runtime-values/package.json +++ b/packages/@aws-cdk/runtime-values/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/runtime-values", - "version": "0.22.0", + "version": "0.23.0", "description": "Runtime values support for the AWS CDK", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -49,23 +49,23 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.22.0", - "@aws-cdk/aws-ec2": "^0.22.0", - "@aws-cdk/aws-lambda": "^0.22.0", - "@aws-cdk/aws-sqs": "^0.22.0", - "cdk-build-tools": "^0.22.0", - "cdk-integ-tools": "^0.22.0", - "pkglint": "^0.22.0" + "@aws-cdk/assert": "^0.23.0", + "@aws-cdk/aws-ec2": "^0.23.0", + "@aws-cdk/aws-lambda": "^0.23.0", + "@aws-cdk/aws-sqs": "^0.23.0", + "cdk-build-tools": "^0.23.0", + "cdk-integ-tools": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/aws-ssm": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-ssm": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { - "@aws-cdk/aws-iam": "^0.22.0", - "@aws-cdk/cdk": "^0.22.0" + "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/cdk": "^0.23.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index f67843cc6c278..61fa7da684adb 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -1,7 +1,7 @@ { "name": "aws-cdk", "description": "CDK Toolkit, the command line tool for CDK apps", - "version": "0.22.0", + "version": "0.23.0", "main": "lib/index.js", "types": "lib/index.d.ts", "bin": { @@ -36,19 +36,19 @@ "@types/minimatch": "^3.0.3", "@types/mockery": "^1.4.29", "@types/request": "^2.47.1", - "@types/table": "^4.0.5", "@types/semver": "^5.5.0", + "@types/table": "^4.0.5", "@types/uuid": "^3.4.3", "@types/yaml": "^1.0.0", "@types/yargs": "^8.0.3", - "cdk-build-tools": "^0.22.0", + "cdk-build-tools": "^0.23.0", "mockery": "^2.1.0", - "pkglint": "^0.22.0" + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/applet-js": "^0.22.0", - "@aws-cdk/cloudformation-diff": "^0.22.0", - "@aws-cdk/cx-api": "^0.22.0", + "@aws-cdk/applet-js": "^0.23.0", + "@aws-cdk/cloudformation-diff": "^0.23.0", + "@aws-cdk/cx-api": "^0.23.0", "archiver": "^2.1.1", "aws-sdk": "^2.259.1", "camelcase": "^5.0.0", diff --git a/packages/simple-resource-bundler/package.json b/packages/simple-resource-bundler/package.json index bfed9fcde7865..d70928da02db0 100644 --- a/packages/simple-resource-bundler/package.json +++ b/packages/simple-resource-bundler/package.json @@ -1,6 +1,6 @@ { "name": "simple-resource-bundler", - "version": "0.22.0", + "version": "0.23.0", "description": "Command-line tool to embed resources into JS libraries", "main": "bundler.js", "types": "bundler.d.ts", @@ -24,8 +24,8 @@ "devDependencies": { "@types/fs-extra": "^5.0.4", "@types/yargs": "^8.0.3", - "cdk-build-tools": "^0.22.0", - "pkglint": "^0.22.0" + "cdk-build-tools": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { "fs-extra": "^7.0.0", diff --git a/tools/awslint/package.json b/tools/awslint/package.json index 4a80eeda18775..9155308393328 100644 --- a/tools/awslint/package.json +++ b/tools/awslint/package.json @@ -1,7 +1,7 @@ { "name": "awslint", "private": true, - "version": "0.22.0", + "version": "0.23.0", "description": "Enforces the AWS Construct Library guidelines", "main": "index.js", "scripts": { diff --git a/tools/cdk-build-tools/package.json b/tools/cdk-build-tools/package.json index a9994143ce8e6..5ecaa5ac5e138 100644 --- a/tools/cdk-build-tools/package.json +++ b/tools/cdk-build-tools/package.json @@ -1,7 +1,7 @@ { "name": "cdk-build-tools", "private": true, - "version": "0.22.0", + "version": "0.23.0", "description": "Tools package with shared build scripts for CDK packages", "main": "lib/index.js", "repository": { @@ -29,10 +29,10 @@ "devDependencies": { "@types/fs-extra": "^5.0.4", "@types/yargs": "^8.0.3", - "pkglint": "^0.22.0" + "pkglint": "^0.23.0" }, "dependencies": { - "awslint": "^0.22.0", + "awslint": "^0.23.0", "fs-extra": "^7.0.0", "jsii": "^0.7.13", "jsii-pacmak": "^0.7.13", diff --git a/tools/cdk-integ-tools/package.json b/tools/cdk-integ-tools/package.json index 500fea706e0bd..a81f239055167 100644 --- a/tools/cdk-integ-tools/package.json +++ b/tools/cdk-integ-tools/package.json @@ -1,7 +1,7 @@ { "name": "cdk-integ-tools", "private": true, - "version": "0.22.0", + "version": "0.23.0", "description": "Package with integration test scripts for CDK packages", "main": "index.js", "repository": { @@ -27,13 +27,13 @@ "license": "Apache-2.0", "devDependencies": { "@types/yargs": "^8.0.3", - "cdk-build-tools": "^0.22.0", - "pkglint": "^0.22.0" + "cdk-build-tools": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { - "@aws-cdk/cloudformation-diff": "^0.22.0", - "@aws-cdk/cx-api": "^0.22.0", - "aws-cdk": "^0.22.0", + "@aws-cdk/cloudformation-diff": "^0.23.0", + "@aws-cdk/cx-api": "^0.23.0", + "aws-cdk": "^0.23.0", "yargs": "^9.0.1" }, "keywords": [ diff --git a/tools/cfn2ts/package.json b/tools/cfn2ts/package.json index 2ceb2437a2fe0..342c4d787b380 100644 --- a/tools/cfn2ts/package.json +++ b/tools/cfn2ts/package.json @@ -1,7 +1,7 @@ { "name": "cfn2ts", "private": true, - "version": "0.22.0", + "version": "0.23.0", "description": "Generates typescript types from CloudFormation spec, with support for enrichments", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -30,7 +30,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-cdk/cfnspec": "^0.22.0", + "@aws-cdk/cfnspec": "^0.23.0", "codemaker": "^0.6.4", "fast-json-patch": "^2.0.6", "fs-extra": "^7.0.0", @@ -39,8 +39,8 @@ "devDependencies": { "@types/fs-extra": "^5.0.4", "@types/yargs": "^8.0.3", - "cdk-build-tools": "^0.22.0", - "pkglint": "^0.22.0" + "cdk-build-tools": "^0.23.0", + "pkglint": "^0.23.0" }, "keywords": [ "aws", diff --git a/tools/pkglint/package.json b/tools/pkglint/package.json index e76b7e733235b..2c5c9f115db1f 100644 --- a/tools/pkglint/package.json +++ b/tools/pkglint/package.json @@ -1,6 +1,6 @@ { "name": "pkglint", - "version": "0.22.0", + "version": "0.23.0", "private": true, "description": "Validate and fix package.json files", "main": "lib/index.js", diff --git a/tools/pkgtools/package.json b/tools/pkgtools/package.json index cb58e9147593a..bdfcae723d01a 100644 --- a/tools/pkgtools/package.json +++ b/tools/pkgtools/package.json @@ -1,7 +1,7 @@ { "name": "pkgtools", "private": true, - "version": "0.22.0", + "version": "0.23.0", "description": "Tools for generating cross-package artifacts", "main": "index.js", "repository": { @@ -28,8 +28,8 @@ "devDependencies": { "@types/fs-extra": "^5.0.4", "@types/yargs": "^8.0.3", - "cdk-build-tools": "^0.22.0", - "pkglint": "^0.22.0" + "cdk-build-tools": "^0.23.0", + "pkglint": "^0.23.0" }, "dependencies": { "fs-extra": "^7.0.0", diff --git a/tools/y-npm/package.json b/tools/y-npm/package.json index 29d4e517ae5d7..6f88d83844139 100644 --- a/tools/y-npm/package.json +++ b/tools/y-npm/package.json @@ -1,6 +1,6 @@ { "name": "y-npm", - "version": "0.22.0", + "version": "0.23.0", "description": "Run npm commands using a local registry overlay", "private": true, "author": { @@ -35,8 +35,8 @@ "@types/colors": "^1.2.1", "@types/fs-extra": "^5.0.4", "@types/semver": "^5.5.0", - "cdk-build-tools": "^0.22.0", - "pkglint": "^0.22.0" + "cdk-build-tools": "^0.23.0", + "pkglint": "^0.23.0" }, "keywords": [ "aws", From 3bc66f2c1e7a3236faa77ecf0913923c70887dff Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Mon, 4 Feb 2019 13:05:04 -0800 Subject: [PATCH 30/38] removing tag aspect props and just using tag props --- .../@aws-cdk/cdk/lib/aspects/tag-aspect.ts | 27 +++---------------- 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts b/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts index f626f63ea56f8..34cd8899215f1 100644 --- a/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts +++ b/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts @@ -3,27 +3,6 @@ import { IConstruct } from '../core/construct'; import { TagProps } from '../core/tag-manager'; import { IAspect } from './aspect'; -export interface TagAspectProps extends TagProps { - /** - * An array of Resource Types that will receive this tag - * - * An empty array will match any Resource. A non-empty array will apply this - * tag only to Resource types that are included in this array. - * @default [] - */ - includeResourceTypes?: string[]; - - /** - * An array of Resource Types that will not receive this tag - * - * An empty array will allow this tag to be applied to all resources. A - * non-empty array will apply this tag only if the Resource type is not in - * this array. - * @default [] - */ - excludeResourceTypes?: string[]; -} - /** * The common functionality for Tag and Remove Tag Aspects */ @@ -37,7 +16,7 @@ export abstract class TagBase implements IAspect { private readonly includeResourceTypes: string[]; private readonly excludeResourceTypes: string[]; - constructor(key: string, props: TagAspectProps = {}) { + constructor(key: string, props: TagProps = {}) { this.key = key; this.includeResourceTypes = props.includeResourceTypes || []; this.excludeResourceTypes = props.excludeResourceTypes || []; @@ -77,7 +56,7 @@ export class Tag extends TagBase { private readonly applyToLaunchedInstances: boolean; private readonly priority: number; - constructor(key: string, value: string, props: TagAspectProps = {}) { + constructor(key: string, value: string, props: TagProps = {}) { super(key, {...props}); this.applyToLaunchedInstances = props.applyToLaunchedInstances !== false; this.priority = props.priority === undefined ? 0 : props.priority; @@ -102,7 +81,7 @@ export class RemoveTag extends TagBase { private readonly priority: number; - constructor(key: string, props: TagAspectProps = {}) { + constructor(key: string, props: TagProps = {}) { super(key, props); this.priority = props.priority === undefined ? 1 : props.priority; } From aeb24f10e9513fb62d2a9e6c89d3f5096a590084 Mon Sep 17 00:00:00 2001 From: Sam Goodwin Date: Mon, 4 Feb 2019 13:29:52 -0800 Subject: [PATCH 31/38] feat(codedeploy) lambda application and deployment groups (#1628) Adds `LambdaApplication` and `LambdaDeploymentGroup` such that an `Alias` to a Lambda `Function` can be deployed via CodeDeploy; supports pre/post hook functions, traffic-shifting and alarm monitoring and rollback. --- .../aws-apigateway/test/test.lambda-api.ts | 7 +- packages/@aws-cdk/aws-codedeploy/README.md | 120 +++- packages/@aws-cdk/aws-codedeploy/lib/index.ts | 6 +- .../aws-codedeploy/lib/lambda/application.ts | 101 ++++ .../lib/lambda/deployment-config.ts | 95 +++ .../lib/lambda/deployment-group.ts | 283 +++++++++ .../aws-codedeploy/lib/lambda/index.ts | 3 + .../aws-codedeploy/lib/pipeline-action.ts | 2 +- .../aws-codedeploy/lib/rollback-config.ts | 26 + .../lib/{ => server}/application.ts | 21 +- .../lib/{ => server}/deployment-config.ts | 14 +- .../lib/{ => server}/deployment-group.ts | 111 +--- .../aws-codedeploy/lib/server/index.ts | 3 + packages/@aws-cdk/aws-codedeploy/lib/utils.ts | 82 +++ packages/@aws-cdk/aws-codedeploy/package.json | 4 +- .../test/lambda/handler/index.js | 3 + .../integ.deployment-group.expected.json | 549 +++++++++++++++++ .../test/lambda/integ.deployment-group.ts | 48 ++ .../test/lambda/postHook/index.js | 24 + .../test/lambda/preHook/index.js | 25 + .../test/lambda/test.application.ts | 28 + .../test/lambda/test.deployment-group.ts | 569 ++++++++++++++++++ .../integ.deployment-group.expected.json | 0 .../{ => server}/integ.deployment-group.ts | 2 +- .../{ => server}/test.deployment-config.ts | 2 +- .../{ => server}/test.deployment-group.ts | 4 +- packages/@aws-cdk/aws-lambda/lib/alias.ts | 6 +- 27 files changed, 2001 insertions(+), 137 deletions(-) create mode 100644 packages/@aws-cdk/aws-codedeploy/lib/lambda/application.ts create mode 100644 packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-config.ts create mode 100644 packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-group.ts create mode 100644 packages/@aws-cdk/aws-codedeploy/lib/lambda/index.ts create mode 100644 packages/@aws-cdk/aws-codedeploy/lib/rollback-config.ts rename packages/@aws-cdk/aws-codedeploy/lib/{ => server}/application.ts (85%) rename packages/@aws-cdk/aws-codedeploy/lib/{ => server}/deployment-config.ts (94%) rename packages/@aws-cdk/aws-codedeploy/lib/{ => server}/deployment-group.ts (82%) create mode 100644 packages/@aws-cdk/aws-codedeploy/lib/server/index.ts create mode 100644 packages/@aws-cdk/aws-codedeploy/lib/utils.ts create mode 100644 packages/@aws-cdk/aws-codedeploy/test/lambda/handler/index.js create mode 100644 packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-group.expected.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-group.ts create mode 100644 packages/@aws-cdk/aws-codedeploy/test/lambda/postHook/index.js create mode 100644 packages/@aws-cdk/aws-codedeploy/test/lambda/preHook/index.js create mode 100644 packages/@aws-cdk/aws-codedeploy/test/lambda/test.application.ts create mode 100644 packages/@aws-cdk/aws-codedeploy/test/lambda/test.deployment-group.ts rename packages/@aws-cdk/aws-codedeploy/test/{ => server}/integ.deployment-group.expected.json (100%) rename packages/@aws-cdk/aws-codedeploy/test/{ => server}/integ.deployment-group.ts (96%) rename packages/@aws-cdk/aws-codedeploy/test/{ => server}/test.deployment-config.ts (98%) rename packages/@aws-cdk/aws-codedeploy/test/{ => server}/test.deployment-group.ts (99%) diff --git a/packages/@aws-cdk/aws-apigateway/test/test.lambda-api.ts b/packages/@aws-cdk/aws-apigateway/test/test.lambda-api.ts index 5a16869d06149..723718c4f8720 100644 --- a/packages/@aws-cdk/aws-apigateway/test/test.lambda-api.ts +++ b/packages/@aws-cdk/aws-apigateway/test/test.lambda-api.ts @@ -126,9 +126,12 @@ export = { }, ":lambda:path/2015-03-31/functions/", { - "Ref": "alias68BF17F5" + "Fn::GetAtt": [ + "handlerE1533BD5", + "Arn" + ] }, - "/invocations" + ":my-alias/invocations" ] ] } diff --git a/packages/@aws-cdk/aws-codedeploy/README.md b/packages/@aws-cdk/aws-codedeploy/README.md index 6bb1f64527847..8852de320fd31 100644 --- a/packages/@aws-cdk/aws-codedeploy/README.md +++ b/packages/@aws-cdk/aws-codedeploy/README.md @@ -1,6 +1,10 @@ ## The CDK Construct Library for AWS CodeDeploy -### Applications +AWS CodeDeploy is a deployment service that automates application deployments to Amazon EC2 instances, on-premises instances, serverless Lambda functions, or Amazon ECS services. + +The CDK currently supports Amazon EC2, on-premise and AWS Lambda applications. + +### EC2/on-premise Applications To create a new CodeDeploy Application that deploys to EC2/on-premise instances: @@ -20,7 +24,7 @@ const application = codedeploy.ServerApplication.import(this, 'ExistingCodeDeplo }); ``` -### Deployment Groups +### EC2/on-premise Deployment Groups To create a new CodeDeploy Deployment Group that deploys to EC2/on-premise instances: @@ -184,3 +188,115 @@ You can also add the Deployment Group to the Pipeline directly: // equivalent to the code above: deploymentGroup.addToPipeline(deployStage, 'CodeDeploy'); ``` + +### Lambda Applications + +To create a new CodeDeploy Application that deploys to a Lambda function: + +```ts +import codedeploy = require('@aws-cdk/aws-codedeploy'); + +const application = new codedeploy.LambdaApplication(this, 'CodeDeployApplication', { + applicationName: 'MyApplication', // optional property +}); +``` + +To import an already existing Application: + +```ts +const application = codedeploy.LambdaApplication.import(this, 'ExistingCodeDeployApplication', { + applicationName: 'MyExistingApplication', +}); +``` + +### Lambda Deployment Groups + +To enable traffic shifting deployments for Lambda functions, CodeDeploy uses Lambda Aliases, which can balance incoming traffic between two different versions of your function. +Before deployment, the alias sends 100% of invokes to the version used in production. +When you publish a new version of the function to your stack, CodeDeploy will send a small percentage of traffic to the new version, monitor, and validate before shifting 100% of traffic to the new version. + +To create a new CodeDeploy Deployment Group that deploys to a Lambda function: + +```ts +import codedeploy = require('@aws-cdk/aws-codedeploy'); +import lambda = require('@aws-cdk/aws-lambda'); + +const myApplication = new codedeploy.LambdaApplication(..); +const func = new lambda.Function(..); +const version = func.addVersion('1'); +const version1Alias = new lambda.Alias({ + aliasName: 'prod', + version +}); + +const deploymentGroup = new codedeploy.LambdaDeploymentGroup(stack, 'BlueGreenDeployment', { + application: myApplication, // optional property: one will be created for you if not provided + alias: version1Alias, + deploymentConfig: codedeploy.LambdaDeploymentConfig.Linear10PercentEvery1Minute, +}); +``` + +In order to deploy a new version of this function: +1. Increment the version, e.g. `const version = func.addVersion('2')`. +2. Re-deploy the stack (this will trigger a deployment). +3. Monitor the CodeDeploy deployment as traffic shifts between the versions. + +#### Rollbacks and Alarms + +CodeDeploy will roll back if the deployment fails. You can optionally trigger a rollback when one or more alarms are in a failed state: + +```ts +const deploymentGroup = new codedeploy.LambdaDeploymentGroup(stack, 'BlueGreenDeployment', { + alias, + deploymentConfig: codedeploy.LambdaDeploymentConfig.Linear10PercentEvery1Minute, + alarms: [ + // pass some alarms when constructing the deployment group + new cloudwatch.Alarm(stack, 'Errors', { + comparisonOperator: cloudwatch.ComparisonOperator.GreaterThanThreshold, + threshold: 1, + evaluationPeriods: 1, + metric: alias.metricErrors() + }) + ] +}); + +// or add alarms to an existing group +deploymentGroup.addAlarm(new cloudwatch.Alarm(stack, 'BlueGreenErrors', { + comparisonOperator: cloudwatch.ComparisonOperator.GreaterThanThreshold, + threshold: 1, + evaluationPeriods: 1, + metric: blueGreenAlias.metricErrors() +})); +``` + +#### Pre and Post Hooks + +CodeDeploy allows you to run an arbitrary Lambda function before traffic shifting actually starts (PreTraffic Hook) and after it completes (PostTraffic Hook). +With either hook, you have the opportunity to run logic that determines whether the deployment must succeed or fail. +For example, with PreTraffic hook you could run integration tests against the newly created Lambda version (but not serving traffic). With PostTraffic hook, you could run end-to-end validation checks. + +```ts +const warmUpUserCache = new lambda.Function(..); +const endToEndValidation = new lambda.Function(..); + +// pass a hook whe creating the deployment group +const deploymentGroup = new codedeploy.LambdaDeploymentGroup(stack, 'BlueGreenDeployment', { + alias: alias, + deploymentConfig: codedeploy.LambdaDeploymentConfig.Linear10PercentEvery1Minute, + preHook: warmUpUserCache, +}); + +// or configure one on an existing deployment group +deploymentGroup.onPostHook(endToEndValidation); +``` + +#### Import an existing Deployment Group + +To import an already existing Deployment Group: + +```ts +const deploymentGroup = codedeploy.LambdaDeploymentGroup.import(this, 'ExistingCodeDeployDeploymentGroup', { + application, + deploymentGroupName: 'MyExistingDeploymentGroup', +}); +``` diff --git a/packages/@aws-cdk/aws-codedeploy/lib/index.ts b/packages/@aws-cdk/aws-codedeploy/lib/index.ts index 2b4163f14be4f..d1d50615e1c8b 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/index.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/index.ts @@ -1,7 +1,7 @@ -export * from './application'; -export * from './deployment-config'; -export * from './deployment-group'; +export * from './rollback-config'; +export * from './lambda'; export * from './pipeline-action'; +export * from './server'; // AWS::CodeDeploy CloudFormation Resources: export * from './codedeploy.generated'; diff --git a/packages/@aws-cdk/aws-codedeploy/lib/lambda/application.ts b/packages/@aws-cdk/aws-codedeploy/lib/lambda/application.ts new file mode 100644 index 0000000000000..cbc4d2f3c78d5 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/lib/lambda/application.ts @@ -0,0 +1,101 @@ +import cdk = require("@aws-cdk/cdk"); +import { CfnApplication } from "../codedeploy.generated"; +import { applicationNameToArn } from "../utils"; + +/** + * Represents a reference to a CodeDeploy Application deploying to AWS Lambda. + * + * If you're managing the Application alongside the rest of your CDK resources, + * use the {@link LambdaApplication} class. + * + * If you want to reference an already existing Application, + * or one defined in a different CDK Stack, + * use the {@link LambdaApplication#import} method. + */ +export interface ILambdaApplication extends cdk.IConstruct { + readonly applicationArn: string; + readonly applicationName: string; + + export(): LambdaApplicationImportProps; +} + +/** + * Construction properties for {@link LambdaApplication}. + */ +export interface LambdaApplicationProps { + /** + * The physical, human-readable name of the CodeDeploy Application. + * + * @default an auto-generated name will be used + */ + applicationName?: string; +} + +/** + * A CodeDeploy Application that deploys to an AWS Lambda function. + */ +export class LambdaApplication extends cdk.Construct implements ILambdaApplication { + /** + * Import an Application defined either outside the CDK, + * or in a different CDK Stack and exported using the {@link ILambdaApplication#export} method. + * + * @param scope the parent Construct for this new Construct + * @param id the logical ID of this new Construct + * @param props the properties of the referenced Application + * @returns a Construct representing a reference to an existing Application + */ + public static import(scope: cdk.Construct, id: string, props: LambdaApplicationImportProps): ILambdaApplication { + return new ImportedLambdaApplication(scope, id, props); + } + + public readonly applicationArn: string; + public readonly applicationName: string; + + constructor(scope: cdk.Construct, id: string, props: LambdaApplicationProps = {}) { + super(scope, id); + + const resource = new CfnApplication(this, 'Resource', { + applicationName: props.applicationName, + computePlatform: 'Lambda' + }); + + this.applicationName = resource.ref; + this.applicationArn = applicationNameToArn(this.applicationName, this); + } + + public export(): LambdaApplicationImportProps { + return { + applicationName: new cdk.Output(this, 'ApplicationName', { value: this.applicationName }).makeImportValue().toString() + }; + } +} + +/** + * Properties of a reference to a CodeDeploy Application. + * + * @see LambdaApplication#import + * @see ILambdaApplication#export + */ +export interface LambdaApplicationImportProps { + /** + * The physical, human-readable name of the Lambda Application we're referencing. + * The Application must be in the same account and region as the root Stack. + */ + applicationName: string; +} + +class ImportedLambdaApplication extends cdk.Construct implements ILambdaApplication { + public readonly applicationArn: string; + public readonly applicationName: string; + + constructor(scope: cdk.Construct, id: string, private readonly props: LambdaApplicationImportProps) { + super(scope, id); + + this.applicationName = props.applicationName; + this.applicationArn = applicationNameToArn(props.applicationName, this); + } + + public export(): LambdaApplicationImportProps { + return this.props; + } +} diff --git a/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-config.ts b/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-config.ts new file mode 100644 index 0000000000000..e128d29458458 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-config.ts @@ -0,0 +1,95 @@ +import cdk = require('@aws-cdk/cdk'); +import { arnForDeploymentConfigName } from '../utils'; + +/** + * The Deployment Configuration of a Lambda Deployment Group. + * The default, pre-defined Configurations are available as constants on the {@link LambdaDeploymentConfig} class + * (`LambdaDeploymentConfig.AllAtOnce`, `LambdaDeploymentConfig.Canary10Percent30Minutes`, etc.). + * + * Note: CloudFormation does not currently support creating custom lambda configs outside + * of using a custom resource. You can import custom deployment config created outside the + * CDK or via a custom resource with {@link LambdaDeploymentConfig#import}. + */ +export interface ILambdaDeploymentConfig { + readonly deploymentConfigName: string; + deploymentConfigArn(scope: cdk.IConstruct): string; +} + +class DefaultLambdaDeploymentConfig implements ILambdaDeploymentConfig { + public readonly deploymentConfigName: string; + + constructor(deploymentConfigName: string) { + this.deploymentConfigName = `CodeDeployDefault.Lambda${deploymentConfigName}`; + } + + public deploymentConfigArn(scope: cdk.IConstruct): string { + return arnForDeploymentConfigName(this.deploymentConfigName, scope); + } +} + +/** + * Properties of a reference to a CodeDeploy Lambda Deployment Configuration. + * + * @see LambdaDeploymentConfig#import + * @see LambdaDeploymentConfig#export + */ +export interface LambdaDeploymentConfigImportProps { + /** + * The physical, human-readable name of the custom CodeDeploy Lambda Deployment Configuration + * that we are referencing. + */ + deploymentConfigName: string; +} + +class ImportedLambdaDeploymentConfig extends cdk.Construct implements ILambdaDeploymentConfig { + public readonly deploymentConfigName: string; + + constructor(scope: cdk.Construct, id: string, private readonly props: LambdaDeploymentConfigImportProps) { + super(scope, id); + + this.deploymentConfigName = props.deploymentConfigName; + } + + public deploymentConfigArn(scope: cdk.IConstruct): string { + return arnForDeploymentConfigName(this.deploymentConfigName, scope); + } + + public export() { + return this.props; + } +} + +/** + * A custom Deployment Configuration for a Lambda Deployment Group. + * + * Note: This class currently stands as namespaced container of the default configurations + * until CloudFormation supports custom Lambda Deployment Configs. Until then it is closed + * (private constructor) and does not extend {@link cdk.Construct} + */ +export class LambdaDeploymentConfig { + public static readonly AllAtOnce: ILambdaDeploymentConfig = new DefaultLambdaDeploymentConfig('AllAtOnce'); + public static readonly Canary10Percent30Minutes: ILambdaDeploymentConfig = new DefaultLambdaDeploymentConfig('Canary10Percent30Minutes'); + public static readonly Canary10Percent5Minutes: ILambdaDeploymentConfig = new DefaultLambdaDeploymentConfig('Canary10Percent5Minutes'); + public static readonly Canary10Percent10Minutes: ILambdaDeploymentConfig = new DefaultLambdaDeploymentConfig('Canary10Percent10Minutes'); + public static readonly Canary10Percent15Minutes: ILambdaDeploymentConfig = new DefaultLambdaDeploymentConfig('Canary10Percent15Minutes'); + public static readonly Linear10PercentEvery10Minutes: ILambdaDeploymentConfig = new DefaultLambdaDeploymentConfig('Linear10PercentEvery10Minutes'); + public static readonly Linear10PercentEvery1Minute: ILambdaDeploymentConfig = new DefaultLambdaDeploymentConfig('Linear10PercentEvery1Minute'); + public static readonly Linear10PercentEvery2Minutes: ILambdaDeploymentConfig = new DefaultLambdaDeploymentConfig('Linear10PercentEvery2Minutes'); + public static readonly Linear10PercentEvery3Minutes: ILambdaDeploymentConfig = new DefaultLambdaDeploymentConfig('Linear10PercentEvery3Minutes'); + + /** + * Import a custom Deployment Configuration for a Lambda Deployment Group defined outside the CDK. + * + * @param scope the parent Construct for this new Construct + * @param id the logical ID of this new Construct + * @param props the properties of the referenced custom Deployment Configuration + * @returns a Construct representing a reference to an existing custom Deployment Configuration + */ + public static import(scope: cdk.Construct, id: string, props: LambdaDeploymentConfigImportProps): ILambdaDeploymentConfig { + return new ImportedLambdaDeploymentConfig(scope, id, props); + } + + private constructor() { + // nothing to do until CFN supports custom lambda deployment configurations + } +} diff --git a/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-group.ts b/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-group.ts new file mode 100644 index 0000000000000..bedf02c871b7d --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-group.ts @@ -0,0 +1,283 @@ +import cloudwatch = require('@aws-cdk/aws-cloudwatch'); +import iam = require('@aws-cdk/aws-iam'); +import lambda = require('@aws-cdk/aws-lambda'); +import cdk = require('@aws-cdk/cdk'); + +import { CfnDeploymentGroup } from '../codedeploy.generated'; +import { AutoRollbackConfig } from '../rollback-config'; +import { deploymentGroupNameToArn, renderAlarmConfiguration, renderAutoRollbackConfiguration } from '../utils'; +import { ILambdaApplication, LambdaApplication } from './application'; +import { ILambdaDeploymentConfig, LambdaDeploymentConfig } from './deployment-config'; + +/** + * Interface for a Lambda deployment groups. + */ +export interface ILambdaDeploymentGroup extends cdk.IConstruct { + /** + * The reference to the CodeDeploy Lambda Application that this Deployment Group belongs to. + */ + readonly application: ILambdaApplication; + + /** + * The physical name of the CodeDeploy Deployment Group. + */ + readonly deploymentGroupName: string; + + /** + * The ARN of this Deployment Group. + */ + readonly deploymentGroupArn: string; + + /** + * Export this Deployment Group for use in another stack or application. + */ + export(): LambdaDeploymentGroupImportProps; +} + +/** + * Construction properties for {@link LambdaDeploymentGroup}. + */ +export interface LambdaDeploymentGroupProps { + /** + * The reference to the CodeDeploy Lambda Application that this Deployment Group belongs to. + * + * @default one will be created for you + */ + application?: LambdaApplication; + + /** + * The physical, human-readable name of the CodeDeploy Deployment Group. + * + * @default an auto-generated name will be used + */ + deploymentGroupName?: string; + + /** + * The Deployment Configuration this Deployment Group uses. + * + * @default LambdaDeploymentConfig#AllAtOnce + */ + deploymentConfig?: ILambdaDeploymentConfig; + + /** + * The CloudWatch alarms associated with this Deployment Group. + * CodeDeploy will stop (and optionally roll back) + * a deployment if during it any of the alarms trigger. + * + * Alarms can also be added after the Deployment Group is created using the {@link #addAlarm} method. + * + * @default [] + * @see https://docs.aws.amazon.com/codedeploy/latest/userguide/monitoring-create-alarms.html + */ + alarms?: cloudwatch.Alarm[]; + + /** + * The service Role of this Deployment Group. + * + * @default a new Role will be created. + */ + role?: iam.Role; + + /** + * Lambda Alias to shift traffic. Updating the version + * of the alias will trigger a CodeDeploy deployment. + */ + alias: lambda.Alias; + + /** + * The Lambda function to run before traffic routing starts. + */ + preHook?: lambda.IFunction; + + /** + * The Lambda function to run after traffic routing starts. + */ + postHook?: lambda.IFunction; + + /** + * Whether to continue a deployment even if fetching the alarm status from CloudWatch failed. + * + * @default false + */ + ignorePollAlarmsFailure?: boolean; + + /** + * The auto-rollback configuration for this Deployment Group. + */ + autoRollback?: AutoRollbackConfig; +} + +export class LambdaDeploymentGroup extends cdk.Construct implements ILambdaDeploymentGroup { + /** + * Import an Lambda Deployment Group defined either outside the CDK, + * or in a different CDK Stack and exported using the {@link #export} method. + * + * @param scope the parent Construct for this new Construct + * @param id the logical ID of this new Construct + * @param props the properties of the referenced Deployment Group + * @returns a Construct representing a reference to an existing Deployment Group + */ + public static import(scope: cdk.Construct, id: string, props: LambdaDeploymentGroupImportProps): ILambdaDeploymentGroup { + return new ImportedLambdaDeploymentGroup(scope, id, props); + } + + public readonly application: ILambdaApplication; + public readonly deploymentGroupName: string; + public readonly deploymentGroupArn: string; + public readonly role: iam.Role; + + private readonly alarms: cloudwatch.Alarm[]; + private preHook?: lambda.IFunction; + private postHook?: lambda.IFunction; + + constructor(scope: cdk.Construct, id: string, props: LambdaDeploymentGroupProps) { + super(scope, id); + + this.application = props.application || new LambdaApplication(this, 'Application'); + this.alarms = props.alarms || []; + + let serviceRole: iam.Role | undefined = props.role; + if (serviceRole) { + if (serviceRole.assumeRolePolicy) { + serviceRole.assumeRolePolicy.addStatement(new iam.PolicyStatement() + .addAction('sts:AssumeRole') + .addServicePrincipal('codedeploy.amazonaws.com')); + } + } else { + serviceRole = new iam.Role(this, 'ServiceRole', { + assumedBy: new iam.ServicePrincipal('codedeploy.amazonaws.com') + }); + } + serviceRole.attachManagedPolicy('arn:aws:iam::aws:policy/service-role/AWSCodeDeployRoleForLambda'); + this.role = serviceRole; + + const resource = new CfnDeploymentGroup(this, 'Resource', { + applicationName: this.application.applicationName, + serviceRoleArn: serviceRole.roleArn, + deploymentGroupName: props.deploymentGroupName, + deploymentConfigName: (props.deploymentConfig || LambdaDeploymentConfig.AllAtOnce).deploymentConfigName, + deploymentStyle: { + deploymentType: 'BLUE_GREEN', + deploymentOption: 'WITH_TRAFFIC_CONTROL' + }, + alarmConfiguration: new cdk.Token(() => renderAlarmConfiguration(this.alarms, props.ignorePollAlarmsFailure)), + autoRollbackConfiguration: new cdk.Token(() => renderAutoRollbackConfiguration(this.alarms, props.autoRollback)), + }); + + this.deploymentGroupName = resource.deploymentGroupName; + this.deploymentGroupArn = deploymentGroupNameToArn(this.application.applicationName, this.deploymentGroupName, this); + + if (props.preHook) { + this.onPreHook(props.preHook); + } + if (props.postHook) { + this.onPostHook(props.postHook); + } + + (props.alias.node.findChild('Resource') as lambda.CfnAlias).options.updatePolicy = { + codeDeployLambdaAliasUpdate: { + applicationName: this.application.applicationName, + deploymentGroupName: resource.deploymentGroupName, + beforeAllowTrafficHook: new cdk.Token(() => this.preHook === undefined ? undefined : this.preHook.functionName).toString(), + afterAllowTrafficHook: new cdk.Token(() => this.postHook === undefined ? undefined : this.postHook.functionName).toString() + } + }; + } + + /** + * Associates an additional alarm with this Deployment Group. + * + * @param alarm the alarm to associate with this Deployment Group + */ + public addAlarm(alarm: cloudwatch.Alarm): void { + this.alarms.push(alarm); + } + + /** + * Associate a function to run before deployment begins. + * @param preHook function to run before deployment beings + * @throws an error if a pre-hook function is already configured + */ + public onPreHook(preHook: lambda.IFunction): void { + if (this.preHook !== undefined) { + throw new Error('A pre-hook function is already defined for this deployment group'); + } + this.preHook = preHook; + this.grantPutLifecycleEventHookExecutionStatus(this.preHook.role); + this.preHook.grantInvoke(this.role); + } + + /** + * Associate a function to run after deployment completes. + * @param preHook function to run after deployment completes + * @throws an error if a post-hook function is already configured + */ + public onPostHook(postHook: lambda.IFunction): void { + if (this.postHook !== undefined) { + throw new Error('A post-hook function is already defined for this deployment group'); + } + this.postHook = postHook; + this.grantPutLifecycleEventHookExecutionStatus(this.postHook.role); + this.postHook.grantInvoke(this.role); + } + + /** + * Grant a principal permission to codedeploy:PutLifecycleEventHookExecutionStatus + * on this deployment group resource. + * @param principal to grant permission to + */ + public grantPutLifecycleEventHookExecutionStatus(principal?: iam.IPrincipal): void { + if (principal) { + principal.addToPolicy(new iam.PolicyStatement() + .addResource(this.deploymentGroupArn) + .addAction('codedeploy:PutLifecycleEventHookExecutionStatus')); + } + } + + public export(): LambdaDeploymentGroupImportProps { + return { + application: this.application, + deploymentGroupName: new cdk.Output(this, 'DeploymentGroupName', { + value: this.deploymentGroupName + }).makeImportValue().toString() + }; + } +} + +/** + * Properties of a reference to a CodeDeploy Lambda Deployment Group. + * + * @see LambdaDeploymentGroup#import + * @see ILambdaDeploymentGroup#export + */ +export interface LambdaDeploymentGroupImportProps { + /** + * The reference to the CodeDeploy Lambda Application + * that this Deployment Group belongs to. + */ + application: ILambdaApplication; + + /** + * The physical, human-readable name of the CodeDeploy Lambda Deployment Group + * that we are referencing. + */ + deploymentGroupName: string; +} + +class ImportedLambdaDeploymentGroup extends cdk.Construct implements ILambdaDeploymentGroup { + public readonly application: ILambdaApplication; + public readonly deploymentGroupName: string; + public readonly deploymentGroupArn: string; + + constructor(scope: cdk.Construct, id: string, private readonly props: LambdaDeploymentGroupImportProps) { + super(scope, id); + this.application = props.application; + this.deploymentGroupName = props.deploymentGroupName; + this.deploymentGroupArn = deploymentGroupNameToArn(props.application.applicationName, + props.deploymentGroupName, this); + } + + public export() { + return this.props; + } +} diff --git a/packages/@aws-cdk/aws-codedeploy/lib/lambda/index.ts b/packages/@aws-cdk/aws-codedeploy/lib/lambda/index.ts new file mode 100644 index 0000000000000..933062a88e18e --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/lib/lambda/index.ts @@ -0,0 +1,3 @@ +export * from './application'; +export * from './deployment-config'; +export * from './deployment-group'; diff --git a/packages/@aws-cdk/aws-codedeploy/lib/pipeline-action.ts b/packages/@aws-cdk/aws-codedeploy/lib/pipeline-action.ts index 70f44bbb87df0..d03585c41e894 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/pipeline-action.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/pipeline-action.ts @@ -1,7 +1,7 @@ import codepipeline = require('@aws-cdk/aws-codepipeline-api'); import iam = require('@aws-cdk/aws-iam'); import cdk = require('@aws-cdk/cdk'); -import { IServerDeploymentGroup } from './deployment-group'; +import { IServerDeploymentGroup } from './server'; /** * Common properties for creating a {@link PipelineDeployAction}, diff --git a/packages/@aws-cdk/aws-codedeploy/lib/rollback-config.ts b/packages/@aws-cdk/aws-codedeploy/lib/rollback-config.ts new file mode 100644 index 0000000000000..eb399ae9fd522 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/lib/rollback-config.ts @@ -0,0 +1,26 @@ +/** + * The configuration for automatically rolling back deployments in a given Deployment Group. + */ +export interface AutoRollbackConfig { + /** + * Whether to automatically roll back a deployment that fails. + * + * @default true + */ + failedDeployment?: boolean; + + /** + * Whether to automatically roll back a deployment that was manually stopped. + * + * @default false + */ + stoppedDeployment?: boolean; + + /** + * Whether to automatically roll back a deployment during which one of the configured + * CloudWatch alarms for this Deployment Group went off. + * + * @default true if you've provided any Alarms with the `alarms` property, false otherwise + */ + deploymentInAlarm?: boolean; +} diff --git a/packages/@aws-cdk/aws-codedeploy/lib/application.ts b/packages/@aws-cdk/aws-codedeploy/lib/server/application.ts similarity index 85% rename from packages/@aws-cdk/aws-codedeploy/lib/application.ts rename to packages/@aws-cdk/aws-codedeploy/lib/server/application.ts index e2aeb6ce4087b..3ea12f261677d 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/application.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/server/application.ts @@ -1,5 +1,6 @@ import cdk = require('@aws-cdk/cdk'); -import { CfnApplication } from './codedeploy.generated'; +import { CfnApplication } from '../codedeploy.generated'; +import { applicationNameToArn } from '../utils'; /** * Represents a reference to a CodeDeploy Application deploying to EC2/on-premise instances. @@ -13,7 +14,6 @@ import { CfnApplication } from './codedeploy.generated'; */ export interface IServerApplication extends cdk.IConstruct { readonly applicationArn: string; - readonly applicationName: string; export(): ServerApplicationImportProps; @@ -44,7 +44,7 @@ class ImportedServerApplication extends cdk.Construct implements IServerApplicat this.applicationArn = applicationNameToArn(this.applicationName, this); } - public export() { + public export(): ServerApplicationImportProps { return this.props; } } @@ -69,7 +69,7 @@ export class ServerApplication extends cdk.Construct implements IServerApplicati * Import an Application defined either outside the CDK, * or in a different CDK Stack and exported using the {@link #export} method. * - * @param parent the parent Construct for this new Construct + * @param scope the parent Construct for this new Construct * @param id the logical ID of this new Construct * @param props the properties of the referenced Application * @returns a Construct representing a reference to an existing Application @@ -81,11 +81,11 @@ export class ServerApplication extends cdk.Construct implements IServerApplicati public readonly applicationArn: string; public readonly applicationName: string; - constructor(scope: cdk.Construct, id: string, props?: ServerApplicationProps) { + constructor(scope: cdk.Construct, id: string, props: ServerApplicationProps = {}) { super(scope, id); const resource = new CfnApplication(this, 'Resource', { - applicationName: props && props.applicationName, + applicationName: props.applicationName, computePlatform: 'Server', }); @@ -99,12 +99,3 @@ export class ServerApplication extends cdk.Construct implements IServerApplicati }; } } - -function applicationNameToArn(applicationName: string, scope: cdk.IConstruct): string { - return cdk.Stack.find(scope).formatArn({ - service: 'codedeploy', - resource: 'application', - resourceName: applicationName, - sep: ':', - }); -} diff --git a/packages/@aws-cdk/aws-codedeploy/lib/deployment-config.ts b/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-config.ts similarity index 94% rename from packages/@aws-cdk/aws-codedeploy/lib/deployment-config.ts rename to packages/@aws-cdk/aws-codedeploy/lib/server/deployment-config.ts index dfca704574f25..88f76c0bad8b3 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/deployment-config.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-config.ts @@ -1,5 +1,6 @@ import cdk = require('@aws-cdk/cdk'); -import { CfnDeploymentConfig } from './codedeploy.generated'; +import { CfnDeploymentConfig } from '../codedeploy.generated'; +import { arnForDeploymentConfigName } from '../utils'; /** * The Deployment Configuration of an EC2/on-premise Deployment Group. @@ -104,7 +105,7 @@ export class ServerDeploymentConfig extends cdk.Construct implements IServerDepl * Import a custom Deployment Configuration for an EC2/on-premise Deployment Group defined either outside the CDK, * or in a different CDK Stack and exported using the {@link #export} method. * - * @param parent the parent Construct for this new Construct + * @param scope the parent Construct for this new Construct * @param id the logical ID of this new Construct * @param props the properties of the referenced custom Deployment Configuration * @returns a Construct representing a reference to an existing custom Deployment Configuration @@ -155,12 +156,3 @@ export class ServerDeploymentConfig extends cdk.Construct implements IServerDepl }; } } - -function arnForDeploymentConfigName(name: string, scope: cdk.IConstruct): string { - return cdk.Stack.find(scope).formatArn({ - service: 'codedeploy', - resource: 'deploymentconfig', - resourceName: name, - sep: ':', - }); -} diff --git a/packages/@aws-cdk/aws-codedeploy/lib/deployment-group.ts b/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-group.ts similarity index 82% rename from packages/@aws-cdk/aws-codedeploy/lib/deployment-group.ts rename to packages/@aws-cdk/aws-codedeploy/lib/server/deployment-group.ts index 083cb853653ad..d9163c5ba022a 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/deployment-group.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-group.ts @@ -1,15 +1,17 @@ -import autoscaling = require("@aws-cdk/aws-autoscaling"); -import cloudwatch = require("@aws-cdk/aws-cloudwatch"); -import codedeploylb = require("@aws-cdk/aws-codedeploy-api"); -import codepipeline = require("@aws-cdk/aws-codepipeline-api"); -import ec2 = require("@aws-cdk/aws-ec2"); +import autoscaling = require('@aws-cdk/aws-autoscaling'); +import cloudwatch = require('@aws-cdk/aws-cloudwatch'); +import codedeploylb = require('@aws-cdk/aws-codedeploy-api'); +import codepipeline = require('@aws-cdk/aws-codepipeline-api'); +import ec2 = require('@aws-cdk/aws-ec2'); import iam = require('@aws-cdk/aws-iam'); -import s3 = require("@aws-cdk/aws-s3"); -import cdk = require("@aws-cdk/cdk"); -import { IServerApplication, ServerApplication } from "./application"; -import { CfnDeploymentGroup } from './codedeploy.generated'; -import { IServerDeploymentConfig, ServerDeploymentConfig } from "./deployment-config"; -import { CommonPipelineDeployActionProps, PipelineDeployAction } from "./pipeline-action"; +import s3 = require('@aws-cdk/aws-s3'); +import cdk = require('@aws-cdk/cdk'); +import { CfnDeploymentGroup } from '../codedeploy.generated'; +import { CommonPipelineDeployActionProps, PipelineDeployAction } from '../pipeline-action'; +import { AutoRollbackConfig } from '../rollback-config'; +import { deploymentGroupNameToArn, renderAlarmConfiguration, renderAutoRollbackConfiguration } from '../utils'; +import { IServerApplication, ServerApplication } from './application'; +import { IServerDeploymentConfig, ServerDeploymentConfig } from './deployment-config'; export interface IServerDeploymentGroup extends cdk.IConstruct { readonly application: IServerApplication; @@ -148,33 +150,6 @@ export class InstanceTagSet { } } -/** - * The configuration for automatically rolling back deployments in a given Deployment Group. - */ -export interface AutoRollbackConfig { - /** - * Whether to automatically roll back a deployment that fails. - * - * @default true - */ - failedDeployment?: boolean; - - /** - * Whether to automatically roll back a deployment that was manually stopped. - * - * @default false - */ - stoppedDeployment?: boolean; - - /** - * Whether to automatically roll back a deployment during which one of the configured - * CloudWatch alarms for this Deployment Group went off. - * - * @default true if you've provided any Alarms with the `alarms` property, false otherwise - */ - deploymentInAlarm?: boolean; -} - /** * Construction properties for {@link ServerDeploymentGroup}. */ @@ -279,7 +254,7 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupBase { * Import an EC2/on-premise Deployment Group defined either outside the CDK, * or in a different CDK Stack and exported using the {@link #export} method. * - * @param parent the parent Construct for this new Construct + * @param scope the parent Construct for this new Construct * @param id the logical ID of this new Construct * @param props the properties of the referenced Deployment Group * @returns a Construct representing a reference to an existing Deployment Group @@ -338,8 +313,8 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupBase { }, ec2TagSet: this.ec2TagSet(props.ec2InstanceTags), onPremisesTagSet: this.onPremiseTagSet(props.onPremiseInstanceTags), - alarmConfiguration: new cdk.Token(() => this.renderAlarmConfiguration(props.ignorePollAlarmsFailure)), - autoRollbackConfiguration: new cdk.Token(() => this.renderAutoRollbackConfiguration(props.autoRollback)), + alarmConfiguration: new cdk.Token(() => renderAlarmConfiguration(this.alarms, props.ignorePollAlarmsFailure)), + autoRollbackConfiguration: new cdk.Token(() => renderAutoRollbackConfiguration(this.alarms, props.autoRollback)), }); this.deploymentGroupName = resource.deploymentGroupName; @@ -513,58 +488,4 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupBase { } return tagsInGroup; } - - private renderAlarmConfiguration(ignorePollAlarmFailure?: boolean): - CfnDeploymentGroup.AlarmConfigurationProperty | undefined { - return this.alarms.length === 0 - ? undefined - : { - alarms: this.alarms.map(a => ({ name: a.alarmName })), - enabled: true, - ignorePollAlarmFailure, - }; - } - - private renderAutoRollbackConfiguration(autoRollbackConfig: AutoRollbackConfig = {}): - CfnDeploymentGroup.AutoRollbackConfigurationProperty | undefined { - const events = new Array(); - - // we roll back failed deployments by default - if (autoRollbackConfig.failedDeployment !== false) { - events.push('DEPLOYMENT_FAILURE'); - } - - // we _do not_ roll back stopped deployments by default - if (autoRollbackConfig.stoppedDeployment === true) { - events.push('DEPLOYMENT_STOP_ON_REQUEST'); - } - - // we _do not_ roll back alarm-triggering deployments by default - // unless the Deployment Group has at least one alarm - if (autoRollbackConfig.deploymentInAlarm !== false) { - if (this.alarms.length > 0) { - events.push('DEPLOYMENT_STOP_ON_ALARM'); - } else if (autoRollbackConfig.deploymentInAlarm === true) { - throw new Error( - "The auto-rollback setting 'deploymentInAlarm' does not have any effect unless you associate " + - "at least one CloudWatch alarm with the Deployment Group"); - } - } - - return events.length > 0 - ? { - enabled: true, - events, - } - : undefined; - } -} - -function deploymentGroupNameToArn(applicationName: string, deploymentGroupName: string, scope: cdk.IConstruct): string { - return cdk.Stack.find(scope).formatArn({ - service: 'codedeploy', - resource: 'deploymentgroup', - resourceName: `${applicationName}/${deploymentGroupName}`, - sep: ':', - }); } diff --git a/packages/@aws-cdk/aws-codedeploy/lib/server/index.ts b/packages/@aws-cdk/aws-codedeploy/lib/server/index.ts new file mode 100644 index 0000000000000..933062a88e18e --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/lib/server/index.ts @@ -0,0 +1,3 @@ +export * from './application'; +export * from './deployment-config'; +export * from './deployment-group'; diff --git a/packages/@aws-cdk/aws-codedeploy/lib/utils.ts b/packages/@aws-cdk/aws-codedeploy/lib/utils.ts new file mode 100644 index 0000000000000..6c54dad262fab --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/lib/utils.ts @@ -0,0 +1,82 @@ +import cloudwatch = require('@aws-cdk/aws-cloudwatch'); +import cdk = require('@aws-cdk/cdk'); +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({ + service: 'codedeploy', + resource: 'application', + resourceName: applicationName, + sep: ':', + }); +} + +export function deploymentGroupNameToArn(applicationName: string, deploymentGroupName: string, scope: cdk.IConstruct): string { + return cdk.Stack.find(scope).formatArn({ + service: 'codedeploy', + resource: 'deploymentgroup', + resourceName: `${applicationName}/${deploymentGroupName}`, + sep: ':', + }); +} + +export function arnForDeploymentConfigName(name: string, scope: cdk.IConstruct): string { + return cdk.Stack.find(scope).formatArn({ + service: 'codedeploy', + resource: 'deploymentconfig', + resourceName: name, + sep: ':', + }); +} + +export function renderAlarmConfiguration(alarms: cloudwatch.Alarm[], ignorePollAlarmFailure?: boolean): + CfnDeploymentGroup.AlarmConfigurationProperty | undefined { + return alarms.length === 0 + ? undefined + : { + alarms: alarms.map(a => ({ name: a.alarmName })), + enabled: true, + ignorePollAlarmFailure, + }; +} + +enum AutoRollbackEvent { + DeploymentFailure = 'DEPLOYMENT_FAILURE', + DeploymentStopOnAlarm = 'DEPLOYMENT_STOP_ON_ALARM', + DeploymentStopOnRequest = 'DEPLOYMENT_STOP_ON_REQUEST' +} + +export function renderAutoRollbackConfiguration(alarms: cloudwatch.Alarm[], autoRollbackConfig: AutoRollbackConfig = {}): + CfnDeploymentGroup.AutoRollbackConfigurationProperty | undefined { + const events = new Array(); + + // we roll back failed deployments by default + if (autoRollbackConfig.failedDeployment !== false) { + events.push(AutoRollbackEvent.DeploymentFailure); + } + + // we _do not_ roll back stopped deployments by default + if (autoRollbackConfig.stoppedDeployment === true) { + events.push(AutoRollbackEvent.DeploymentStopOnRequest); + } + + // we _do not_ roll back alarm-triggering deployments by default + // unless the Deployment Group has at least one alarm + if (autoRollbackConfig.deploymentInAlarm !== false) { + if (alarms.length > 0) { + events.push(AutoRollbackEvent.DeploymentStopOnAlarm); + } else if (autoRollbackConfig.deploymentInAlarm === true) { + throw new Error( + "The auto-rollback setting 'deploymentInAlarm' does not have any effect unless you associate " + + "at least one CloudWatch alarm with the Deployment Group"); + } + } + + return events.length > 0 + ? { + enabled: true, + events, + } + : undefined; +} diff --git a/packages/@aws-cdk/aws-codedeploy/package.json b/packages/@aws-cdk/aws-codedeploy/package.json index 31b3c9d4a1252..ba37d801f2568 100644 --- a/packages/@aws-cdk/aws-codedeploy/package.json +++ b/packages/@aws-cdk/aws-codedeploy/package.json @@ -69,6 +69,7 @@ "@aws-cdk/aws-codedeploy-api": "^0.23.0", "@aws-cdk/aws-codepipeline-api": "^0.23.0", "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-lambda": "^0.23.0", "@aws-cdk/aws-s3": "^0.23.0", "@aws-cdk/cdk": "^0.23.0" }, @@ -79,6 +80,7 @@ "@aws-cdk/aws-codedeploy-api": "^0.23.0", "@aws-cdk/aws-codepipeline-api": "^0.23.0", "@aws-cdk/aws-iam": "^0.23.0", + "@aws-cdk/aws-lambda": "^0.23.0", "@aws-cdk/cdk": "^0.23.0" }, "engines": { @@ -89,4 +91,4 @@ "construct-ctor:@aws-cdk/aws-codedeploy.ServerDeploymentGroupBase..params[2]" ] } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/handler/index.js b/packages/@aws-cdk/aws-codedeploy/test/lambda/handler/index.js new file mode 100644 index 0000000000000..a29f80fa0ff77 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/handler/index.js @@ -0,0 +1,3 @@ +exports.handler = async (event, context) => { + return 'Hello, World!'; +} \ 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 new file mode 100644 index 0000000000000..6dfeb4061a356 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-group.expected.json @@ -0,0 +1,549 @@ +{ + "Resources": { + "HandlerServiceRoleFCDC14AE": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "Handler886CB40B": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "HandlerCodeS3Bucket8DD11ED9" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "HandlerCodeS3VersionKey0BB5191E" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "HandlerCodeS3VersionKey0BB5191E" + } + ] + } + ] + } + ] + ] + } + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "HandlerServiceRoleFCDC14AE", + "Arn" + ] + }, + "Runtime": "nodejs8.10" + }, + "DependsOn": [ + "HandlerServiceRoleFCDC14AE" + ] + }, + "HandlerVersion1F237F6D0": { + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "Handler886CB40B" + } + } + }, + "Alias325C5727": { + "Type": "AWS::Lambda::Alias", + "Properties": { + "FunctionName": { + "Ref": "Handler886CB40B" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "HandlerVersion1F237F6D0", + "Version" + ] + }, + "Name": "alias" + }, + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "BlueGreenDeploymentApplication36C892C0" + }, + "DeploymentGroupName": { + "Ref": "BlueGreenDeployment5C188134" + }, + "BeforeAllowTrafficHook": { + "Ref": "PreHook8B53F672" + }, + "AfterAllowTrafficHook": { + "Ref": "PostHookF2E49B30" + } + } + } + }, + "PreHookServiceRoleC724B9BA": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "PreHookServiceRoleDefaultPolicy65358F76": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "codedeploy:PutLifecycleEventHookExecutionStatus", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":codedeploy:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":deploymentgroup:", + { + "Ref": "BlueGreenDeploymentApplication36C892C0" + }, + "/", + { + "Ref": "BlueGreenDeployment5C188134" + } + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PreHookServiceRoleDefaultPolicy65358F76", + "Roles": [ + { + "Ref": "PreHookServiceRoleC724B9BA" + } + ] + } + }, + "PreHook8B53F672": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "PreHookCodeS3BucketE2616D65" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "PreHookCodeS3VersionKey292C5372" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "PreHookCodeS3VersionKey292C5372" + } + ] + } + ] + } + ] + ] + } + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "PreHookServiceRoleC724B9BA", + "Arn" + ] + }, + "Runtime": "nodejs8.10" + }, + "DependsOn": [ + "PreHookServiceRoleC724B9BA", + "PreHookServiceRoleDefaultPolicy65358F76" + ] + }, + "PostHookServiceRoleE8A6AAC2": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "PostHookServiceRoleDefaultPolicy82AEE758": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "codedeploy:PutLifecycleEventHookExecutionStatus", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":codedeploy:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":deploymentgroup:", + { + "Ref": "BlueGreenDeploymentApplication36C892C0" + }, + "/", + { + "Ref": "BlueGreenDeployment5C188134" + } + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PostHookServiceRoleDefaultPolicy82AEE758", + "Roles": [ + { + "Ref": "PostHookServiceRoleE8A6AAC2" + } + ] + } + }, + "PostHookF2E49B30": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "PostHookCodeS3BucketECF09EB8" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "PostHookCodeS3VersionKey53451C7E" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "PostHookCodeS3VersionKey53451C7E" + } + ] + } + ] + } + ] + ] + } + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "PostHookServiceRoleE8A6AAC2", + "Arn" + ] + }, + "Runtime": "nodejs8.10" + }, + "DependsOn": [ + "PostHookServiceRoleDefaultPolicy82AEE758", + "PostHookServiceRoleE8A6AAC2" + ] + }, + "BlueGreenErrors60C27452": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "ComparisonOperator": "GreaterThanThreshold", + "EvaluationPeriods": 1, + "Threshold": 1, + "Dimensions": [ + { + "Name": "FunctionName", + "Value": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "Handler886CB40B", + "Arn" + ] + }, + ":alias" + ] + ] + } + } + ], + "MetricName": "Errors", + "Namespace": "AWS/Lambda", + "Period": 300, + "Statistic": "Sum" + } + }, + "BlueGreenDeploymentApplication36C892C0": { + "Type": "AWS::CodeDeploy::Application", + "Properties": { + "ComputePlatform": "Lambda" + } + }, + "BlueGreenDeploymentServiceRole225851FB": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codedeploy.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSCodeDeployRoleForLambda" + ] + } + }, + "BlueGreenDeploymentServiceRoleDefaultPolicy7008FB0A": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "lambda:InvokeFunction", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PreHook8B53F672", + "Arn" + ] + } + }, + { + "Action": "lambda:InvokeFunction", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PostHookF2E49B30", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "BlueGreenDeploymentServiceRoleDefaultPolicy7008FB0A", + "Roles": [ + { + "Ref": "BlueGreenDeploymentServiceRole225851FB" + } + ] + } + }, + "BlueGreenDeployment5C188134": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "BlueGreenDeploymentApplication36C892C0" + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "BlueGreenDeploymentServiceRole225851FB", + "Arn" + ] + }, + "AlarmConfiguration": { + "Alarms": [ + { + "Name": { + "Ref": "BlueGreenErrors60C27452" + } + } + ], + "Enabled": true + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM" + ] + }, + "DeploymentConfigName": "CodeDeployDefault.LambdaLinear10PercentEvery1Minute", + "DeploymentStyle": { + "DeploymentOption": "WITH_TRAFFIC_CONTROL", + "DeploymentType": "BLUE_GREEN" + } + } + } + }, + "Parameters": { + "HandlerCodeS3Bucket8DD11ED9": { + "Type": "String", + "Description": "S3 bucket for asset \"aws-cdk-codedeploy-lambda/Handler/Code\"" + }, + "HandlerCodeS3VersionKey0BB5191E": { + "Type": "String", + "Description": "S3 key for asset version \"aws-cdk-codedeploy-lambda/Handler/Code\"" + }, + "PreHookCodeS3BucketE2616D65": { + "Type": "String", + "Description": "S3 bucket for asset \"aws-cdk-codedeploy-lambda/PreHook/Code\"" + }, + "PreHookCodeS3VersionKey292C5372": { + "Type": "String", + "Description": "S3 key for asset version \"aws-cdk-codedeploy-lambda/PreHook/Code\"" + }, + "PostHookCodeS3BucketECF09EB8": { + "Type": "String", + "Description": "S3 bucket for asset \"aws-cdk-codedeploy-lambda/PostHook/Code\"" + }, + "PostHookCodeS3VersionKey53451C7E": { + "Type": "String", + "Description": "S3 key for asset version \"aws-cdk-codedeploy-lambda/PostHook/Code\"" + } + } +} diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-group.ts b/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-group.ts new file mode 100644 index 0000000000000..1090f9f8501a7 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-group.ts @@ -0,0 +1,48 @@ +import cloudwatch = require('@aws-cdk/aws-cloudwatch'); +import lambda = require('@aws-cdk/aws-lambda'); +import cdk = require('@aws-cdk/cdk'); +import codedeploy = require('../../lib'); + +import path = require('path'); + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'aws-cdk-codedeploy-lambda'); + +const handler = new lambda.Function(stack, `Handler`, { + code: lambda.Code.asset(path.join(__dirname, 'handler')), + handler: 'index.handler', + runtime: lambda.Runtime.NodeJS810, +}); +const version = handler.addVersion('1'); +const blueGreenAlias = new lambda.Alias(stack, `Alias`, { + aliasName: `alias`, + version +}); + +const preHook = new lambda.Function(stack, `PreHook`, { + code: lambda.Code.asset(path.join(__dirname, 'preHook')), + handler: 'index.handler', + runtime: lambda.Runtime.NodeJS810 +}); +const postHook = new lambda.Function(stack, `PostHook`, { + code: lambda.Code.asset(path.join(__dirname, 'postHook')), + handler: 'index.handler', + runtime: lambda.Runtime.NodeJS810 +}); + +new codedeploy.LambdaDeploymentGroup(stack, 'BlueGreenDeployment', { + alias: blueGreenAlias, + deploymentConfig: codedeploy.LambdaDeploymentConfig.Linear10PercentEvery1Minute, + alarms: [ + new cloudwatch.Alarm(stack, 'BlueGreenErrors', { + comparisonOperator: cloudwatch.ComparisonOperator.GreaterThanThreshold, + threshold: 1, + evaluationPeriods: 1, + metric: blueGreenAlias.metricErrors() + }) + ], + preHook, + postHook +}); + +app.run(); diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/postHook/index.js b/packages/@aws-cdk/aws-codedeploy/test/lambda/postHook/index.js new file mode 100644 index 0000000000000..ca32788d0395b --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/postHook/index.js @@ -0,0 +1,24 @@ +'use strict'; + +const aws = require('aws-sdk'); +const codedeploy = new aws.CodeDeploy({ apiVersion: '2014-10-06' }); + +exports.handler = (event, context, callback) => { + console.log('post hook'); + /* + [Perform post-validation steps here] + */ + + // Pass AWS CodeDeploy the prepared validation test results. + codedeploy.putLifecycleEventHookExecutionStatus({ + deploymentId: event.DeploymentId, + lifecycleEventHookExecutionId: event.LifecycleEventHookExecutionId, + status: 'Succeeded' // status can be 'Succeeded' or 'Failed' + }, function (err, data) { + if (err) { + callback('Validation test failed'); + } else { + callback(null, 'Validation test succeeded'); + } + }); +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/preHook/index.js b/packages/@aws-cdk/aws-codedeploy/test/lambda/preHook/index.js new file mode 100644 index 0000000000000..6302421b79ec4 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/preHook/index.js @@ -0,0 +1,25 @@ +'use strict'; + +const aws = require('aws-sdk'); +const codedeploy = new aws.CodeDeploy({ apiVersion: '2014-10-06' }); + +exports.handler = (event, context, callback) => { + console.log('pre hook'); + /* + [Perform pre-validation or pre-warming steps here] + */ + + // Pass AWS CodeDeploy the prepared validation test results. + codedeploy.putLifecycleEventHookExecutionStatus({ + deploymentId: event.DeploymentId, + lifecycleEventHookExecutionId: event.LifecycleEventHookExecutionId, + status: 'Succeeded' // status can be 'Succeeded' or 'Failed' + }, function (err, data) { + console.log(err); + if (err) { + callback('Validation test failed'); + } else { + callback(null, 'Validation test succeeded'); + } + }); +} diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/test.application.ts b/packages/@aws-cdk/aws-codedeploy/test/lambda/test.application.ts new file mode 100644 index 0000000000000..d81aa9b3e76e2 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/test.application.ts @@ -0,0 +1,28 @@ +import { expect, haveResource } from '@aws-cdk/assert'; +import cdk = require('@aws-cdk/cdk'); +import { Test } from 'nodeunit'; +import codedeploy = require('../../lib'); + +export = { + "CodeDeploy Lambda Application": { + "can be created"(test: Test) { + const stack = new cdk.Stack(); + new codedeploy.LambdaApplication(stack, 'MyApp'); + expect(stack).to(haveResource('AWS::CodeDeploy::Application', { + ComputePlatform: 'Lambda' + })); + test.done(); + }, + "can be created with explicit name"(test: Test) { + const stack = new cdk.Stack(); + new codedeploy.LambdaApplication(stack, 'MyApp', { + applicationName: 'my-name' + }); + expect(stack).to(haveResource('AWS::CodeDeploy::Application', { + ApplicationName: 'my-name', + ComputePlatform: 'Lambda' + })); + test.done(); + }, + } +}; diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/test.deployment-group.ts b/packages/@aws-cdk/aws-codedeploy/test/lambda/test.deployment-group.ts new file mode 100644 index 0000000000000..19274ac6d4598 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/test.deployment-group.ts @@ -0,0 +1,569 @@ +import { expect, haveResource, haveResourceLike, ResourcePart } from '@aws-cdk/assert'; +import cloudwatch = require('@aws-cdk/aws-cloudwatch'); +import iam = require('@aws-cdk/aws-iam'); +import lambda = require('@aws-cdk/aws-lambda'); +import cdk = require('@aws-cdk/cdk'); +import { Test } from 'nodeunit'; +import codedeploy = require('../../lib'); +import { LambdaDeploymentConfig } from '../../lib'; + +function mockFunction(stack: cdk.Stack, id: string) { + return new lambda.Function(stack, id, { + code: lambda.Code.inline('mock'), + handler: 'index.handler', + runtime: lambda.Runtime.NodeJS810 + }); +} +function mockAlias(stack: cdk.Stack) { + return new lambda.Alias(stack, 'Alias', { + aliasName: 'my-alias', + version: new lambda.Version(stack, 'Version', { + lambda: mockFunction(stack, 'Function') + }) + }); +} + +export = { + "CodeDeploy Lambda DeploymentGroup": { + "can be created with default AllAtOnce IN_PLACE configuration"(test: Test) { + const stack = new cdk.Stack(); + const application = new codedeploy.LambdaApplication(stack, 'MyApp'); + const alias = mockAlias(stack); + new codedeploy.LambdaDeploymentGroup(stack, 'MyDG', { + application, + alias, + deploymentConfig: LambdaDeploymentConfig.AllAtOnce + }); + + expect(stack).to(haveResource('AWS::CodeDeploy::DeploymentGroup', { + ApplicationName: { + Ref: "MyApp3CE31C26" + }, + ServiceRoleArn: { + "Fn::GetAtt": [ + "MyDGServiceRole5E94FD88", + "Arn" + ] + }, + AutoRollbackConfiguration: { + Enabled: true, + Events: [ + "DEPLOYMENT_FAILURE" + ] + }, + DeploymentConfigName: "CodeDeployDefault.LambdaAllAtOnce", + DeploymentStyle: { + DeploymentOption: "WITH_TRAFFIC_CONTROL", + DeploymentType: "BLUE_GREEN" + } + })); + + expect(stack).to(haveResource('AWS::Lambda::Alias', { + Type: "AWS::Lambda::Alias", + Properties: { + FunctionName: { + Ref: "Function76856677" + }, + FunctionVersion: { + "Fn::GetAtt": [ + "Version6A868472", + "Version" + ] + }, + Name: "my-alias" + }, + UpdatePolicy: { + CodeDeployLambdaAliasUpdate: { + ApplicationName: { + Ref: "MyApp3CE31C26" + }, + DeploymentGroupName: { + Ref: "MyDGC350BD3F" + } + } + } + }, ResourcePart.CompleteDefinition)); + + expect(stack).to(haveResource('AWS::IAM::Role', { + AssumeRolePolicyDocument: { + Statement: [{ + Action: "sts:AssumeRole", + Effect: "Allow", + Principal: { + Service: "codedeploy.amazonaws.com" + } + }], + Version: "2012-10-17" + }, + ManagedPolicyArns: ['arn:aws:iam::aws:policy/service-role/AWSCodeDeployRoleForLambda'] + })); + + test.done(); + }, + "can be created with explicit name"(test: Test) { + const stack = new cdk.Stack(); + const application = new codedeploy.LambdaApplication(stack, 'MyApp'); + const alias = mockAlias(stack); + new codedeploy.LambdaDeploymentGroup(stack, 'MyDG', { + application, + alias, + deploymentConfig: LambdaDeploymentConfig.AllAtOnce, + deploymentGroupName: 'test' + }); + + expect(stack).to(haveResourceLike('AWS::CodeDeploy::DeploymentGroup', { + DeploymentGroupName: "test", + })); + + test.done(); + }, + "can be created with explicit role"(test: Test) { + const stack = new cdk.Stack(); + const application = new codedeploy.LambdaApplication(stack, 'MyApp'); + const alias = mockAlias(stack); + const serviceRole = new iam.Role(stack, 'MyRole', { + assumedBy: new iam.ServicePrincipal('not-codedeploy.amazonaws.com') + }); + + new codedeploy.LambdaDeploymentGroup(stack, 'MyDG', { + application, + alias, + deploymentConfig: LambdaDeploymentConfig.AllAtOnce, + role: serviceRole + }); + + expect(stack).to(haveResource('AWS::IAM::Role', { + AssumeRolePolicyDocument: { + Statement: [{ + Action: "sts:AssumeRole", + Effect: "Allow", + Principal: { + Service: "not-codedeploy.amazonaws.com" + } + }, { + Action: "sts:AssumeRole", + Effect: "Allow", + Principal: { + Service: "codedeploy.amazonaws.com" + } + }], + Version: "2012-10-17" + }, + ManagedPolicyArns: ['arn:aws:iam::aws:policy/service-role/AWSCodeDeployRoleForLambda'] + })); + + test.done(); + }, + "can configure blue/green traffic shifting"(test: Test) { + const stack = new cdk.Stack(); + const application = new codedeploy.LambdaApplication(stack, 'MyApp'); + const alias = mockAlias(stack); + new codedeploy.LambdaDeploymentGroup(stack, 'MyDG', { + application, + alias, + deploymentConfig: LambdaDeploymentConfig.Linear10PercentEvery1Minute + }); + + expect(stack).to(haveResource('AWS::CodeDeploy::DeploymentGroup', { + ApplicationName: { + Ref: "MyApp3CE31C26" + }, + ServiceRoleArn: { + "Fn::GetAtt": [ + "MyDGServiceRole5E94FD88", + "Arn" + ] + }, + AutoRollbackConfiguration: { + Enabled: true, + Events: [ + "DEPLOYMENT_FAILURE" + ] + }, + DeploymentConfigName: "CodeDeployDefault.LambdaLinear10PercentEvery1Minute", + DeploymentStyle: { + DeploymentOption: "WITH_TRAFFIC_CONTROL", + DeploymentType: "BLUE_GREEN" + } + })); + + test.done(); + }, + "can rollback on alarm"(test: Test) { + const stack = new cdk.Stack(); + const application = new codedeploy.LambdaApplication(stack, 'MyApp'); + const alias = mockAlias(stack); + new codedeploy.LambdaDeploymentGroup(stack, 'MyDG', { + application, + alias, + deploymentConfig: codedeploy.LambdaDeploymentConfig.AllAtOnce, + alarms: [new cloudwatch.Alarm(stack, 'Failures', { + metric: alias.metricErrors(), + comparisonOperator: cloudwatch.ComparisonOperator.GreaterThanThreshold, + threshold: 1, + evaluationPeriods: 1 + })] + }); + + expect(stack).to(haveResourceLike('AWS::CodeDeploy::DeploymentGroup', { + AlarmConfiguration: { + Alarms: [{ + Name: { + Ref: "Failures8A3E1A2F" + } + }], + Enabled: true + }, + AutoRollbackConfiguration: { + Enabled: true, + Events: [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM" + ] + }, + })); + + test.done(); + }, + "onPreHook throws error if pre-hook already defined"(test: Test) { + const stack = new cdk.Stack(); + const alias = mockAlias(stack); + const group = new codedeploy.LambdaDeploymentGroup(stack, 'MyDG', { + alias, + preHook: mockFunction(stack, 'PreHook'), + deploymentConfig: LambdaDeploymentConfig.AllAtOnce + }); + test.throws(() => group.onPreHook(mockFunction(stack, 'PreHook2'))); + test.done(); + }, + "onPostHook throws error if post-hook already defined"(test: Test) { + const stack = new cdk.Stack(); + const alias = mockAlias(stack); + const group = new codedeploy.LambdaDeploymentGroup(stack, 'MyDG', { + alias, + postHook: mockFunction(stack, 'PostHook'), + deploymentConfig: LambdaDeploymentConfig.AllAtOnce + }); + test.throws(() => group.onPostHook(mockFunction(stack, 'PostHook2'))); + test.done(); + }, + "can run pre hook lambda function before deployment"(test: Test) { + const stack = new cdk.Stack(); + const application = new codedeploy.LambdaApplication(stack, 'MyApp'); + const alias = mockAlias(stack); + new codedeploy.LambdaDeploymentGroup(stack, 'MyDG', { + application, + alias, + preHook: mockFunction(stack, 'PreHook'), + deploymentConfig: LambdaDeploymentConfig.AllAtOnce + }); + + expect(stack).to(haveResourceLike('AWS::Lambda::Alias', { + UpdatePolicy: { + CodeDeployLambdaAliasUpdate: { + ApplicationName: { + Ref: "MyApp3CE31C26" + }, + DeploymentGroupName: { + Ref: "MyDGC350BD3F" + }, + BeforeAllowTrafficHook: { + Ref: "PreHook8B53F672" + } + } + } + }, ResourcePart.CompleteDefinition)); + + expect(stack).to(haveResource('AWS::IAM::Policy', { + PolicyName: "MyDGServiceRoleDefaultPolicy65E8E1EA", + Roles: [{ + Ref: 'MyDGServiceRole5E94FD88' + }], + PolicyDocument: { + Statement: [{ + Action: 'lambda:InvokeFunction', + Resource: { + "Fn::GetAtt": [ + "PreHook8B53F672", + "Arn" + ] + }, + Effect: 'Allow' + }], + Version: "2012-10-17" + } + })); + + test.done(); + }, + "can add pre hook lambda function after creating the deployment group"(test: Test) { + const stack = new cdk.Stack(); + const application = new codedeploy.LambdaApplication(stack, 'MyApp'); + const alias = mockAlias(stack); + const group = new codedeploy.LambdaDeploymentGroup(stack, 'MyDG', { + application, + alias, + deploymentConfig: LambdaDeploymentConfig.AllAtOnce + }); + group.onPreHook(mockFunction(stack, 'PreHook')); + + expect(stack).to(haveResourceLike('AWS::Lambda::Alias', { + UpdatePolicy: { + CodeDeployLambdaAliasUpdate: { + ApplicationName: { + Ref: "MyApp3CE31C26" + }, + DeploymentGroupName: { + Ref: "MyDGC350BD3F" + }, + BeforeAllowTrafficHook: { + Ref: "PreHook8B53F672" + } + } + } + }, ResourcePart.CompleteDefinition)); + + expect(stack).to(haveResource('AWS::IAM::Policy', { + PolicyName: "MyDGServiceRoleDefaultPolicy65E8E1EA", + Roles: [{ + Ref: 'MyDGServiceRole5E94FD88' + }], + PolicyDocument: { + Statement: [{ + Action: 'lambda:InvokeFunction', + Resource: { + "Fn::GetAtt": [ + "PreHook8B53F672", + "Arn" + ] + }, + Effect: 'Allow' + }], + Version: "2012-10-17" + } + })); + + test.done(); + }, + "can run post hook lambda function before deployment"(test: Test) { + const stack = new cdk.Stack(); + const application = new codedeploy.LambdaApplication(stack, 'MyApp'); + const alias = mockAlias(stack); + new codedeploy.LambdaDeploymentGroup(stack, 'MyDG', { + application, + alias, + postHook: mockFunction(stack, 'PostHook'), + deploymentConfig: LambdaDeploymentConfig.AllAtOnce + }); + + expect(stack).to(haveResourceLike('AWS::Lambda::Alias', { + UpdatePolicy: { + CodeDeployLambdaAliasUpdate: { + ApplicationName: { + Ref: "MyApp3CE31C26" + }, + DeploymentGroupName: { + Ref: "MyDGC350BD3F" + }, + AfterAllowTrafficHook: { + Ref: 'PostHookF2E49B30' + } + } + } + }, ResourcePart.CompleteDefinition)); + + expect(stack).to(haveResource('AWS::IAM::Policy', { + PolicyName: "MyDGServiceRoleDefaultPolicy65E8E1EA", + Roles: [{ + Ref: 'MyDGServiceRole5E94FD88' + }], + PolicyDocument: { + Statement: [{ + Action: 'lambda:InvokeFunction', + Resource: { + "Fn::GetAtt": [ + "PostHookF2E49B30", + "Arn" + ] + }, + Effect: 'Allow' + }], + Version: "2012-10-17" + }, + })); + + test.done(); + }, + "can add post hook lambda function after creating the deployment group"(test: Test) { + const stack = new cdk.Stack(); + const application = new codedeploy.LambdaApplication(stack, 'MyApp'); + const alias = mockAlias(stack); + const group = new codedeploy.LambdaDeploymentGroup(stack, 'MyDG', { + application, + alias, + deploymentConfig: LambdaDeploymentConfig.AllAtOnce + }); + group.onPostHook(mockFunction(stack, 'PostHook')); + + expect(stack).to(haveResourceLike('AWS::Lambda::Alias', { + UpdatePolicy: { + CodeDeployLambdaAliasUpdate: { + ApplicationName: { + Ref: "MyApp3CE31C26" + }, + DeploymentGroupName: { + Ref: "MyDGC350BD3F" + }, + AfterAllowTrafficHook: { + Ref: 'PostHookF2E49B30' + } + } + } + }, ResourcePart.CompleteDefinition)); + + expect(stack).to(haveResource('AWS::IAM::Policy', { + PolicyName: "MyDGServiceRoleDefaultPolicy65E8E1EA", + Roles: [{ + Ref: 'MyDGServiceRole5E94FD88' + }], + PolicyDocument: { + Statement: [{ + Action: 'lambda:InvokeFunction', + Resource: { + "Fn::GetAtt": [ + "PostHookF2E49B30", + "Arn" + ] + }, + Effect: 'Allow' + }], + Version: "2012-10-17" + }, + })); + + test.done(); + }, + "can disable rollback when alarm polling fails"(test: Test) { + const stack = new cdk.Stack(); + const application = new codedeploy.LambdaApplication(stack, 'MyApp'); + const alias = mockAlias(stack); + new codedeploy.LambdaDeploymentGroup(stack, 'MyDG', { + application, + alias, + postHook: mockFunction(stack, 'PostHook'), + deploymentConfig: LambdaDeploymentConfig.AllAtOnce, + ignorePollAlarmsFailure: true, + alarms: [new cloudwatch.Alarm(stack, 'Failures', { + metric: alias.metricErrors(), + comparisonOperator: cloudwatch.ComparisonOperator.GreaterThanThreshold, + threshold: 1, + evaluationPeriods: 1 + })] + }); + + expect(stack).to(haveResourceLike('AWS::CodeDeploy::DeploymentGroup', { + AlarmConfiguration: { + Alarms: [{ + Name: { + Ref: "Failures8A3E1A2F" + } + }], + Enabled: true, + IgnorePollAlarmFailure: true + }, + })); + + test.done(); + }, + "can disable rollback when deployment fails"(test: Test) { + const stack = new cdk.Stack(); + const application = new codedeploy.LambdaApplication(stack, 'MyApp'); + const alias = mockAlias(stack); + new codedeploy.LambdaDeploymentGroup(stack, 'MyDG', { + application, + alias, + postHook: mockFunction(stack, 'PostHook'), + deploymentConfig: LambdaDeploymentConfig.AllAtOnce, + autoRollback: { + failedDeployment: false + } + }); + + expect(stack).to(haveResource('AWS::CodeDeploy::DeploymentGroup', { + ApplicationName: { + Ref: "MyApp3CE31C26" + }, + ServiceRoleArn: { + "Fn::GetAtt": [ + "MyDGServiceRole5E94FD88", + "Arn" + ] + }, + DeploymentConfigName: "CodeDeployDefault.LambdaAllAtOnce", + DeploymentStyle: { + DeploymentOption: "WITH_TRAFFIC_CONTROL", + DeploymentType: "BLUE_GREEN" + } + })); + + test.done(); + }, + "can enable rollback when deployment stops"(test: Test) { + const stack = new cdk.Stack(); + const application = new codedeploy.LambdaApplication(stack, 'MyApp'); + const alias = mockAlias(stack); + new codedeploy.LambdaDeploymentGroup(stack, 'MyDG', { + application, + alias, + postHook: mockFunction(stack, 'PostHook'), + deploymentConfig: LambdaDeploymentConfig.AllAtOnce, + autoRollback: { + stoppedDeployment: true + } + }); + + expect(stack).to(haveResourceLike('AWS::CodeDeploy::DeploymentGroup', { + AutoRollbackConfiguration: { + Enabled: true, + Events: [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + })); + + test.done(); + }, + "can disable rollback when alarm in failure state"(test: Test) { + const stack = new cdk.Stack(); + const application = new codedeploy.LambdaApplication(stack, 'MyApp'); + const alias = mockAlias(stack); + new codedeploy.LambdaDeploymentGroup(stack, 'MyDG', { + application, + alias, + postHook: mockFunction(stack, 'PostHook'), + deploymentConfig: LambdaDeploymentConfig.AllAtOnce, + autoRollback: { + deploymentInAlarm: false + }, + alarms: [new cloudwatch.Alarm(stack, 'Failures', { + metric: alias.metricErrors(), + comparisonOperator: cloudwatch.ComparisonOperator.GreaterThanThreshold, + threshold: 1, + evaluationPeriods: 1 + })] + }); + + expect(stack).to(haveResourceLike('AWS::CodeDeploy::DeploymentGroup', { + AutoRollbackConfiguration: { + Enabled: true, + Events: [ + "DEPLOYMENT_FAILURE", + ] + }, + })); + + test.done(); + }, + } +}; diff --git a/packages/@aws-cdk/aws-codedeploy/test/integ.deployment-group.expected.json b/packages/@aws-cdk/aws-codedeploy/test/server/integ.deployment-group.expected.json similarity index 100% rename from packages/@aws-cdk/aws-codedeploy/test/integ.deployment-group.expected.json rename to packages/@aws-cdk/aws-codedeploy/test/server/integ.deployment-group.expected.json diff --git a/packages/@aws-cdk/aws-codedeploy/test/integ.deployment-group.ts b/packages/@aws-cdk/aws-codedeploy/test/server/integ.deployment-group.ts similarity index 96% rename from packages/@aws-cdk/aws-codedeploy/test/integ.deployment-group.ts rename to packages/@aws-cdk/aws-codedeploy/test/server/integ.deployment-group.ts index 6e096629fada3..5005904f39493 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/integ.deployment-group.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/server/integ.deployment-group.ts @@ -3,7 +3,7 @@ import cloudwatch = require('@aws-cdk/aws-cloudwatch'); import ec2 = require('@aws-cdk/aws-ec2'); import lb = require('@aws-cdk/aws-elasticloadbalancing'); import cdk = require('@aws-cdk/cdk'); -import codedeploy = require('../lib'); +import codedeploy = require('../../lib'); const app = new cdk.App(); diff --git a/packages/@aws-cdk/aws-codedeploy/test/test.deployment-config.ts b/packages/@aws-cdk/aws-codedeploy/test/server/test.deployment-config.ts similarity index 98% rename from packages/@aws-cdk/aws-codedeploy/test/test.deployment-config.ts rename to packages/@aws-cdk/aws-codedeploy/test/server/test.deployment-config.ts index eea90cf28f523..b12087a930432 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/test.deployment-config.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/server/test.deployment-config.ts @@ -1,7 +1,7 @@ import { expect, haveResource } from '@aws-cdk/assert'; import cdk = require('@aws-cdk/cdk'); import { Test } from 'nodeunit'; -import codedeploy = require('../lib'); +import codedeploy = require('../../lib'); // tslint:disable:object-literal-key-quotes diff --git a/packages/@aws-cdk/aws-codedeploy/test/test.deployment-group.ts b/packages/@aws-cdk/aws-codedeploy/test/server/test.deployment-group.ts similarity index 99% rename from packages/@aws-cdk/aws-codedeploy/test/test.deployment-group.ts rename to packages/@aws-cdk/aws-codedeploy/test/server/test.deployment-group.ts index 4b470209cc461..a9631773b57b8 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/test.deployment-group.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/server/test.deployment-group.ts @@ -5,12 +5,12 @@ import ec2 = require('@aws-cdk/aws-ec2'); import lbv2 = require('@aws-cdk/aws-elasticloadbalancingv2'); import cdk = require('@aws-cdk/cdk'); import { Test } from 'nodeunit'; -import codedeploy = require('../lib'); +import codedeploy = require('../../lib'); // tslint:disable:object-literal-key-quotes export = { - 'CodeDeploy Deployment Group': { + 'CodeDeploy Server Deployment Group': { "can be created by explicitly passing an Application"(test: Test) { const stack = new cdk.Stack(); diff --git a/packages/@aws-cdk/aws-lambda/lib/alias.ts b/packages/@aws-cdk/aws-lambda/lib/alias.ts index 0f54a1feada37..0557401360c93 100644 --- a/packages/@aws-cdk/aws-lambda/lib/alias.ts +++ b/packages/@aws-cdk/aws-lambda/lib/alias.ts @@ -85,7 +85,7 @@ export class Alias extends FunctionBase { this.underlyingLambda = props.version.lambda; - const alias = new CfnAlias(this, 'Resource', { + new CfnAlias(this, 'Resource', { name: props.aliasName, description: props.description, functionName: this.underlyingLambda.functionName, @@ -95,8 +95,8 @@ export class Alias extends FunctionBase { // Not actually the name, but an ARN can be used in all places // where the name is expected, and an ARN can refer to an Alias. - this.functionName = alias.ref; - this.functionArn = alias.aliasArn; + this.functionName = `${props.version.lambda.functionArn}:${props.aliasName}`; + this.functionArn = `${props.version.lambda.functionArn}:${props.aliasName}`; } public export(): FunctionImportProps { From db752152d355ff5e43b0104ce06b02b4507ce603 Mon Sep 17 00:00:00 2001 From: Doug Date: Mon, 4 Feb 2019 15:18:57 -0800 Subject: [PATCH 32/38] chore(docs): move developer guide to docs.aws.amazon.com (#1470) --- docs/src/apples_example.rst | 445 -------------- docs/src/applets.rst | 86 --- docs/src/apps.rst | 84 --- docs/src/assets.rst | 29 - docs/src/aws-construct-lib.rst | 380 ------------ docs/src/cloudformation.rst | 202 ------- docs/src/concepts.rst | 49 -- docs/src/conf.py | 2 +- docs/src/constructs.rst | 203 ------- docs/src/context.rst | 130 ----- docs/src/ecs_example.rst | 200 ------- docs/src/environments.rst | 64 --- docs/src/examples.rst | 217 ------- docs/src/getting-started.rst | 183 ------ docs/src/index.rst | 100 +--- docs/src/logical-ids.rst | 137 ----- docs/src/passing-in-data.rst | 282 --------- docs/src/reference.rst.template | 1 - docs/src/region-note.rst | 16 - docs/src/stacks.rst | 53 -- docs/src/tools.rst | 431 -------------- docs/src/tutorial.rst | 988 -------------------------------- docs/src/writing-constructs.rst | 236 -------- 23 files changed, 7 insertions(+), 4511 deletions(-) delete mode 100644 docs/src/apples_example.rst delete mode 100644 docs/src/applets.rst delete mode 100644 docs/src/apps.rst delete mode 100644 docs/src/assets.rst delete mode 100644 docs/src/aws-construct-lib.rst delete mode 100644 docs/src/cloudformation.rst delete mode 100644 docs/src/concepts.rst delete mode 100644 docs/src/constructs.rst delete mode 100644 docs/src/context.rst delete mode 100644 docs/src/ecs_example.rst delete mode 100644 docs/src/environments.rst delete mode 100644 docs/src/examples.rst delete mode 100644 docs/src/getting-started.rst delete mode 100644 docs/src/logical-ids.rst delete mode 100644 docs/src/passing-in-data.rst delete mode 100644 docs/src/region-note.rst delete mode 100644 docs/src/stacks.rst delete mode 100644 docs/src/tools.rst delete mode 100644 docs/src/tutorial.rst delete mode 100644 docs/src/writing-constructs.rst diff --git a/docs/src/apples_example.rst b/docs/src/apples_example.rst deleted file mode 100644 index fb78ea2ca987d..0000000000000 --- a/docs/src/apples_example.rst +++ /dev/null @@ -1,445 +0,0 @@ -.. Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 - International License (the "License"). You may not use this file except in compliance with the - License. A copy of the License is located at http://creativecommons.org/licenses/by-nc-sa/4.0/. - - This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - either express or implied. See the License for the specific language governing permissions and - limitations under the License. - -.. _apples_example: - -############## -Apples Example -############## - -This example walks you through creating the resources for a simple widget dispensing service. -It includes: - -* An |LAMlong| function -* An |ABP| API to call our |LAM| function -* An |S3| bucket that contains our |LAM| function code - -.. _overview: - -Overview -======== - -This tutorial contains the following steps. - -1. Create a |cdk| app - -2. Create a |LAM| function that gets a list of widgets with: GET / - -3. Create the service that calls the |LAM| function - -4. Add the service to the |cdk| app - -5. Test the app - -6. Add |LAM| functions to: - - * create an widget based with: POST /{name} - * get an widget by name with: GET /{name} - * delete an widget by name with: DELETE /{name} - -.. _create_app: - -Step 1: Create a |cdk| App -========================== - -Let's create the TypeScript app **MyWidgetService** in in the current folder. - -.. code-block:: sh - - mkdir MyWidgetService - cd MyWidgetService - cdk init --language typescript - -This creates *my_widget_service.ts* in the *bin* directory -and *my_widget_service-stack.ts* in the *lib*. - -Make sure it builds and creates an empty stack. - -.. code-block:: sh - - npm run build - cdk synth - -You should see a stack like the following, -where CDK-VERSION is the version of the CDK. - -.. code-block:: sh - - Resources: - CDKMetadata: - Type: AWS::CDK::Metadata - Properties: - Modules: "@aws-cdk/cdk=CDK-VERSION,@aws-cdk/cx-api=CDK-VERSION,my_widget_service=0.1.0" - -.. _create_lambda_functions: - -Step 2: Create a |LAM| Function to List all Widgets -=================================================== - -The next step is to create a |LAM| function to list all of the widgets in our -|S3| bucket. - -Create the directory *resources* at the same level as the *bin* directory. - -.. code-block:: sh - - mkdir resources - -Create the following Javascript file, *widgets.js*, -in the *resources* directory. - -.. code-block:: js - - const AWS = require('aws-sdk'); - const S3 = new AWS.S3(); - - const bucketName = process.env.BUCKET; - - exports.main = async function(event, context) { - try { - var method = event.httpMethod; - - if (method === "GET") { - if (event.path === "/") { - const data = await S3.listObjectsV2({ Bucket: bucketName }).promise(); - var body = { - widgets: data.Contents.map(function(e) { return e.Key }) - }; - return { - statusCode: 200, - headers: {}, - body: JSON.stringify(body) - }; - } - } - - // We only accept GET for now - return { - statusCode: 400, - headers: {}, - body: "We only accept GET /" - }; - } catch(error) { - var body = error.stack || JSON.stringify(error, null, 2); - return { - statusCode: 400, - headers: {}, - body: JSON.stringify(body) - } - } - } - -Save it and make sure it builds and creates an empty stack. -Note that since we haven't wired the function to our app, -the Lambda file does not appear in the output. - -.. code-block:: sh - - npm run build - cdk synth - -.. _create_widgets_service: - -Step 3: Create Widgets Service -============================== - -Add the |ABP|, |LAM|, and |S3| packages to our app. - -.. code-block:: sh - - npm install @aws-cdk/aws-apigateway @aws-cdk/aws-lambda @aws-cdk/aws-s3 - -Create the following Typescript file, *widget_service.ts*, -in the *lib* directory. - -.. code-block:: ts - - import cdk = require('@aws-cdk/cdk'); - import apigateway = require('@aws-cdk/aws-apigateway'); - import lambda = require('@aws-cdk/aws-lambda'); - import s3 = require('@aws-cdk/aws-s3'); - - export class WidgetService extends cdk.Construct { - constructor(parent: cdk.Construct, name: string) { - super(parent, name); - - // Use S3 bucket to store our widgets - const bucket = new s3.Bucket(this, 'WidgetStore'); - - // Create a handler that calls the function main - // in the source file widgets(.js) in the resources directory - // to handle requests through API Gateway - const handler = new lambda.Function(this, 'WidgetHandler', { - runtime: lambda.Runtime.NodeJS810, - code: lambda.Code.directory('resources'), - handler: 'widgets.main', - environment: { - BUCKET: bucket.bucketName // So runtime has the bucket name - } - }); - - bucket.grantReadWrite(handler.role); - - // Create an API Gateway REST API - const api = new apigateway.RestApi(this, 'widgets-api', { - restApiName: 'Widget Service', - description: 'This service serves widgets.' - }); - - // Pass the request to the handler - const getWidgetsIntegration = new apigateway.LambdaIntegration(handler); - - // Use the getWidgetsIntegration when there is a GET request - api.root.addMethod('GET', getWidgetsIntegration); // GET / - } - } - -Save it and make sure it builds and creates a (still empty) stack. - -.. code-block:: sh - - npm run build - cdk synth - -.. _add_service: - -Step 4: Add the Service to the App -================================== - -To add the service to our app, -we need to first modify *my_widget_service-stack.ts*. -Add the following line of code after the existing **import** statement. - -.. code-block:: ts - - import widget_service = require('../lib/widget_service'); - -Replace the comment in the constructor with the following line of code. - -.. code-block:: ts - - new widget_service.WidgetService(this, 'Widgets'); - -Make sure it builds and creates a stack -(we don't show the stack as it's over 250 lines). - -.. code-block:: sh - - npm run build - cdk synth - -.. _deploy_and_test: - -Step 5: Deploy and Test the App -=============================== - -Before you can deploy your first |cdk| app, -you must bootstrap your deployment, -which creates some AWS infracture that the |cdk| -needs. -See the **bootstrap** section of the :doc:`tools` topic for details -(you'll get a warning and nothing changes if you may have done this already). - -.. code-block:: sh - - cdk bootstrap - -Run the following command to deploy your app. - -.. code-block:: sh - - cdk deploy - -If the deployment is successfull, -save the URL for your server, which appears in one of the last lines in the window, -where GUID is an alpha-numeric GUID and REGION is your region. - -.. code-block:: sh - - https://GUID.execute-REGION.amazonaws.com/prod/ - -You can test your app by getting the list of widgets (currently empty) by navigating to this URL in a -browser or use the following **curl** command. - -.. code-block:: sh - - curl -X GET 'https://GUID.execute-REGION.amazonaws.com/prod' - -You can also open the |console|, -navigate to the |ABP| service, -find **Widget Service** in the list. -Select **GET** and **Test** to test the function. -Since we haven't stored any widgets yet, the output should be similar to the following -(there may be some slight differences in whitespace and quotation marks). - -.. code-block:: sh - - { "widgets": [] } - -.. _adding_functions: - -Step 6: Add the Individual Widget Functions -=========================================== - -The next step is to create |LAM| functions to create, show, and delete -individual widgets. -Replace the existing **exports.main** function in *widgets.js* with the following code. - -.. code-block:: js - - exports.main = async function(event, context) { - try { - var method = event.httpMethod; - // Get name, if present - var widgetName = event.path.startsWith('/') ? event.path.substring(1) : event.path; - - if (method === "GET") { - // GET / to get the names of all widgets - if (event.path === "/") { - const data = await S3.listObjectsV2({ Bucket: bucketName }).promise(); - var body = { - widgets: data.Contents.map(function(e) { return e.Key }) - }; - return { - statusCode: 200, - headers: {}, - body: JSON.stringify(body) - }; - } - - if (widgetName) { - // GET /name to get info on widget name - const data = await S3.getObject({ Bucket: bucketName, Key: widgetName}).promise(); - var body = data.Body.toString('utf-8'); - - return { - statusCode: 200, - headers: {}, - body: JSON.stringify(body) - }; - } - } - - if (method === "POST") { - // POST /name - // Return error if we do not have a name - if (!widgetName) { - return { - statusCode: 400, - headers: {}, - body: "Widget name missing" - }; - } - - // Create some dummy data to populate object - const now = new Date(); - var data = widgetName + " created: " + now; - - var base64data = new Buffer(data, 'binary'); - - await S3.putObject({ - Bucket: bucketName, - Key: widgetName, - Body: base64data, - ContentType: 'application/json' - }).promise(); - - return { - statusCode: 200, - headers: {}, - body: JSON.stringify(event.widgets) - }; - } - - if (method === "DELETE") { - // DELETE /name - // Return an error if we do not have a name - if (!widgetName) { - return { - statusCode: 400, - headers: {}, - body: "Widget name missing" - }; - } - - await S3.deleteObject({ - Bucket: bucketName, Key: widgetName - }).promise(); - - return { - statusCode: 200, - headers: {}, - body: "Successfully deleted widget " + widgetName - }; - } - - // We got something besides a GET, POST, or DELETE - return { - statusCode: 400, - headers: {}, - body: "We only accept GET, POST, and DELETE, not " + method - }; - } catch(error) { - var body = error.stack || JSON.stringify(error, null, 2); - return { - statusCode: 400, - headers: {}, - body: body - } - } - } - -Wire these functions up to our |ABP| code in *widget_service.ts* -by adding the following code at the end of the constructor. - -.. code-block:: ts - - const widget = api.root.addResource('{name}'); - - // Add new widget to bucket with: POST /{name} - const postWidgetIntegration = new apigateway.LambdaIntegration(handler); - - // Get a specific widget from bucket with: GET /{name} - const getWidgetIntegration = new apigateway.LambdaIntegration(handler); - - // Remove a specific widget from the bucket with: DELETE /{name} - const deleteWidgetIntegration = new apigateway.LambdaIntegration(handler); - - widget.addMethod('POST', postWidgetIntegration); // POST /{name} - widget.addMethod('GET', getWidgetIntegration); // GET /{name} - widget.addMethod('DELETE', deleteWidgetIntegration); // DELETE /{name} - -Save, build, and deploy the app. - -.. code-block:: sh - - npm run build - cdk deploy - -Now we should be able to store, show, or delete an individual widget. -Use the following **curl** commands to list the widgets, -create the widget *dummy*, -list all of the widgets, -show the contents of *dummy* (it should show today's date), -and delete *dummy*, -and again show the list of widgets. - -.. code-block:: sh - - curl -X GET 'https://GUID.execute-REGION.amazonaws.com/prod' - curl -X POST 'https://GUID.execute-REGION.amazonaws.com/prod/dummy' - curl -X GET 'https://GUID.execute-REGION.amazonaws.com/prod' - curl -X GET 'https://GUID.execute-REGION.amazonaws.com/prod/dummy' - curl -X DELETE 'https://GUID.execute-REGION.amazonaws.com/prod/dummy' - curl -X GET 'https://GUID.execute-REGION.amazonaws.com/prod' - -You can also use the |ABP| console to test these functions. -You'll have to set the **name** entry to the name of an widget, -such as **dummy**. diff --git a/docs/src/applets.rst b/docs/src/applets.rst deleted file mode 100644 index 5198aa97de479..0000000000000 --- a/docs/src/applets.rst +++ /dev/null @@ -1,86 +0,0 @@ -.. Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 - International License (the "License"). You may not use this file except in compliance with the - License. A copy of the License is located at http://creativecommons.org/licenses/by-nc-sa/4.0/. - - This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - either express or implied. See the License for the specific language governing permissions and - limitations under the License. - -.. _applets: - -####### -Applets -####### - -.. note:: Currently the |cdk| only supports applets published as JavaScript modules. - -Applets are files in the YAML format that instantiate constructs directly, -without writing any code. The structure of an applet file looks like this: - -.. code:: js - - applets: - Applet1: - type: MODULE[:CLASS] - properties: - property1: value1 - property2: value2 - ... - Applet2: - type: MODULE[:CLASS] - properties: - ... - -Every applet will be synthesized to its own stack, named after the key used -in the applet definition. - -Specifying the applet to load -============================= - -An applet ``type`` specification looks like this: - -.. code:: js - - applet: MODULE[:CLASS] - -**MODULE** can be used to indicate: - -* A local file, such as ``./my-module`` (expects ``my-module.js`` in the same - directory). -* A local module such as ``my-dependency`` (expects an NPM package at - ``node_modules/my-dependency``). -* A global module, such as ``@aws-cdk/aws-s3`` (expects the package to have been - globally installed using NPM). -* An NPM package, such as ``npm://some-package@1.2.3`` (the version specifier - may be omitted to refer to the latest version of the package). - -**CLASS** should reference the name of a class exported by the indicated module. -If the class name is omitted, ``Applet`` is used as the default class name. - -Properties -========== - -Pass properties to the applet by specifying them in the ``properties`` object. -The properties will be passed to the instantiation of the class in the ``type`` -parameter. - -Running -======= - -To run an applet, pass its YAML file directly as the ``--app`` argument to a -``cdk`` invocation: - -.. code-block:: sh - - cdk --app ./my-applet.yaml deploy - -To avoid needing to specify ``--app`` for every invocation, make a ``cdk.json`` -file and add in the application in the config as usual: - -.. code-block:: json - - { - "app": "./my-applet.yaml" - } diff --git a/docs/src/apps.rst b/docs/src/apps.rst deleted file mode 100644 index 2362aef78319e..0000000000000 --- a/docs/src/apps.rst +++ /dev/null @@ -1,84 +0,0 @@ -.. Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 - International License (the "License"). You may not use this file except in compliance with the - License. A copy of the License is located at http://creativecommons.org/licenses/by-nc-sa/4.0/. - - This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - either express or implied. See the License for the specific language governing permissions and - limitations under the License. - -.. _apps: - -#### -Apps -#### - -The main artifact of an |cdk| program is called a *CDK App*. -This is an executable program that can be used to synthesize deployment artifacts -that can be deployed by supporting tools like the |toolkit|, -which are described in :doc:`tools`. - -Tools interact with apps through the program's **argv**/**stdout** interface, -which can be easily implemented using the **App** class, -as shown in the following example. - -.. code-block:: js - - import { App } from '@aws-cdk/cdk' - - const app = new App(); // input: ARGV - - // - - app.run(); - -A |app-construct| is a collection of |stack| objects, as shown in the following -example. - -.. code-block:: js - - import { App } from '@aws-cdk/cdk' - import { MyStack } from './my-stack' - - const app = new App(); - - const dev = new MyStack(app, { name: 'Dev', region: 'us-west-2', dev: true }) - const preProd = new MyStack(app, { name: 'PreProd', region: 'us-west-2', preProd: true }) - const prod = [ - new MyStack(app, { name: 'NAEast', region: 'us-east-1' }), - new MyStack(app, { name: 'NAWest', region: 'us-west-2' }), - new MyStack(app, { name: 'EU', region: 'eu-west-1', encryptedStorage: true }) - ] - - new DeploymentPipeline(app, { - region: 'us-east-1', - strategy: DeploymentStrategy.Waved, - preProdStages: [ preProd ], - prodStages: prod - }); - - app.run(); - -Use the |toolkit| to list the stacks in this executable, -as shown in the following example. - -.. code-block:: sh - - cdk list - [ - { name: "Dev", region: "us-west-2" } - { name: "PreProd", region: "us-west-2" }, - { name: "NAEast", region: "us-east-1" }, - { name: "NAWest", region: "us-west-2" }, - { name: "EU", region: "eu-west-1" }, - { name: "DeploymentPipeline", region: 'us-east-1' } - ] - -Or deploy one of the stacks, -as shown in the following example. - -.. code-block:: sh - - cdk deploy Dev - ... diff --git a/docs/src/assets.rst b/docs/src/assets.rst deleted file mode 100644 index 4bcc0fb31f9ee..0000000000000 --- a/docs/src/assets.rst +++ /dev/null @@ -1,29 +0,0 @@ -.. Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 - International License (the "License"). You may not use this file except in compliance with the - License. A copy of the License is located at http://creativecommons.org/licenses/by-nc-sa/4.0/. - - This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - either express or implied. See the License for the specific language governing permissions and - limitations under the License. - -.. _assets: - -###### -Assets -###### - -Assets are local files, directories or docker images which can be bundled into -CDK constructs and apps. A common example is a directory which contains the -handler code for an AWS Lambda function, but assets can represent any artifact -that is needed for the app’s operation. - -When deploying an AWS CDK app that includes constructs with assets, the toolkit -will first prepare and publish them to S3 or ECR, and only then deploy the stacks. -The locations of the published assets will be passed in as CloudFormation Parameters -to the relevant stacks. - -See :py:doc:`Assets ` for documentation about file assets -and :py:doc:`Docker Assets ` for documentation about -Docker image assets. diff --git a/docs/src/aws-construct-lib.rst b/docs/src/aws-construct-lib.rst deleted file mode 100644 index 84c6b4ceb4ca8..0000000000000 --- a/docs/src/aws-construct-lib.rst +++ /dev/null @@ -1,380 +0,0 @@ -.. Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 - International License (the "License"). You may not use this file except in compliance with the - License. A copy of the License is located at http://creativecommons.org/licenses/by-nc-sa/4.0/. - - This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - either express or implied. See the License for the specific language governing permissions and - limitations under the License. - -.. _aws_construct_lib: - -##################### -AWS Construct Library -##################### - -The AWS Construct Library is a set of modules which expose a rich API for -defining AWS resources in CDK apps. The AWS Construct Library is organized to -modules based on the AWS service the resource belongs to. For example, the -:py:mod:`@aws-cdk/aws-ec2` module includes the `@aws-cdk/aws-ec2.VpcNetwork` -construct which makes it easy to define an `Amazon VPC -`_ in your CDK app. - -The AWS Construct Library includes many common patterns and capabilities which -are designed to allow developers to focus on their application-specific -architectures and reduces the boilerplate and glue logic needed when working -with AWS. - -.. _least_privilege: - -Least-Privilege IAM Policies -============================ - -IAM policies are automatically defined based on intent. For example, when -subscribing an AWS SNS :py:class:`Topic <@aws-cdk/aws-sns.Topic>` to a AWS Lambda -:py:class:`Function <@aws-cdk/aws-lambda.Function>`, the function's IAM permission -policy will automatically be modified to allow the specific topic to invoke the -function. - -Furthermore, most AWS Constructs expose ``grant*`` methods which allow -intent-based permission definitions. For example, the AWS S3 :py:class:`Bucket <@aws-cdk/aws-s3.Bucket>` -construct has a :py:meth:`grantRead(principal) <@aws-cdk/aws-s3.IBucket.grantRead>` -method which accepts an AWS IAM :py:class:`Principal <@aws-cdk/aws-iam.IPrincipal>` -such as a :py:class:`User <@aws-cdk/aws-iam.User>` or a :py:class:`Role <@aws-cdk/aws-iam.Role>`, -and will modify their policy to allow the principal to read objects from the bucket. - -.. _event_driven_apis: - -Event-Driven APIs -================= - -Many of the AWS constructs include ``on*`` methods which can be used to react -to events emitted by the construct. For example, the AWS CodeCommit -:py:mod:`Repository <@aws-cdk/aws-codecommit.Repository>` construct has an -:py:meth:`onCommit <@aws-cdk/aws-codecommit.IRepository.onCommit>` method. - -AWS Constructs that can be used as targets for various event providers implement -interfaces such as :py:mod:`IEventRuleTarget <@aws-cdk/aws-events.IEventRuleTarget>` -(for AWS CloudWatch Event Rule target), -:py:mod:`IAlarmAction <@aws-cdk/aws-cloudwatch.IAlarmAction>` -(for AWS CloudWatch Alarm actions), etc. - -For more information see the :doc:`refs/_aws-cdk_aws-cloudwatch` and :doc:`refs/_aws-cdk_aws-events` -documentation. - -.. _security_groups: - -Security Groups -=============== - -EC2 network entities such as the :py:mod:`Elastic Load Balancer <@aws-cdk/aws-ec2.ElasticLoadBalancer` -and :py:mod:`AutoScalingGroup <@aws-cdk/aws-ec2.AutoScalingGroup>` instances can connect to each other -based on definitions of security groups. - -The AWS CDK provides a rich API for defining security group connections. For more information, -see **Allowing Connections** in the :doc:`@aws-cdk/aws-ec2 ` documentation. - -.. _metrics: - -Metrics -======= - -Many AWS resources emit AWS CloudWatch metrics as part of their normal operation. Metrics can -be used to setup :py:mod:`Alarms <@aws-cdk/aws-cloudwatch.Alarm>` or included in :py:mod:`Dashboards <@aws-cdk/aws-cloudwatch.Dashboard>`. - -:py:mod:`Metric <@aws-cdk/aws-cloudwatch.Metric>` objects for AWS Constructs can be obtained -via ``metricXxx()`` methods. For example, the :py:meth:`metricDuration() <@aws-cdk/aws-lambda.IFunction.metricDuration>` -method reports the execution time of an AWS Lambda function. - -For more information see the :doc:`refs/_aws-cdk_aws-cloudwatch` documentation. - -.. _import: - -Imports -======= - -If you need to reference a resource which is defined outside of your CDK app (e.g. a bucket, a VPC, etc), -you can use the ``Xxxx.import(...)`` static methods which are available on AWS Constructs. - -For example, the :py:meth:`Bucket.import() <@aws-cdk/aws-s3.Bucket.import>` method can be used to obtain -an :py:mod:`IBucket <@aws-cdk/aws-s3.IBucket>` object which can be used in most places where -a bucket is required. This patterns allows treating resources defined outside your app as if they -were part of your app. - - - -.. _cloudformation_layer: - -Access the AWS CloudFormation Layer -=================================== - -This topic discusses ways to directly modify the underlying CloudFormation -resources at the AWS Construct Library. We also call this technique an "escape -hatch", as it allows users to "escape" from the abstraction boundary defined by -the AWS Construct and patch the underlying resources. - -.. important:: - - **We do not recommend this method, as it breaks the abstraction layer and - might produce unexpected results**. - - Furthermore, the internal implementation of an AWS construct is not part of - the API compatibility guarantees that we can make. This means that updates to - the construct library may break your code without a major version bump. - -AWS constructs, such as :py:class:`Topic <@aws-cdk/aws-sns.Topic>`, encapsulate -one or more AWS CloudFormation resources behind their APIs. These resources are -also represented as ``CfnXxx`` constructs in each -library. For example, the :py:class:`@aws-cdk/aws-s3.Bucket` construct -encapsulates the :py:class:`@aws-cdk/aws-s3.CfnBucket`. When -a stack that includes an AWS construct is synthesized, the CloudFormation -definition of the underlying resources are included in the resulting template. - -Eventually, the APIs provided by AWS constructs are expected to support all the -services and capabilities offered by AWS, but we are aware that the library -still has many gaps both at the service level (some services don't have any -constructs yet) and at the resource level (an AWS construct exists, but some -features are missing). - -.. note:: - - If you encounter a missing capability in the AWS Construct Library, whether - it is an entire library, a specific resource or a feature, - `raise an issue `_ on GitHub, - and letting us know. - -This topic covers the following use cases: - -- How to access the low-level CloudFormation resources encapsulated by an AWS construct -- How to specify resource options such as metadata, dependencies on resources -- How to add overrides to a CloudFormation resource and property definitions -- How to directly define low-level CloudFormation resources without an AWS construct - -You can also find more information on how to work directly with the AWS -CloudFormation layer under :py:doc:`cloudformation`. - -Accessing Low-level Resources ------------------------------ - -You can use :py:meth:`construct.findChild(id) <@aws-cdk/cdk.Construct.findChild>` -to access any child of this construct by its construct ID. By convention, the "main" -resource of any AWS Construct is called ``"Resource"``. - -The following example shows how to access the underlying S3 bucket resource -given an :py:class:`s3.Bucket <@aws-cdk/s3.Bucket>` construct: - -.. code-block:: ts - - // let's create an AWS bucket construct - const bucket = new s3.Bucket(this, 'MyBucket'); - - // we use our knowledge that the main construct is called "Resource" and - // that it's actual type is s3.CfnBucket; const - const bucketResource = bucket.findChild('Resource') as s3.CfnBucket; - -At this point, ``bucketResource`` represents the low-level CloudFormation resource of type -:py:class:`s3.CfnBucket <@aws-cdk/aws-s3.CfnBucket>` -encapsulated by our bucket. - -:py:meth:`construct.findChild(id) <@aws-cdk/cdk.Construct.findChild>` will fail -if the child could not be located, which means that if the underlying |l2| changes -the IDs or structure for some reason, synthesis fails. - -It is also possible to use :py:meth:`construct.children <@aws-cdk/cdk.Construct.children>` for more -advanced queries. For example, we can look for a child that has a certain CloudFormation resource -type: - -.. code-block:: ts - - const bucketResource = - bucket.children.find(c => (c as cdk.Resource).resourceType === 'AWS::S3::Bucket') - as s3.CfnBucket; - -From that point, users are interacting with CloudFormation resource classes -(which extend :py:class:`cdk.Resource <@aws-cdk/cdk.Resource>`), so we will look -into how to use their APIs in order to modify the behavior of the AWS construct -at hand. - -Resource Options ----------------- - -:py:class:`cdk.Resource <@aws-cdk/cdk.Resource>` has a few facilities for -setting resource options such as ``Metadata``, ``DependsOn``, etc. - -For example, this code: - -.. code-block:: ts - - const bucketResource = bucket.findChild('Resource') as s3.CfnBucket; - - bucketResource.options.metadata = { MetadataKey: 'MetadataValue' }; - bucketResource.options.updatePolicy = { - autoScalingRollingUpdate: { - pauseTime: '390' - } - }; - - bucketResource.addDependency(otherBucket.findChild('Resource') as cdk.Resource); - -Synthesizes the following template: - -.. code-block:: json - - { - "Type": "AWS::S3::Bucket", - "DependsOn": [ "Other34654A52" ], - "UpdatePolicy": { - "AutoScalingRollingUpdate": { - "PauseTime": "390" - } - }, - "Metadata": { - "MetadataKey": "MetadataValue" - } - } - -Overriding Resource Properties ------------------------------- - -Each low-level resource in the CDK has a strongly-typed property called -``propertyOverrides``. It allows users to apply overrides that adhere to the -CloudFormation schema of the resource, and use code-completion and -type-checking. - -You will normally use this mechanism when a certain feature is available at the -CloudFormation layer but is not exposed by the AWS Construct. - -The following example sets a bucket's analytics configuration: - -.. code-block:: ts - - bucketResource.propertyOverrides.analyticsConfigurations = [ - { - id: 'config1', - storageClassAnalysis: { - dataExport: { - outputSchemaVersion: '1', - destination: { - format: 'html', - bucketArn: otherBucket.bucketArn // use tokens freely - } - } - } - } - ]; - -Raw Overrides -------------- - -In cases the strongly-typed overrides are not sufficient, or, for example, if -the schema defined in CloudFormation is not up-to-date, the method -:py:meth:`cdk.Resource.addOverride(path, value) <@aws-cdk/cdk.Resource.addOverride>` -can be used to define an override that will by applied to the resource -definition during synthesis. - -For example: - -.. code-block:: ts - - // define an override at the resource definition root, you can even modify the "Type" - // of the resource if needed. - bucketResource.addOverride('Type', 'AWS::S3::SpecialBucket'); - - // define an override for a property (both are equivalent operations): - bucketResource.addPropertyOverride('VersioningConfiguration.Status', 'NewStatus'); - bucketResource.addOverride('Properties.VersioningConfiguration.Status', 'NewStatus'); - - // use dot-notation to define overrides in complex structures which will be merged - // with the values set by the higher-level construct - bucketResource.addPropertyOverride('LoggingConfiguration.DestinationBucketName', otherBucket.bucketName); - - // it is also possible to assign a `null` value - bucketResource.addPropertyOverride('Foo.Bar', null); - -Synthesizes to: - -.. code-block:: json - - { - "Type": "AWS::S3::SpecialBucket", - "Properties": { - "Foo": { - "Bar": null - }, - "VersioningConfiguration": { - "Status": "NewStatus" - }, - "LoggingConfiguration": { - "DestinationBucketName": { - "Ref": "Other34654A52" - } - } - } - } - -Use ``undefined``, :py:meth:`cdk.Resource.addDeletionOverride <@aws-cdk/cdk.Resource.addDeletionOverride>` -or :py:meth:`cdk.Resource.addPropertyDeletionOverride <@aws-cdk/cdk.Resource.addPropertyDeletionOverride>` -to delete values: - -.. code-block:: ts - - const bucket = new s3.Bucket(this, 'MyBucket', { - versioned: true, - encryption: s3.BucketEncryption.KmsManaged - }); - - const bucketResource = bucket.findChild('Resource') as s3.CfnBucket; - bucketResource.addPropertyOverride('BucketEncryption.ServerSideEncryptionConfiguration.0.EncryptEverythingAndAlways', true); - bucketResource.addPropertyDeletionOverride('BucketEncryption.ServerSideEncryptionConfiguration.0.ServerSideEncryptionByDefault'); - -Synthesizes to: - -.. code-block:: json - - { - "MyBucketF68F3FF0": { - "Type": "AWS::S3::Bucket", - "Properties": { - "BucketEncryption": { - "ServerSideEncryptionConfiguration": [ - { - "EncryptEverythingAndAlways": true - } - ] - }, - "VersioningConfiguration": { - "Status": "Enabled" - } - } - } - } - -Directly Defining CloudFormation Resources -------------------------------------------- - -It is also possible to explicitly define CloudFormation resources in your stack. -To that end, instantiate one of the ``CfnXxx`` constructs of the dedicated library. - -.. code-block:: ts - - new s3.CfnBucket(this, 'MyBucket', { - analyticsConfigurations: [ - // ... - ] - }); - -In the rare case where you want to define a resource that doesn't have a -corresponding ``CfnXxx`` class (such as a new resource that was not yet -published in the CloudFormation resource specification), you can instantiate the -:py:class:`cdk.Resource <@aws-cdk/cdk.Resource>` object: - -.. code-block:: ts - - new cdk.Resource(this, 'MyBucket', { - type: 'AWS::S3::Bucket', - properties: { - AnalyticsConfiguration: [ /* ... */ ] // note the PascalCase here - } - }); - diff --git a/docs/src/cloudformation.rst b/docs/src/cloudformation.rst deleted file mode 100644 index 688adc4e0ed64..0000000000000 --- a/docs/src/cloudformation.rst +++ /dev/null @@ -1,202 +0,0 @@ -.. Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 - International License (the "License"). You may not use this file except in compliance with the - License. A copy of the License is located at http://creativecommons.org/licenses/by-nc-sa/4.0/. - - This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - either express or implied. See the License for the specific language governing permissions and - limitations under the License. - -.. _cloudformation: - -########################## -AWS CloudFormation Library -########################## - -The :doc:`AWS Construct Library ` includes constructs with rich APIs -for defining AWS infrastructure. For example, the -:py:class:`@aws-cdk/aws-s3.Bucket` construct can be used to define S3 Buckets, -the :py:class:`@aws-cdk/aws-sns.Topic` construct can be used to define SNS -Topics, etc. - -Under the hood, these constructs are implemented using CloudFormation resources, -which are available under the **CfnXxx** classes in each library. For -example, the :py:class:`@aws-cdk/aws-s3.Bucket` construct uses the -:py:class:`@aws-cdk/aws-s3.CfnBucket` resource (as well as -other resources, depending on what bucket APIs are used). - -.. important:: - - Generally, when building CDK apps, you shouldn't need to interact with - CloudFormation directly. However, there might be advanced use cases and - migration scenarios where this might be required. We are also aware that - there might be gaps in capabilities in the AWS Construct Library over time. - -Resources ---------- - -CloudFormation resource classes are automatically generated from the `AWS -CloudFormation Resource Specification -`_ -and available under the **CfnXxx** classes of each AWS library. Their -API matches 1:1 with how you would use these resources in CloudFormation. - -When defining CloudFormation resource, the **props** argument of the class -initializer will match 1:1 to the resource's properties in CloudFormation. - -For example, to define an -`AWS::SQS::Queue `_ -resource encrypted with an AWS managed key you can directly specify the -`KmsMasterKeyId `_ -property. - -.. code-block:: ts - - import sqs = require('@aws-cdk/aws-sqs'); - - new sqs.CfnQueue(this, 'MyQueueResource', { - kmsMasterKeyId: 'alias/aws/sqs' - }); - -For reference, if you use the :py:class:`@aws-cdk/aws-sqs.Queue` construct, you -can define managed queue encryption as follows: - -.. code-block:: js - - import sqs = require('@aws-cdk/aws-sqs'); - - new sqs.Queue(this, 'MyQueue', { - encryption: sqs.QueueEncryption.KmsManaged - }); - - -.. _construct_attributes: - -Resource Attributes -------------------- - -To reference the runtime attributes of CloudFormation resources, -use one of the properties available on the resource object. - -The following example configures a |LAM| function's dead letter queue to use -the ARN of an |SQS| queue resource. - -.. code-block:: ts - - import sqs = require('@aws-cdk/aws-sqs'); - import lambda = require('@aws-cdk/aws-lambda'); - - const dlq = new sqs.CfnQueue(this, { name: 'DLQ' }); - - new lambda.CfnFunction(this, { - deadLetterConfig: { - targetArn: dlq.queueArn - } - }); - -The :py:attr:`@aws-cdk/cdk.Resource.ref` attribute represents the |cfn| -resource's intrinsic reference (or "Return Value"). For example, for `dlq.ref` -will also `refer -`_ -to the queue's ARN. When possible, it is preferrable to use an explicitly named -attribute instead of *ref*. - -.. _resource_options: - -Resource Options ----------------- - -The :py:attr:`@aws-cdk/cdk.Resource.options` object includes |CFN| options, such -as :code:`condition`, :code:`updatePolicy`, :code:`createPolicy` and -:code:`metadata`, for a resource. - -.. _parameters: - -Parameters ----------- - -.. NEEDS SOME INTRO TEXT - -.. code-block:: ts - - import sns = require('@aws-cdk/aws-sns'); - import cdk = require('@aws-cdk/cdk'); - - const p = new cdk.Parameter(this, 'MyParam', { type: 'String' }); - new sns.CfnTopic(this, 'MyTopic', { displayName: p.ref }); - -.. _outputs: - -Outputs -------- - -.. NEEDS SOME INTRO TEXT - -.. code-block:: js - - import sqs = require('@aws-cdk/aws-sqs'); - import cdk = require('@aws-cdk/cdk'); - - const queue = new sqs.CfnQueue(this, 'MyQueue'); - const out = new cdk.Output(this, 'MyQueueArn', { value: queue.queueArn }); - - const import = out.makeImportValue(); - assert(import === { "Fn::ImportValue": out.exportName } - -.. _conditions: - -Conditions ----------- - -`cdk.Condition` can be used to define CloudFormation "Condition" elements in the template. -The `cdk.Fn.conditionXx()` static methods can be used to produce "condition expressions". - -.. code-block:: js - - import sqs = require('@aws-cdk/aws-sqs'); - import cdk = require('@aws-cdk/cdk'); - - const param = new cdk.Parameter(this, 'Param1', { type: 'String' }); - const cond1 = new cdk.Condition(this, 'Condition1', { expression: cdk.Fn.conditionEquals("a", "b") }); - const cond2 = new cdk.Condition(this, 'Condition2', { expression: cdk.Fn.conditionContains([ "a", "b", "c" ], "c") }); - const cond3 = new cdk.Condition(this, 'Condition3', { expression: cdk.Fn.conditionEquals(param, "hello") }); - - const cond4 = new cdk.Condition(this, 'Condition4', { - expression: cdk.Fn.conditionOr(cond1, cond2, cdk.Fn.conditionNot(cond3)) - }); - - const cond = new cdk.Condition(this, 'MyCondition', { - expression: new cdk.FnIf(...) - }); - - const queue = new sqs.CfnQueue(this, 'MyQueue'); - queue.options.condition = cond4; - -.. _intrinsic_functions: - -Intrinsic Functions -------------------- - -.. NEEDS SOME INTRO TEXT - -.. code-block:: js - - import { Fn } from'@aws-cdk/cdk'; - Fn.join(",", [...]) - -.. _pseudo_parameters: - -Pseudo Parameters ------------------ - -.. NEEDS SOME INTRO TEXT - -.. code-block:: js - - import cdk = require('@aws-cdk/cdk'); - new cdk.AwsRegion() - -.. Add a new topic in "Advanced Topics" about integrating - cdk synch > mytemplate - into a CI/CD pipeline diff --git a/docs/src/concepts.rst b/docs/src/concepts.rst deleted file mode 100644 index dd1e2aa81d3c1..0000000000000 --- a/docs/src/concepts.rst +++ /dev/null @@ -1,49 +0,0 @@ -.. Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 - International License (the "License"). You may not use this file except in compliance with the - License. A copy of the License is located at http://creativecommons.org/licenses/by-nc-sa/4.0/. - - This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - either express or implied. See the License for the specific language governing permissions and - limitations under the License. - -.. _concepts: - -############## -|cdk| Concepts -############## - -This topic describes some of the concepts (the why and how) -behind the |cdk|. -It also discusses the advantages of a |l2| over a low-level |l1|. - -|cdk| apps are represented as a hierarchal structure we call the *construct -tree*. Every node in the tree is a |construct-class| object. The -root of an |cdk| app is typically an |app-class| construct. Apps -contain one or more |stack-class| constructs, which are deployable -units of your app. - -This composition of constructs gives you the flexibility to architect your app, such as -having a stack deployed in multiple regions. Stacks represent a collection of AWS resources, either directly or -indirectly through a child construct that itself represents an AWS resource, such as an |SQS| -queue, an |SNS| topic, an |LAM| function, or an |DDB| table. - -This composition of constructs also means you can easily create sharable constructs, -and make changes to any construct and have those changes available to consumers -as shared class libraries. - -.. toctree:: - :titlesonly: - :caption: Topics - :maxdepth: 1 - - constructs - stacks - logical-ids - environments - apps - passing-in-data - context - assets - applets diff --git a/docs/src/conf.py b/docs/src/conf.py index a884d3b0ba0c6..a40735703906e 100644 --- a/docs/src/conf.py +++ b/docs/src/conf.py @@ -62,7 +62,7 @@ # -- Project information ----------------------------------------------------- -copyright = u'2018, Amazon Web Services' +copyright = u'2019, Amazon Web Services' author = u'Amazon Web Services' # CDK_VERSION automatically replaced by build script diff --git a/docs/src/constructs.rst b/docs/src/constructs.rst deleted file mode 100644 index f21c583119dd9..0000000000000 --- a/docs/src/constructs.rst +++ /dev/null @@ -1,203 +0,0 @@ -.. Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 - International License (the "License"). You may not use this file except in compliance with the - License. A copy of the License is located at http://creativecommons.org/licenses/by-nc-sa/4.0/. - - This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - either express or implied. See the License for the specific language governing permissions and - limitations under the License. - -.. _constructs: - -########## -Constructs -########## - -Constructs are the building blocks of |cdk| applications. Constructs can have -child constructs, which in turn can have child constructs, forming a -hierarchical tree structure. - -The |cdk| includes two different levels of constructs: - -|l1| - - These constructs are low-level constructs that provide a direct, one-to-one, - mapping to an |CFN| resource, - as listed in the |CFN| topic `AWS Resource Types Reference `_. - - All |l1| members are found in the :py:mod:`@aws-cdk/resources` package. - -|l2| - - These constructs have been handwritten by AWS and come with - convenient defaults and additional knowledge about the inner workings of the - AWS resources they represent. In general, you will be able to express your - intent without worrying about the details too much, and the correct resources - will automatically be defined for you. - - |l2| members are found in the :py:mod:`@aws-cdk/NAMESPACE` packages, - where NAMESPACE is the short name for the associated service, - such as SQS for the |l2| for the |SQS| service. - See the :ref:`reference` section for descriptions of the |cdk| - packages and constructs. - -.. Hide for now - At an even higher-level than an |l2|, a |l3| - aggregates multiple, other constructs together - into common architectural patterns, such as a *queue processor* or an *HTTP - service*. - - By leveraging these common patterns, you can assemble your - application even faster than by using an |l2| directly. - - A |l3| - is not included with the standard CDK Construct - Library. Instead, we encourage you to develop and share them inside your - organization or on GitHub. - -.. _construct_structure: - -Construct Structure -=================== - -The construct tree structure is a powerful design pattern for composing high-level -abstractions. For example, you can define a ``StorageLayer`` construct that -represents your application's storage layer and include all the AWS resources, -such as |DDB| tables and |S3| buckets, needed to implement your storage layer in -this construct. When your higher-level code uses this construct, it only needs -to instantiate the ``StorageLayer`` construct. - -When you initialize a construct, -add the construct to the construct tree by specifying the parent construct as the first initializer parameter, -an identifier for the construct as the second parameter, -and a set of properties for the final parameter, -as shown in the following example. - -.. code-block:: js - - new SomeConstruct(parent, name[, props]); - -In almost all cases, you should pass the keyword ``this`` for the ``parent`` -argument, because you will generally initialize new constructs in the context of -the parent construct. Any descriptive string will do for the ``name`` -argument, -and an in-line object for the set of properties. - -.. code-block:: js - - new BeautifulConstruct(this, 'Foo', { - applicationName: 'myApp', - timeout: 300 - }); - -.. note:: - - Associating the construct to its parent as part of - initialization is necessary because the construct occasionally needs contextual - information from its parent, such as to which the region the stack is deployed. - -Use the following operations to inspect the construct tree. - -:py:attr:`aws-cdk.Construct.parent` - Gets the construct's parent construct. - -:py:meth:`aws-cdk.Construct.getChildren` - Gets an array of all of the contruct's children. - -:py:meth:`aws-cdk.Construct.getChild` - Gets the child construct with the specified ID. - -:py:meth:`aws-cdk.Construct.toTreeString()` - - Gets a string representing the construct's tree. - -.. We discuss the advantages of an |l2| over a |l1| in the :ref:`l2_advantages` section. - -.. _construct_names: - -Construct Identifiers -===================== - -Every construct in a CDK app must have an identifier unique among its siblings. -Identifiers are used to track constructs in the construct hierarchy, and to allocate -logical IDs so that |CFN| can keep track of the generated resources. - -When a construct is created, its identifier is specified as the second -initializer argument: - -.. code-block:: js - - const c1 = new MyBeautifulConstruct(this, 'OneBeautiful'); - const c2 = new MyBeautifulConstruct(this, 'TwoBeautiful'); - assert(c1.id === 'OneBeautiful'); - assert(c2.id === 'TwoBeautiful'); - -Use the :py:attr:`aws-cdk.Construct.path` property to get the path of this -construct from the root of the tree. - -When you synthesize an |cdk| tree into an |CFN| template, the |CFN| logical ID -for each resource in the template is allocated according to the path of that -resource in the construct tree. For more information, see :ref:`logical_ids`. - -Construct IDs may be any string with the following caveats: - -* Path separators (``/``s) will be replaced by double-dashes ``--``. This means - that if you are trying to look up a child construct that may have a path separator, - you will need to manually replace it with ``--``. -* Construct IDs may not include unresolved tokens (such as `new AwsRegion()`). This is - because those tokens are only resolved during deployment, and therefore cannot be used - to render a stable logical ID for any resources in this tree. - -Note that the ID of a construct does not directly map onto the physical name of -the resource when it is created! If you want to give a physical name to a bucket -or table, specify the physical name using use the appropriate property, such as -``bucketName`` or ``tableName``. Example: - -.. code-block:: js - - new Bucket(this, 'MyBucket', { - bucketName: 'physical-bucket-name' - }); - -Avoid specifying physical names. Instead, let -|CFN| generate names for you. -Use attributes, such as **bucket.bucketName**, -to discover the generated names. - -.. and pass them to your application's runtime - code, as described in :ref:`creating_runtime_value`. - -.. _construct_properties: - -Construct Properties -==================== - -Customize constructs by passing a property object as the third -parameter (*props*). Every construct has its own set of parameters, defined as an -interface. You can pass a property object to your construct in two ways: - -.. code-block:: js - - // Inline (recommended) - new Queue(this, 'MyQueue', { - visibilityTimeout: 300 - }); - - // Instantiate separate property object - const props: QueueProps = { - visibilityTimeout: 300 - }; - - new Queue(this, 'MyQueue', props); - -.. _construct_metadata: - -Construct Metadata -================== - -You can attach metadata to a construct using the -:py:meth:`aws-cdk.Construct.addMetadata` operation. Metadata entries -automatically include the stack trace from which the metadata entry was added. -Therefore, at any level of a construct you can find the code location, even if metadata -was created by a lower-level library that you don't own. diff --git a/docs/src/context.rst b/docs/src/context.rst deleted file mode 100644 index 30f96245fb3cb..0000000000000 --- a/docs/src/context.rst +++ /dev/null @@ -1,130 +0,0 @@ -.. Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 - International License (the "License"). You may not use this file except in compliance with the - License. A copy of the License is located at http://creativecommons.org/licenses/by-nc-sa/4.0/. - - This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - either express or implied. See the License for the specific language governing permissions and - limitations under the License. - -.. _context: - -##################### -Environmental Context -##################### - -When you synthesize a stack to create a |CFN| template, the |cdk| might need information based on the -environment (account and Region), such as the availability zones or AMIs available in the Region. -To enable this feature, the |toolkit| uses *context providers*, -and saves the context information into |cx-json| -the first time you call |cx-synth-code|. - -The |cdk| currently supports the following context providers. - -:py:class:`AvailabilityZoneProvider <@aws-cdk/cdk.AvailabilityZoneProvider>` - Use this provider to get the list of all supported availability zones in this environment. - For example, the following code iterates over all of the AZs in the current environment. - -.. code:: js - - // "this" refers to a parent Construct - const zones: string[] = new AvailabilityZoneProvider(this).availabilityZones; - - for (let zone of zones) { - // do somethning for each zone! - } - -:py:class:`SSMParameterProvider <@aws-cdk/cdk.SSMParameterProvider>` - Use this provider to read values from the current Region's SSM parameter store. - For example, the follow code returns the value of the 'my-awesome-parameter' key: - -.. code:: js - - const ami: string = new SSMParameterProvider(this, { - parameterName: 'my-awesome-parameter' - }).parameterValue(); - - -This is only for reading plain strings, and not recommended for secrets. For reading secure strings from SSM Parmeter -store, see the `:doc:passing_in_value_from_ssm` topic. - -:py:class:`HostedZoneProvider <@aws-cdk/aws-route53.HostedZoneProvider>` - - Use this provider to discover existing hosted zones in your account. - For example, the following code imports an existing hosted zone into - your CDK app so you can add records to it: - -.. code:: js - - const zone: HostedZoneRef = new HostedZoneProvider(this, { - domainName: 'test.com' - }).findAndImport(this, 'HostedZone'); - -:py:class:`VpcNetworkProvider <@aws-cdk/aws-ec2.VpcNetworkProvider>` - Use this provider to look up and reference existing VPC in your accounts. - For example, the follow code imports a VPC by tag name: - -.. code:: js - - const provider = new VpcNetworkProvider(this, { - tags: { - Purpose: 'WebServices' - } - }); - const vpc = VpcNetworkRef.import(this, 'VPC', provider.vpcProps); - - -########################### -Viewing and managing context -########################### - -Context is used to retrieve things like Availability Zones in your account, or -AMI IDs used to start your instances. In order to avoid unexpected changes to -your deployments-- let's say you were adding a ``Queue`` to your application but -it happened that a new Amazon Linux AMI was released and all of a sudden your -AutoScalingGroup will change-- we store the context values in ``cdk.json``, so -after they've been retrieved once we can be sure we're using the same value on -the next synthesis. - -To have a look at the context values stored for your application, run ``cdk -context``. You will see something like the following: - -.. code:: - - $ cdk context - - Context found in cdk.json: - - β”Œβ”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ # β”‚ Key β”‚ Value β”‚ - β”œβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ - β”‚ 1 β”‚ availability-zones:account=123456789012:region=us- β”‚ [ "us-east-1a", "us-east-1b", "us-east-1c", β”‚ - β”‚ β”‚ east-1 β”‚ "us-east-1d", "us-east-1e", "us-east-1f" ] β”‚ - β”œβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ - β”‚ 2 β”‚ ssm:account=123456789012:parameterName=/aws/ β”‚ "ami-013be31976ca2c322" β”‚ - β”‚ β”‚ service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_ β”‚ β”‚ - β”‚ β”‚ 64-gp2:region=us-east-1 β”‚ β”‚ - β””β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - - Run cdk context --reset KEY_OR_NUMBER to remove a context key. It will be refreshed on the next CDK synthesis run. - -At some point, we *do* want to update to the latest version of the Amazon Linux -AMI. To do a controlled update of the context value, reset it and -synthesize again: - -.. code:: - - $ cdk context --reset 2 - Context value - ssm:account=123456789012:parameterName=/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2:region=us-east-1 - reset. It will be refreshed on the next SDK synthesis run. - - $ cdk synth - ... - -To clear all context values, run: - -.. code:: - - $ cdk context --clear diff --git a/docs/src/ecs_example.rst b/docs/src/ecs_example.rst deleted file mode 100644 index d5202c9267e61..0000000000000 --- a/docs/src/ecs_example.rst +++ /dev/null @@ -1,200 +0,0 @@ -.. Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 - International License (the "License"). You may not use this file except in compliance with the - License. A copy of the License is located at http://creativecommons.org/licenses/by-nc-sa/4.0/. - - This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - either express or implied. See the License for the specific language governing permissions and - limitations under the License. - -.. _ecs_example: - -############# -|ECS| Example -############# - -This example walks you through creating a Fargate service running on an ECS cluster fronted by an internet-facing -application load balancer. - -|ECSlong| (|ECS|) is a highly scalable, fast, container management service -that makes it easy to run, stop, and manage Docker containers on a cluster. -You can host your cluster on a serverless infrastructure that is managed by -|ECS| by launching your services or tasks using the Fargate launch type. -For more control you can host your tasks on a cluster of -|EC2long| (|EC2|) instances that you manage by using the EC2 launch type. - -This example shows you how to launch some services using the Fargate launch type. -If you've ever used the console to create a Fargate service, -you know that there are many steps you must follow to accomplish that task. -AWS has a number of tutorials and documentation topics that walk you through -creating a Fargate service, -including: - -* `How to Deploy Docker Containers - AWS `_ - -* `Setting up with Amazon ECS `_ and - `Getting Started with Amazon ECS using Fargate `_ - -This example creates a similar Fargate service in |cdk| code. - -Since |ECS| can be used with a number of AWS services, -you should understand how the |ECS| construct that we use in this example -gives you a leg up on using AWS services by providing the following benefits: - -* Automatically configures a load balancer. - -* Automatic security group opening for load balancers, - which enables load balancers to communicate with instances - without you explictly creating a security group. - -* Automatic ordering dependency between service and load balancer attaching to a target group, - where the |cdk| enforces the correct order of creating the listener before an instance is created - -* Automatic userdata configuration on auto-scaling group, - which creates the correct configuration to associate a cluster to AMI(s). - -* Early validation of parameter combinations, which exposes |CFN| issues earlier, thus saving you deployment time. - For example, depending upon the task, it is easy to mis-configure the memory settings. - Previously you would not encounter an error until you deployed your app, - but now the |cdk| can detect a misconfiguration and emit an error when you synthesize your app. - -* Automatically adds permissions for |ECR| if you use an image from |ECR| - When you use an image from |ECR|, the |cdk| adds the correct permissions. - -* Automatic autoscaling - The |cdk| supplies a method so you can autoscaling instances when you use an |EC2| cluster; - this functionality is done automatically when you use an instance in a Fargate cluster. - - In addition, the |cdk| will prevent instances from being deleted when - autoscaling tries to kill an instance, - but either a task is running or is scheduled on that instance. - - Previously, you had to create a Lambda function to have this functionality. - -* Asset support, so that you can deploy source from your machine to |ECS| in one step - Previously, to use application source you had to perform a number of manual steps - (upload to |ECR|, create Docker image, etc.). - -.. _creating_ecs_l2_example_1: - -Step 1: Create the Directory and Initialize the |cdk| ------------------------------------------------------ - -Let's start with creating a new directory to hold our |cdk| code -and create a new app in that directory. - -.. code-block:: sh - - mkdir MyEcsConstruct - cd MyEcsConstruct - -.. tabs:: - - .. group-tab:: TypeScript - - .. code-block:: sh - - cdk init --language typescript - - Build the app and confirm that it creates an empty stack. - - .. code-block:: sh - - npm run build - cdk synth - - You should see a stack like the following, - where CDK-VERSION is the version of the CDK. - - .. code-block:: sh - - Resources: - CDKMetadata: - Type: 'AWS::CDK::Metadata' - Properties: - Modules: @aws-cdk/cdk=CDK-VERSION,@aws-cdk/cx-api=CDK-VERSION,my_ecs_construct=0.1.0 - -.. _creating_ecs_l2_example_2: - -Step 2: Add the |EC2| and |ECS| Packages ----------------------------------------- - -Install support for |EC2| and |ECS|. - -.. tabs:: - - .. group-tab:: TypeScript - - .. code-block:: sh - - npm install @aws-cdk/aws-ec2 @aws-cdk/aws-ecs - -.. _creating_ecs_l2_example_3: - -Step 3: Create a Fargate Service --------------------------------- - -There are two different ways of running your container tasks with |ECS|: - -- Using the **Fargate** launch type, - where |ECS| manages the physical machines that your containers are running on for you -- Using the **EC2** launch type, where you do the managing, such as specifying autoscaling - -The following example creates a Fargate service running on an ECS cluster fronted by an internet-facing -application load balancer. - -.. tabs:: - - .. group-tab:: TypeScript - - Add the following import statements to *lib/my_ecs_construct-stack.ts*: - - .. code-block:: typescript - - import ec2 = require('@aws-cdk/aws-ec2'); - import ecs = require('@aws-cdk/aws-ecs'); - - Replace the comment at the end of the constructor with the following code: - - .. code-block:: typescript - - const vpc = new ec2.VpcNetwork(this, 'MyVpc', { - maxAZs: 3 // Default is all AZs in region - }); - - const cluster = new ecs.Cluster(this, 'MyCluster', { - vpc: vpc - }); - - // Create a load-balanced Fargate service and make it public - new ecs.LoadBalancedFargateService(this, 'MyFargateService', { - cluster: cluster, // Required - cpu: '512', // Default is 256 - desiredCount: 6, // Default is 1 - image: ecs.ContainerImage.fromDockerHub('amazon/amazon-ecs-sample'), // Required - memoryMiB: '2048', // Default is 512 - publicLoadBalancer: true // Default is false - }); - - Save it and make sure it builds and creates a stack. - - .. code-block:: sh - - npm run build - cdk synth - - The stack is hundreds of lines, so we won't show it here. - The stack should contain one default instance, a private subnet and a public subnet - for the three availability zones, and a security group. - - Deploy the stack. - - .. code-block:: sh - - cdk deploy - - |CFN| displays information about the dozens of steps that - it takes as it deploys your app. - -That's how easy it is to create a Fargate service to run a Docker image. diff --git a/docs/src/environments.rst b/docs/src/environments.rst deleted file mode 100644 index 9ec71063eac59..0000000000000 --- a/docs/src/environments.rst +++ /dev/null @@ -1,64 +0,0 @@ -.. Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 - International License (the "License"). You may not use this file except in compliance with the - License. A copy of the License is located at http://creativecommons.org/licenses/by-nc-sa/4.0/. - - This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - either express or implied. See the License for the specific language governing permissions and - limitations under the License. - -.. _environments: - -############################### -Environments and Authentication -############################### - -The |cdk| refers to the combination of an account ID and a Region as an *environment*. -The simplest environment is the one you get by default, -which is the one you get when you have set up your credentials and a default Region as described in -:ref:`credentials`. - -When you create a |stack-class| instance, you can supply the target deployment environment -for the stack using the **env** property, as shown in the following example, -where REGION is the Region in which you want to create the stack and ACCOUNT is your account ID. - -.. code:: js - - new MyStack(app, { env: { region: 'REGION', account: 'ACCOUNT' } }); - -For each of the two arguments **region** and **account**, the |cdk| uses the -following lookup procedure: - -- If **region** or **account** are provided directly as an property to the - Stack, use that. -- Otherwise, read **default-account** and **default-region** from the application's context. - These can be set in the |toolkit| in either the local |cx-json| file or the global version in - *$HOME/.cdk* on Linux or MacOS or *%USERPROFILE%\\.cdk* on Windows. -- If these are not defined, it will determine them as follows: - - - **account**: use account from default SDK credentials. Environment - variables are tried first (**AWS_ACCESS_KEY_ID** and **AWS_SECRET_ACCESS_KEY**), - followed by credentials in *$HOME/.aws/credentials* on Linux or MacOS - or *%USERPROFILE%\\.aws\\credentials* on Windows. - - **region**: use the default region configured in *$HOME/.aws/config* on - Linux or MacOS or *%USERPROFILE%\\.aws\\config* on Windows. - - You can set these defaults manually, but we recommend you use ``aws - configure``, as described in the :doc:`getting-started` topic. - -We recommend that you use the default environment for development stacks, -and explicitly specify accounts and Regions for production stacks. - -.. note:: - - Note that even though the region and account might explicitly be set on your - Stack, if you run ``cdk deploy`` the |cdk| will still use the - currently-configured SDK credentials, as provided via the **AWS_** - environment variables or ``aws configure``. This means that if you want to - deploy stacks to multiple accounts, you will have to set the correct - credentials for each invocation to ``cdk deploy STACK``. - - In the future, we will provide the ability to specify credential sources for - individual accounts so that you can deploy to multiple accounts using one - invocation of ``cdk deploy``, but this feature is not available yet. - diff --git a/docs/src/examples.rst b/docs/src/examples.rst deleted file mode 100644 index fc5876b556925..0000000000000 --- a/docs/src/examples.rst +++ /dev/null @@ -1,217 +0,0 @@ -.. Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 - International License (the "License"). You may not use this file except in compliance with the - License. A copy of the License is located at http://creativecommons.org/licenses/by-nc-sa/4.0/. - - This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - either express or implied. See the License for the specific language governing permissions and - limitations under the License. - -.. _cdk_examples: - -############## -|cdk| Examples -############## - -This topic contains some examples to help you get started using some of the advanced constructs -offered by the |cdk|. - -* :doc:`ecs_example` walks you through creating a Fargate service running on an ECS cluster fronted by an internet-facing -application load balancer. - -* :doc:`apples_example` walks you through creating the resources for a simple widget dispensing service. - -.. toctree:: - :maxdepth: 2 - :hidden: - - ECS Example - Apples Example - -.. _creating_ecs_l2_example: - -Creating an |ECS| |cdk| App -=========================== - -|ECSlong| (|ECS|) is a highly scalable, fast, container management service -that makes it easy to run, stop, and manage Docker containers on a cluster. -You can host your cluster on infrastructure that is managed by -|ECS| by launching your services or tasks using the Fargate launch type. -For more control you can host your tasks on a cluster of -|EC2long| (|EC2|) instances that you manage by using the EC2 launch type. - -This example shows you how to launch some services using the Fargate launch type. -If you've ever used the console to create a Fargate service, -you know that there are many steps you must follow to accomplish that task. -AWS has a number of tutorials and documentation topics that walk you through -creating a Fargate service, -including: - -* `How to Deploy Docker Containers - AWS `_ - -* `Setting up with Amazon ECS `_ and - `Getting Started with Amazon ECS using Fargate `_ - -This example creates a similar Fargate service in |cdk| code. - -Since |ECS| can be used with a number of AWS services, -you should understand how the |ECS| construct that we use in this example -gives you a leg up on using AWS services by providing the following benefits: - -* Automatically configures a load balancer. - -* Automatic security group opening for load balancers, - which enables load balancers to communicate with instances - without you explictly creating a security group. - -* Automatic ordering dependency between service and load balancer attaching to a target group, - where the |cdk| enforces the correct order of creating the listener before an instance is created - -* Automatic userdata configuration on auto-scaling group, - which creates the correct configuration to associate a cluster to AMI(s). - -* Early validation of parameter combinations, which exposes |CFN| issues earlier, thus saving you deployment time. - For example, depending upon the task, it is easy to mis-configure the memory settings. - Previously you would not encounter an error until you deployed your app, - but now the |cdk| can detect a misconfiguration and emit an error when you synthesize your app. - -* Automatically adds permissions for |ECR| if you use an image from |ECR| - When you use an image from |ECR|, the |cdk| adds the correct permissions. - -* Convenient API for autoscaling - The |cdk| supplies a method so you can autoscale instances when you use an |EC2| cluster; - this functionality is done automatically when you use an instance in a Fargate cluster. - - In addition, the |cdk| will prevent instances from being deleted when - autoscaling tries to kill an instance, - but either a task is running or is scheduled on that instance. - - Previously, you had to create a Lambda function to have this functionality. - -* Asset support, so that you can deploy source from your machine to |ECS| in one step - Previously, to use application source you had to perform a number of manual steps - (upload to |ECR|, create Docker image, etc.). - -.. _creating_ecs_l2_example_1: - -Step 1: Create the Directory and Initialize the |cdk| ------------------------------------------------------ - -Let's start with creating a new directory to hold our |cdk| code -and create a new app in that directory. - -.. code-block:: sh - - mkdir MyEcsConstruct - cd MyEcsConstruct - -.. tabs:: - - .. group-tab:: TypeScript - - .. code-block:: sh - - cdk init --language typescript - - Build the app and confirm that it creates an empty stack. - - .. code-block:: sh - - npm run build - cdk synth - - You should see a stack like the following, - where CDK-VERSION is the version of the CDK. - - .. code-block:: sh - - Resources: - CDKMetadata: - Type: 'AWS::CDK::Metadata' - Properties: - Modules: @aws-cdk/cdk=CDK-VERSION,@aws-cdk/cx-api=CDK-VERSION,my_ecs_construct=0.1.0 - -.. _creating_ecs_l2_example_2: - -Step 2: Add the |EC2| and |ECS| Packages ----------------------------------------- - -Install support for |EC2| and |ECS|. - -.. tabs:: - - .. group-tab:: TypeScript - - .. code-block:: sh - - npm install @aws-cdk/aws-ec2 @aws-cdk/aws-ecs - -.. _creating_ecs_l2_example_3: - -Step 3: Create a Fargate Service --------------------------------- - -There are two different ways of running your container tasks with |ECS|: - -- Using the **Fargate** launch type, - where |ECS| manages the physical machines that your containers are running on for you -- Using the **EC2** launch type, where you do the managing, such as specifying autoscaling - -The following example creates a Fargate service running on an ECS cluster fronted by an internet-facing -application load balancer. - -.. tabs:: - - .. group-tab:: TypeScript - - Add the following import statements to *lib/my_ecs_construct-stack.ts*: - - .. code-block:: typescript - - import ec2 = require('@aws-cdk/aws-ec2'); - import ecs = require('@aws-cdk/aws-ecs'); - - Replace the comment at the end of the constructor with the following code: - - .. code-block:: typescript - - const vpc = new ec2.VpcNetwork(this, 'MyVpc', { - maxAZs: 3 // Default is all AZs in region - }); - - const cluster = new ecs.Cluster(this, 'MyCluster', { - vpc: vpc - }); - - // Create a load-balanced Fargate service and make it public - new ecs.LoadBalancedFargateService(this, 'MyFargateService', { - cluster: cluster, // Required - cpu: '512', // Default is 256 - desiredCount: 6, // Default is 1 - image: ecs.ContainerImage.fromDockerHub('amazon/amazon-ecs-sample'), // Required - memoryMiB: '2048', // Default is 512 - publicLoadBalancer: true // Default is false - }); - - Save it and make sure it builds and creates a stack. - - .. code-block:: sh - - npm run build - cdk synth - - The stack is hundreds of lines, so we won't show it here. - The stack should contain one default instance, a private subnet and a public subnet - for the three availability zones, and a security group. - - Deploy the stack. - - .. code-block:: sh - - cdk deploy - - |CFN| displays information about the dozens of steps that - it takes as it deploys your app. - -That's how easy it is to create a Fargate service to run a Docker image. diff --git a/docs/src/getting-started.rst b/docs/src/getting-started.rst deleted file mode 100644 index 211f670613e4e..0000000000000 --- a/docs/src/getting-started.rst +++ /dev/null @@ -1,183 +0,0 @@ -.. Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 - International License (the "License"). You may not use this file except in compliance with the - License. A copy of the License is located at http://creativecommons.org/licenses/by-nc-sa/4.0/. - - This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - either express or implied. See the License for the specific language governing permissions and - limitations under the License. - -.. _getting_started: - -############################## -Getting Started with the |cdk| -############################## - -This topic describes how to download, install, and configure the |cdk|. - -.. _installing_cdk: - -Installing the |cdk| -==================== - -This section describes how to install the |cdk|, -and lists the prerequsites for each supported language. - -.. _installing_prerequisites: - -Prerequisites -------------- - -You must install -`Node.js (>= 8.11.x) `_ to use the command-line -toolkit and language bindings. - -If you use Java, you must set the `JAVA_HOME` environment variable to the path to where you have -installed the JDK on your machine to build an |cdk| app in Java. - -Specify your credentials and region with the -`AWS CLI `_. -You must specify both your credentials and a region to use the toolkit. -See :ref:`credentials ` for information on using the AWS CLI to -specify your credentials. - -.. _installing_toolkit: - -Installing the Command-Line Toolkit ------------------------------------ - -Install the toolkit using the following `npm `_ command: - -.. code-block:: sh - - npm install -g aws-cdk - -Run the following command to see the currently installed version of the toolkit -(this guide was written for |version|): - -.. code-block:: sh - - cdk --version - -.. _credentials: - -Configuring the |cdk| -===================== - -You must specify your default credentials and region to use the toolkit. - -Use the `AWS Command Line Interface `_ -``aws configure`` command to specify your default credentials and region. - -You can also set environment variables for your default credentials and region. -Environment variables take precedence over settings in the credentials or config file. - -* *AWS_ACCESS_KEY_ID* specifies your access key -* *AWS_SECRET_ACCESS_KEY* specifies your secret access key -* *AWS_DEFAULT_REGION* specifies your default region - -See `Environment Variables `_ -in the CLI User Guide for details. - -.. include:: region-note.rst - -The |cdk| toolkit needs to know how to execute your |cdk| app. It requires that the -:code:`--app` command-line option points to an executable program that adheres -to the toolkit's protocol. -Although you can include an :code:`--app` option every time you use the toolkit, -we recommend that you instead create a :code:`cdk.json` file at the root of -your project directory with the following content: - -.. tabs:: - - .. group-tab:: C# - - Define the :code:`--app` option in a **cdk.json** file: - - .. code-block:: json - - { - "app": "dotnet run --project HelloCdk.csproj" - } - - .. group-tab:: JavaScript - - Define the :code:`--app` option in **cdk.json** to execute **hello-cdk.js** - using **node**: - - .. code-block:: json - - { - "app": "node bin/hello-cdk.js" - } - - .. group-tab:: TypeScript - - Define the :code:`--app` option in **cdk.json** to execute **hello-cdk.js** - using **node**: - - .. code-block:: json - - { - "app": "node bin/hello-cdk.js" - } - - .. group-tab:: Java - - Specify a - **CLASSPATH**, which contains both the compiled code and dependencies, - to execute the Java program. - - Use **maven-dependency-plugin** in your **pom.xml** file to produce the file **.classpath.txt** - whenever the project is compiled: - - .. code-block:: xml - - - - - - - - org.apache.maven.plugins - maven-dependency-plugin - 2.8 - - - build-classpath - generate-sources - - build-classpath - - - .classpath.txt - - - - - - - - Run **mvn compile** and verify that **.classpath.txt** exists: - - .. code-block:: sh - - mvn compile - ls .classpath.txt - - Create a shim **app.sh** to execute the |cdk| Java app: - - .. code-block:: sh - - #!/bin/bash - exec java -cp target/classes:$(cat .classpath.txt) com.acme.MyApp app $@ - - Define the :code:`--app` option in **cdk.json**: - - .. code-block:: json - - { - "app": "/bin/bash ./app.sh" - } - diff --git a/docs/src/index.rst b/docs/src/index.rst index 109981a62d3cf..f831d81695b25 100644 --- a/docs/src/index.rst +++ b/docs/src/index.rst @@ -1,4 +1,4 @@ -.. Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +.. Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License (the "License"). You may not use this file except in compliance with the @@ -12,106 +12,18 @@ Welcome ####### -Welcome to the |cdk-long| (|cdk|) User Guide! +Welcome to the |cdk-long| (|cdk|)! The |cdk| is an infrastructure modeling framework that allows you to define your cloud resources using an imperative programming interface. *The CDK is currently in developer preview. We look forward to community feedback and collaboration*. -To get started see :doc:`getting-started` - -.. image:: screencast.gif - -.. npm install -g aws-cdk - ADDED: mkdir ~/hello-cdk; cd ~/hello-cdk - cdk init --language typescript - npm run build - ADDED: cdk synth - cdk deploy - ADDED: - vim bin/aws-cdk.ts -> lib/hello-cdk-stack.ts - // add SNS, SQS, queue, topic, topic.subscribeQueue - cdk diff - cdk deploy - cdk diff - -Developers can use one of the supported programming languages to define reusable -cloud components called :doc:`constructs`, which are composed together into -:doc:`stacks` and :doc:`apps`. - -.. note:: - Unless otherwise indicated, - the code examples in this guide are in TypeScript. - - To aid you in porting a TypeScript example to a supported programming language, - take a look at the example code for your language in the :doc:`Reference section `. - -The :ref:`AWS CDK Toolkit ` is a command-line tool for interacting with CDK -apps. It allows developers to synthesize artifacts such as AWS CloudFormation -Templates, deploy stacks to development AWS accounts and "diff" against a -deployed stack to understand the impact of a code change. - -The :doc:`AWS Construct Library ` includes a module for each -AWS service with constructs that offer rich APIs that encapsulate the details of -how to use AWS. The AWS Construct Library aims to reduce the complexity and -glue-logic required when integrating various AWS services to achieve your goals -on AWS. - -.. note:: There is no charge for using the |cdk|, however you may incur AWS charges for creating or using AWS - `chargeable resources `_, - such as running |EC2| instances or using |S3| storage. - Use the - `AWS Simple Monthly Calculator `_ - to estimate charges for the use of various AWS resources. - -.. _additional_docs: - -Additional Documentation and Resources -====================================== - -In addition to this guide, the following are other resources available to |cdk| users: - -* `AWS Developer blog `_ -* `Gitter Channel `_ -* `Stack Overflow `_ -* `GitHub repository `_ - - * `License `_ - * `Issues `_ - * `Releases `_ - * `Examples `_ - * `Documentation source `_ - -* `AWS CDK Sample for Cloud9 `_ -* `AWS CloudFormation Concepts `_ - -.. _about_aws: - -About Amazon Web Services -========================= - -Amazon Web Services (AWS) is a collection of digital infrastructure services that developers can -leverage when developing their applications. The services include computing, storage, database, and -application synchronization (messaging and queuing). - -AWS uses a pay-as-you-go service model. You are charged only for the services that you |mdash| or -your applications |mdash| use. Also, to make AWS useful as a platform for prototyping and -experimentation, AWS offers a free usage tier, in which services are free below a certain level of -usage. For more information about AWS costs and the free usage tier, see -`Test-Driving AWS in the Free Usage Tier `_. - -To obtain an AWS account, go to `aws.amazon.com `_ and click :guilabel:`Create an AWS Account`. +This documentation is the reference for the |cdk|. +The |cdk| documentation also includes the +`AWS CDK User Guide `_. .. toctree:: :maxdepth: 2 :hidden: - - Getting Started - Tutorial - Examples - Concepts - AWS Construct Library - AWS CloudFormation Library - Tools - Writing Constructs + Reference diff --git a/docs/src/logical-ids.rst b/docs/src/logical-ids.rst deleted file mode 100644 index 7446849dc60e5..0000000000000 --- a/docs/src/logical-ids.rst +++ /dev/null @@ -1,137 +0,0 @@ -.. Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 - International License (the "License"). You may not use this file except in compliance with the - License. A copy of the License is located at http://creativecommons.org/licenses/by-nc-sa/4.0/. - - This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - either express or implied. See the License for the specific language governing permissions and - limitations under the License. - -.. _logical_ids: - -########### -Logical IDs -########### - -When you synthesize a stack into an |CFN| template, -the |cdk| assigns a -`logical ID `_, -which must be unique within the template, -to each resource in the stack. - -.. important:: - - When you update the template, |CFN| uses these logical IDs to plan the update - and apply changes. Therefore, logical IDs must remain "stable" across updates. - If you make a modification in your code that results in a change to a logical ID - of a resource, |CFN| deletes the resource and creates a new resource when it - updates the stack. - -Each resource in the construct tree has a unique path that represents its -location within the tree. -Since logical IDs can only use alphanumeric characters and cannot exceed 255 characters, -the CDK is unable to simply use a delimited path as the logical ID. -Instead, logical IDs are allocated by concatenating a human-friendly rendition -from the path (concatenation, de-duplicate, trim) with an eight-character MD5 -hash of the delimited path. -This final component is necessary since |CFN| logical IDs cannot include -the delimiting slash character (/), so simply concatenating the component -values does not work. For example, concatenating the components of the -path */a/b/c* produces **abc**, which is the same as concatenating the components of -the path */ab/c*. - -.. code-block:: text - - VPCPrivateSubnet2RouteTable0A19E10E - <-----------human---------><-hash-> - -Low-level CloudFormation resources (from `@aws-cdk/resources`) -that are direct children of the |stack-class| class use -their name as their logical ID without modification. This makes it easier to -port existing templates into a CDK app. - -This scheme ensures that: - -Logical IDs have a human-friendly portion - This is useful when interacting directly with the synthesized |CFN| - template during development and deployment. - -Logical IDs are unique within the stack - This is ensured by the MD5 component, - which is based on the absolute path to the resource, - which is unique within a stack. - -Logical IDs remain unchanged across updates - This is true as long as their location within the construct tree doesn't change. - -The |cdk| applies some heuristics to improve the human-friendliness of the prefix: - -- If a path component is **Default**, it is hidden completely from the logical ID - computation. You will generally want to use this if you create a new construct - that wraps an existing one. By naming the inner construct **Default**, you - ensure that the logical identifiers of resources in already-deployed copy of - that construct do not change. -- If a path component is **Resource**, it is omitted from the human readable portion - of the logical ID. This postfix does not normally contribute any additional useful information to the ID. -- If two subsequent names in the path are the same, only one is retained. -- If the prefix exceeds 240 characters, it is trimmed to 240 characters. - This ensures that the total length of the logical ID does not exceed the 255 character - |CFN| limit for logical IDs. - -.. _changing_logical_ids: - -Renaming Logical IDs -==================== - -The :py:meth:`aws-cdk.Stack.renameLogical` method can be used to explicitly assign -logical IDs to certain resources. - -.. code-block:: javascript - - class MyStack extends Stack { - constructor(parent: App, name: string, props: StackProps) { - super(parent, name); - - // note that `renameLogical` must be called /before/ defining the construct. - // a good practice would be to always put these at the top of your stack initializer. - this.renameLogical('MyTableCD117FA1', 'MyTable'); - this.renameLogical('MyQueueAB4432A3', 'MyAwesomeQueue'); - - new Table(this, 'MyTable'); - new Queue(this, 'MyQueue'); - } - } - -In some cases changing a resource -results in a structural change, -which results in a different path, -thus changing the logical ID of the resource. - -When a resource's logical ID changes, -|CFN| eventually deletes the old resource and create a new resource, -as it cannot determine that the two resources are the same. -Depending on the nature of the resource, -this can be disastrous in production, such as when deleting a |DDB| table. - -You could use -`AWS CloudFormation Stack Policies -`_ -to protect critical resources in your stack from accidental deletion. -Although this |CFN| feature is not supported in the |cdk| and |toolkit|, -the |cdk| does provide a few other mechanisms to help deal with logical ID changes. - -If you have CDK stacks deployed with persistent resources such as S3 buckets or -DynamoDB tables, you might want to explicitly "rename" the new logical IDs to -match your existing resources. - -First, make sure you compare the newly synthesized template with any deployed -stacks. `cdk diff` will tell you which resources are about to be destroyed: - -.. code:: shell - - [-] ☒️ Destroying MyTable (type: AWS::DynamoDB::Table) - [+] πŸ†• Creating MyTableCD117FA1 (type: AWS::DynamoDB::Table) - -Now, you can add a :py:meth:`aws-cdk.Stack.renameLogical` call before the -table is defined to rename **MyTableCD117FA1** to **MyTable**. diff --git a/docs/src/passing-in-data.rst b/docs/src/passing-in-data.rst deleted file mode 100644 index 0a04174b40a02..0000000000000 --- a/docs/src/passing-in-data.rst +++ /dev/null @@ -1,282 +0,0 @@ -.. Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 - International License (the "License"). You may not use this file except in compliance with the - License. A copy of the License is located at http://creativecommons.org/licenses/by-nc-sa/4.0/. - - This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - either express or implied. See the License for the specific language governing permissions and - limitations under the License. - -.. _passing_in_values: - -############################################ -Passing in External Values to Your |cdk| App -############################################ - -.. See https://github.com/awslabs/aws-cdk/issues/603 (includes work from the following PR) - https://github.com/awslabs/aws-cdk/pull/183 - -There may be cases where you want to parameterize one or more of your stack resources. -Therefore, you want to be able to pass values into your app from outside your app. -Currently, you can get values into your app from outside your app through any of the following. - -- Using a context variable -- Using an environment variable -- Using an SSM Parameter Store variable -- Using a Secrets manager value -- Using a value from another stack -- Using a |CFN| parameter -- Using a resource in an existing |CFN| template - -Each of these techniques is described in the following sections. - -.. _passing_in_values_from_context: - -Getting a Value from a Context Variable -======================================= - -You can specify a context variable either as -part of a |toolkit| command, -or in a **context** section of *cdk.json*. - -To create a command-line context variable, -use the **--context** (**-c**) option of a |toolkit| command, -as shown in the following example. - -.. code-block:: sh - - cdk synth -c bucket_name=mygroovybucket - -To specify the same context variable and value in *cdk.json*: - -.. code-block:: json - - { - "context": { - "bucket_name": "myotherbucket" - } - } - -To get the value of a context variable in your app, -use code like the following, -which gets the value of the context variable **bucket_name**. - -.. code-block:: ts - - const bucket_name string = this.getContext("bucket_name"); - -.. _passing_in_value_from_env_vars: - -Getting a Value from an Environment Variable -============================================ - -To get the value of an environment variable, -use code like the following, -which gets the value of the environment variable **MYBUCKET**. - -.. code-block:: ts - - const bucket_name = process.env.MYBUCKET; - -.. _passing_in_value_from_ssm: - -Getting a Value from an SSM Store Variable -========================================== - -There are three ways to get the value of an SSM parameter store variable, depending on whether you want -the latest version of a plain string, a particular version of a plain string, or a particular version -of a secret string. It is not possible to retrieve the latest version of a secure string. To read the -latest version of a secret, you have to read the secret from SecretsManager (see `doc:using_value_from_secrets_manager`). -The first two are not recommended for values that are supposed to be secrets, such as passwords. - -To retrieve the latest value once (as a context value, see the :doc:`context` topic), and keep on using the same value -until the context value manually refreshed, use a :py:class:`SSMParameterProvider <@aws-cdk/cdk.SSMParameterProvider>`: - -.. code-block:: ts - - import cdk = require('@aws-cdk/cdk'); - - const myvalue = new cdk.SSMParameterProvider(this).getString("my-parameter-name"); - -To read a particular version of an SSM Parameter Store plain string value at CloudFormation deployment time, -use :py:class:`SsmParameterStoreString <@aws-cdk/cdk.SsmParameterStoreString>`: - -.. code-block:: ts - - import ssm = require('@aws-cdk/aws-ssm'); - - const parameterString = new ssm.ParameterStoreString(this, 'MyParameter', { - parameterName: 'my-parameter-name', - version: 1, - }); - const myvalue = parameterString.value; - -To read a particular version of an SSM Parameter Store SecureString value at CloudFormation deployment time, -use :py:class:`SsmParameterStoreSecureString <@aws-cdk/cdk.SsmParameterStoreSecureString>`: - -.. code-block:: ts - - import ssm = require('@aws-cdk/aws-ssm'); - - const secureString = new ssm.ParameterStoreSecureString(this, 'MySecretParameter', { - parameterName: 'my-secret-parameter-name', - version: 1, - }); - const myvalue = secureString.value; - - -.. _using_value_from_secrets_manager: - -Getting a Value from AWS Secrets Manager -======================================== - -To use values from AWS Secrets Manager in your CDK app, create an instance of :py:class:`SecretsManagerValue -<@aws-cdk/cdk.SecretsManagerValue>`. It represents a value that is retrieved from Secrets Manager and used -at CloudFormation deployment time. - -.. code-block:: ts - - import secretsmanager = require('@aws-cdk/aws-secretsmanager'); - - const loginSecret = new secretsmanager.SecretString(stack, 'Secret', { - secretId: 'MyLogin' - - // By default, the latest version is retrieved. It's possible to - // use a specific version instead. - // versionStage: 'AWSCURRENT' - }); - - // Retrieve a value from the secret's JSON - const username = loginSecret.jsonFieldValue('username'); - const password = loginSecret.jsonFieldValue('password'); - - // Retrieve the whole secret's string value - const fullValue = loginSecret.value; - -.. _passing_in_value_between_stacks: - -Passing in a Value From Another Stack -===================================== - -You can pass a value from one stack to another stack in the same app -by using the **export** method in one stack and the **import** method in the other stack. - -The following example creates a bucket on one stack -and passes a reference to that bucket to the other stack through an interface. - -First create a stack with a bucket. -The stack includes a property we use to pass the bucket's properties to the other stack. -Note how we use the **export** method on the bucket to get its properties and save them -in the stack property. - -.. code-block:: ts - - class HelloCdkStack extends cdk.Stack { - // Property that defines the stack you are exporting from - public readonly myBucketAttributes: s3.BucketAttributes; - - constructor(parent: cdk.App, name: string, props?: cdk.StackProps) { - super(parent, name, props); - - const mybucket = new s3.Bucket(this, "MyFirstBucket"); - - // Save bucket's *BucketAttributes* - this.myBucketAttributes = mybucket.export(); - } - } - -Create an interface for the second stack's properties. -We use this interface to pass the bucket properties between the two stacks. - -.. code-block:: ts - - // Interface we'll use to pass the bucket's properties to another stack - interface MyCdkStackProps { - theBucketAttributes: s3.BucketAttributes; - } - -Create the second stack that gets a reference to the other bucket -from the properties passed in through the constructor. - -.. code-block:: ts - - // The class for the other stack - class MyCdkStack extends cdk.Stack { - constructor(parent: cdk.App, name: string, props: MyCdkStackProps) { - super(parent, name); - - const myOtherBucket = s3.Bucket.import(this, "MyOtherBucket", props.theBucketAttributes); - - // Do something with myOtherBucket - } - } - -Finally, connect the dots in your app. - -.. code-block:: ts - - const app = new cdk.App(); - - const myStack = new HelloCdkStack(app, "HelloCdkStack"); - - new MyCdkStack(app, "MyCdkStack", { - theBucketAttributes: myStack.myBucketAttributes - }); - - app.run(); - -.. _using_cfn_parameter: - -Using an |CFN| Parameter -======================== - -See the -`Parameters `_ -topic for information about using the optional *Parameters* section to customize your |CFN| templates. - -You can also get a reference to a resource in an existing |CFN| template, -as described in :doc:`concepts`. - -.. _using_cfn_template: - -Using an Existing |CFN| Template -================================ - -The |cdk| provides a mechanism that you can use to -incorporate resources from an existing |CFN| template -into your |cdk| app. -For example, suppose you have a template, -*my-template.json*, -with the following resource, -where **S3Bucket** is the logical ID of the bucket in your template: - -.. code-block:: json - - { - "S3Bucket": { - "Type": "AWS::S3::Bucket", - "Properties": { - "prop1": "value1" - } - } - } - -You can include this bucket in your |cdk| app, -as shown in the following example -(note that you cannot use this method in an |l2| construct): - -.. code-block:: ts - - import cdk = require("@aws-cdk/cdk"); - import fs = require("fs"); - - new cdk.Include(this, "ExistingInfrastructure", { - template: JSON.parse(fs.readFileSync("my-template.json").toString()) - }); - -Then to access an attribute of the resource, such as the bucket's ARN: - -.. code-block:: ts - - const bucketArn = new cdk.FnGetAtt("S3Bucket", "Arn"); diff --git a/docs/src/reference.rst.template b/docs/src/reference.rst.template index 993dbc4a43ac0..187f7baba5719 100644 --- a/docs/src/reference.rst.template +++ b/docs/src/reference.rst.template @@ -16,4 +16,3 @@ Reference .. toctree:: :maxdepth: 1 - diff --git a/docs/src/region-note.rst b/docs/src/region-note.rst deleted file mode 100644 index 9dc7e361dc061..0000000000000 --- a/docs/src/region-note.rst +++ /dev/null @@ -1,16 +0,0 @@ -.. Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 - International License (the "License"). You may not use this file except in compliance with the - License. A copy of the License is located at http://creativecommons.org/licenses/by-nc-sa/4.0/. - - This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - either express or implied. See the License for the specific language governing permissions and - limitations under the License. - -.. note:: - - The China regions (cn-north-1 and cn-northwest-1) do not support version reporting. - - See :ref:`version_reporting` for more details and how to - :ref:`opt-out ` diff --git a/docs/src/stacks.rst b/docs/src/stacks.rst deleted file mode 100644 index fcd356ce9eef0..0000000000000 --- a/docs/src/stacks.rst +++ /dev/null @@ -1,53 +0,0 @@ -.. Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 - International License (the "License"). You may not use this file except in compliance with the - License. A copy of the License is located at http://creativecommons.org/licenses/by-nc-sa/4.0/. - - This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - either express or implied. See the License for the specific language governing permissions and - limitations under the License. - -.. _stacks: - -###### -Stacks -###### - -A |stack| is an |cdk| construct that can be deployed into an AWS environment. -The combination of region and account becomes the stack's *environment*, as -described in :ref:`environments`. Most production apps consist of multiple stacks of -resources that are deployed as a single transaction using a resource -provisioning service like |CFN|. Any resources added directly or indirectly as -children of a stack are included in the stack's template as it is synthesized by -your |cdk| program. - -Define an application stack by extending the |stack-class| class, as -shown in the following example. - -.. code-block:: js - - import { Stack, StackProps } from '@aws-cdk/cdk' - - interface MyStackProps extends StackProps { - encryptedStorage: boolean; - } - - class MyStack extends Stack { - constructor(parent: Construct, name: string, props?: MyStackProps) { - super(parent, name, props); - - new MyStorageLayer(this, 'Storage', { encryptedStorage: props.encryptedStorage }); - new MyControlPlane(this, 'CPlane'); - new MyDataPlane(this, 'DPlane'); - } - } - -And then, add instances of this class to your app: - -.. code-block:: js - - const app = new App(); - - new MyStack(app, 'NorthAmerica', { env: { region: 'us-east-1' } }); - new MyStack(app, 'Europe', { env: { region: 'us-west-2' } }); diff --git a/docs/src/tools.rst b/docs/src/tools.rst deleted file mode 100644 index 238453ee3b31e..0000000000000 --- a/docs/src/tools.rst +++ /dev/null @@ -1,431 +0,0 @@ -.. Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 - International License (the "License"). You may not use this file except in compliance with the - License. A copy of the License is located at http://creativecommons.org/licenses/by-nc-sa/4.0/. - - This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - either express or implied. See the License for the specific language governing permissions and - limitations under the License. - -.. _tools: - -########### -|cdk| Tools -########### - -.. _toolkit: - -Command-line Toolkit (cdk) -========================== - -``cdk`` (the |toolkit|) is the main tool you use to interact with your |*CDK App*|. It executes -the CDK app you have written and compiled, interrogates the application -model you have defined, and produces and deploys the |CFN| templates -generated by the |cdk|. - -There are two ways that you can tell ``cdk`` what command to use to run your CDK app. -The first way is to include an explicit ``--app`` option whenever you use a ``cdk`` command: - -.. code-block:: sh - - cdk --app 'node bin/main.js' synth - -The second way is to add the following entry to -the file *cdk.json*: - -.. code-block:: javascript - - { - "app": "node bin/main.js" - } - -Here are the actions you can take on your CDK app -(this is the output of the ``cdk --help`` command): - -.. code-block:: sh - - Usage: cdk -a COMMAND - - Commands: - list Lists all stacks in the app [aliases: ls] - synthesize [STACKS..] Synthesizes and prints the CloudFormation template - for this stack [aliases: synth] - bootstrap [ENVIRONMENTS..] Deploys the CDK toolkit stack into an AWS - environment - deploy [STACKS..] Deploys the stack(s) named STACKS into your AWS - account - destroy [STACKS..] Destroy the stack(s) named STACKS - diff [STACK] Compares the specified stack with the deployed - stack or a local template file - metadata [STACK] Returns all metadata associated with this stack - init [TEMPLATE] Create a new, empty CDK project from a template. - Invoked without TEMPLATE, the app template will be - used. - docs Opens the documentation in a browser[aliases: doc] - doctor Check your set-up for potential problems - - Options: - --app, -a REQUIRED: Command-line for executing your CDK app (e.g. - "node bin/my-app.js") [string] - --context, -c Add contextual string parameter. [array] - --plugin, -p Name or path of a node package that extend the CDK - features. Can be specified multiple times [array] - --rename Rename stack name if different from the one defined in - the cloud executable [string] - --trace Print trace for stack warnings [boolean] - --strict Do not construct stacks with warnings [boolean] - --ignore-errors Ignores synthesis errors, which will likely produce an - invalid output [boolean] [default: false] - --json, -j Use JSON output instead of YAML [boolean] - --verbose, -v Show debug logs [boolean] - --profile Use the indicated AWS profile as the default environment - [string] - --proxy Use the indicated proxy. Will read from HTTPS_PROXY - environment variable if not specified. [string] - --ec2creds, -i Force trying to fetch EC2 instance credentials. Default: - guess EC2 instance status. [boolean] - --version-reporting Disable insersion of the CDKMetadata resource in - synthesized templates [boolean] - --role-arn, -r ARN of Role to use when invoking CloudFormation [string] - --version Show version number [boolean] - --help Show help [boolean] - - If your app has a single stack, there is no need to specify the stack name - - If one of cdk.json or ~/.cdk.json exists, options specified there will be used - as defaults. Settings in cdk.json take precedence. - -.. _config-files: - -Configuration -============= - -The CDK toolkit resolves its configuration by reading files in the following order: - -1. ``~/.cdk.json``: user-level configuration file -2. ``cdk.json``: project configuration file -3. Command line arguments - -The following options are supported in **cdk.json**: - -* ``app`` (string): the command-line to use in order to invoke your CDK app. -* ``browser`` (string): the command to use to open the browser, using %u as a placeholder for the path of the file to open -* ``context`` (hash): key-value pairs of context values which can later be read by ``Construct.getContext(key)`` -* ``language`` (string): programming langauge to use for **cdk-init** -* ``pathMetadata`` (boolean): Include "aws:cdk:path" CloudFormation metadata for each resource (enabled by default) -* ``plugin`` (array): Name or path of a node package that extend the CDK features -* ``requireApproval`` (string): what security-sensitive changes need manual approval (choices: "never", "any-change", "broadening") -* ``toolkitStackName`` (string): the name of the CDK toolkit stack -* ``versionReporting`` (boolean): Include the "AWS::CDK::Metadata" resource in synthesized templates - -.. _security-changes: - -Security-related changes -======================== - -In order to protect you against unintended changes that affect your security posture, -the CDK toolkit will prompt you to approve security-related changes before deploying -them. - -You change the level of changes that requires approval by specifying: - -.. code-block:: - - cdk deploy --require-approval LEVEL - -Where ``LEVEL`` can be one of: - -* ``never`` - approval is never required. -* ``any-change`` - require approval on any IAM or security-group related change. -* ``broadening`` (default) - require approval when IAM statements or traffic rules are added. Removals - do not require approval. - -The setting also be configured in **cdk.json**: - -.. code-block:: js - - { - "app": "...", - "requireApproval": "never" - } - -.. _version-reporting: - -Version Reporting -================= - -In order to gain insights in how the |cdk| is used, the versions of libraries used by |cdk| applications are collected -and reported using a resource identified as ``AWS::CDK::Metadata`` that is added to CloudFormation templates, and can easily -be reviewed. This information may also be used to identify stacks using a package with known serious security or -reliability issues and contact their users with important information. - -The |cdk| reports the name and version of npm modules that are loaded into the application at synthesis time, unless -their ``package.json`` file contains the ``"private": true`` attribute. - -The ``AWS::CDK::Metadata`` resource looks like the following: - -.. code-block:: yaml - - CDKMetadata: - Type: "AWS::CDK::Metadata" - Properties: - Modules: "@aws-cdk/core=0.7.2-beta,@aws-cdk/s3=0.7.2-beta,lodash=4.17.10" - -.. _version_reporting_opt_out: - -Opting-out from Version Reporting ---------------------------------- - -To out-out, use one of the following methods: - -* Use the ``--no-version-reporting`` in ``cdk`` invocations: - - .. code-block:: sh - - cdk --no-version-reporting synth - -* Set ``versionReporting`` to ``false`` in **./cdk.json** or **~/cdk.json**: - - .. code-block:: js - - { - "app": "...", - "versionReporting": false - } - -Plugins -======= - -The |cdk| toolkit provides extension points that enable users to augment the features provided by -the toolkit. There is currently only one extension point, allowing users to define custom AWS -credential providers, but other extension points may be added in the future as the needs arise. - -Loading Plugins ---------------- - -Plugins can be loaded by providing the Node module name (or path) to the CDK toolkit: - -1. Using the ``--plugin`` command line option (which can be specified multiple times): - - .. code-block:: console - - $ cdk list --plugin=module - $ cdk deploy --plugin=module_1 --plugin=module_2 - -2. Adding entries to the ``~/.cdk.json`` or ``cdk.json`` file: - - .. code-block:: js - - { - // ... - "plugin": [ - "module_1", - "module_2" - ], - // ... - } - -Authoring Plugins ------------------ - -Plugins must be authored in TypeScript or Javascript, and are defined by implementing a Node module -that implements the following protocol, and using :js:class:`~aws-cdk.PluginHost` methods: - -.. tabs:: - .. code-tab:: typescript - - import cdk = require('aws-cdk'); - - export = { - version: '1', // Version of the plugin infrastructure (currently always '1') - init(host: cdk.PluginHost): void { - // Your plugin initialization hook. - // Use methods of ``host`` to register custom code with the CDK toolkit - } - }; - - .. code-tab:: javascript - - export = { - version: '1', // Version of the plugin infrastructure (currently always '1') - init(host) { - // Your plugin initialization hook. - // Use methods of ``host`` to register custom code with the CDK toolkit - } - }; - -Credential Provider Plugins -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Custom credential providers are classes implementing the -:js:class:`~aws-cdk.CredentialProviderSource` interface, and registered to the toolkit using -the :js:func:`~aws-cdk.PluginHost.registerCredentialProviderSource` method. - -.. tabs:: - .. code-tab:: typescript - - import cdk = require('aws-cdk'); - import aws = require('aws-sdk'); - - class CustomCredentialProviderSource implements cdk.CredentialProviderSource { - public async isAvailable(): Promise { - // Return ``false`` if the plugin could determine it cannot be used (for example, - // if it depends on files that are not present on this host). - return true; - } - - public async canProvideCredentials(accountId: string): Promise { - // Return ``false`` if the plugin is unable to provide credentials for the - // requested account (for example if it's not managed by the credentials - // system that this plugin adds support for). - return true; - } - - public async getProvider(accountId: string, mode: cdk.Mode): Promise { - let credentials: aws.Credentials; - // Somehow obtain credentials in ``credentials``, and return those. - return credentials; - } - } - - export = { - version = '1', - init(host: cdk.PluginHost): void { - // Register the credential provider to the PluginHost. - host.registerCredentialProviderSource(new CustomCredentialProviderSource()); - } - }; - - .. code-tab:: javascript - - class CustomCredentialProviderSource { - async isAvailable() { - // Return ``false`` if the plugin could determine it cannot be used (for example, - // if it depends on files that are not present on this host). - return true; - } - - async canProvideCredentials(accountId) { - // Return ``false`` if the plugin is unable to provide credentials for the - // requested account (for example if it's not managed by the credentials - // system that this plugin adds support for). - return true; - } - - async getProvider(accountId, mode) { - let credentials; - // Somehow obtain credentials in ``credentials``, and return those. - return credentials; - } - } - - export = { - version = '1', - init(host) { - // Register the credential provider to the PluginHost. - host.registerCredentialProviderSource(new CustomCredentialProviderSource()); - } - }; - -Note that the credentials obtained from the providers for a given account and mode will be cached, -and as a consequence it is strongly advised that the credentials objects returned are self-refreshing, -as descibed in the `AWS SDK for Javascript documentation `_. - -Reference ---------- - -.. js:module:: aws-cdk - -CredentialProviderSource *(interface)* -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. js:class:: CredentialProviderSource - - .. js:attribute:: name - - A friendly name for the provider, which will be used in error messages, for example. - - :type: ``string`` - - .. js:method:: isAvailable() - - Whether the credential provider is even online. Guaranteed to be called before any of the other functions are called. - - :returns: ``Promise`` - - .. js:method:: canProvideCredentials(accountId) - - Whether the credential provider can provide credentials for the given account. - - :param string accountId: the account ID for which credentials are needed. - :returns: ``Promise`` - - .. js:method:: getProvider(accountId, mode) - - Construct a credential provider for the given account and the given access mode. - Guaranteed to be called only if canProvideCredentails() returned true at some point. - - :param string accountId: the account ID for which credentials are needed. - :param aws-cdk.Mode mode: the kind of operations the credentials are intended to perform. - :returns: ``Promise`` - -Mode *(enum)* -^^^^^^^^^^^^^ - -.. js:class:: Mode - - .. js:data:: ForReading - - Credentials are inteded to be used for read-only scenarios. - - .. js:data:: ForWriting - - Credentials are intended to be used for read-write scenarios. - -Plugin *(interface)* -^^^^^^^^^^^^^^^^^^^^ - -.. js:class:: Plugin() - - .. js:attribute:: version - - The version of the plug-in interface used by the plug-in. This will be used by - the plug-in host to handle version changes. Currently, the only valid value for - this attribute is ``'1'``. - - :type: ``string`` - - .. js:method:: init(host) - - When defined, this function is invoked right after the plug-in has been loaded, - so that the plug-in is able to initialize itself. It may call methods of the - :js:class:`~aws-cdk.PluginHost` instance it receives to register new - :js:class:`~aws-cdk.CredentialProviderSource` instances. - - :param aws-cdk.PluginHost host: The |cdk| plugin host - :returns: ``void`` - -PluginHost -^^^^^^^^^^ - -.. js:class:: PluginHost() - - .. js:data:: instance - - :type: :js:class:`~aws-cdk.PluginHost` - - .. js:method:: load(moduleSpec) - - Loads a plug-in into this PluginHost. - - :param string moduleSpec: the specification (path or name) of the plug-in module to be loaded. - :throws Error: if the provided ``moduleSpec`` cannot be loaded or is not a valid :js:class:`~aws-cdk.Plugin`. - :returns: ``void`` - - .. js:method:: registerCredentialProviderSource(source) - - Allows plug-ins to register new :js:class:`~aws-cdk.CredentialProviderSources`. - - :param aws-cdk.CredentialProviderSources source: a new CredentialProviderSource to register. - :returns: ``void`` diff --git a/docs/src/tutorial.rst b/docs/src/tutorial.rst deleted file mode 100644 index 39befa9b408b1..0000000000000 --- a/docs/src/tutorial.rst +++ /dev/null @@ -1,988 +0,0 @@ -.. Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 - International License (the "License"). You may not use this file except in compliance with the - License. A copy of the License is located at http://creativecommons.org/licenses/by-nc-sa/4.0/. - - This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - either express or implied. See the License for the specific language governing permissions and - limitations under the License. - -.. _tutorial: - -####################################### -Tutorial: Creating an |cdk| Application -####################################### - -This topic walks you through creating and deploying an |cdk| app, -by using the `cdk init` command, as described in the -`Creating a Project from the Template`_ section, -or manually, as described in the -`Creating a Project Manually`_ section. - -In either case, the first step is to create the directory for your project, -with an empty Git repository. -All of these instructions use this directory: - -.. code-block:: sh - - mkdir hello-cdk - cd hello-cdk - git init - -.. _template_create_project: - -Creating a Project from the Template -==================================== - -In this section we use the :code:`cdk init` command to create a skeleton project from a -template in any of the supported languages. - -.. _template_initialize: - -Initializing the Project ------------------------- - -Run the `cdk init` command to initialize an empty project. -The |cdk| contains templates for all of the supported languages. -To create an empty (no AWS resources in the resulting |CFN| stack), -run the following command, where LANGUAGE is one of the supported programming languages: -**csharp** (C#), **java** (Java), or **typescript** (TypeScript). - -.. code-block:: sh - - cdk init --language LANGUAGE - -.. _template_compile: - -Compiling the Project ---------------------- - -If needed, compile the code: - -.. tabs:: - - .. group-tab:: C# - - Compile the code using your IDE or via the dotnet CLI: - - .. code-block:: sh - - dotnet build - - .. group-tab:: JavaScript - - No need to compile - - .. group-tab:: TypeScript - - To compile your program from **.ts** to **.js**: - - .. code-block:: sh - - npm run build - - You can also use the **watch** command to continuously compile your code - as it changes, so you don't have to invoke the compiler explicitly: - - .. code-block:: sh - - # run in another terminal session - npm run watch - - .. group-tab:: Java - - Compile your code using your IDE or via the command line via **mvn**: - - .. code-block:: sh - - mvn compile - -You have now created your first |cdk| app. -The next section creates a similar app manually, -so you can skip it and continue with the -`Listing the Stacks in the App`_ section. - -.. _manual_create_project: - -Creating a Project Manually -=========================== - -In this section we create a new |cdk| project using explicit command-line commands. -Be sure to navigate to the *hello-cdk* directory before you start. - -.. _manual_initialize: - -Initializing the Project ------------------------- - -Create an empty project for the |cdk| app. - -.. tabs:: - - .. group-tab:: C# - - Create a new console application. - - .. code-block:: sh - - dotnet new console - - .. group-tab:: JavaScript - - Create an initial npm **package.json** file: - - .. code-block:: sh - - npm init -y # creates package.json - - Create a **.gitignore** file with the following content: - - .. code-block:: sh - - *.js - node_modules - - .. group-tab:: TypeScript - - Create an initial npm **package.json** file: - - .. code-block:: sh - - npm init -y # creates package.json - - Create a **.gitignore** file with the following content: - - .. code-block:: sh - - *.js - *.d.ts - node_modules - - Add the `build` and `watch` TypeScript commands to **package.json**: - - .. code-block:: json - - { - "scripts": { - "build": "tsc", - "watch": "tsc -w" - } - } - - Create a minimal **tsconfig.json**: - - .. code-block:: json - - { - "compilerOptions": { - "target": "es2018", - "module": "commonjs" - } - } - - Create a minimal **cdk.json** (this saves you from including `--app node bin/hello-cdk.js` in every `cdk` command): - - .. code-block:: json - - { - "app": "node bin/hello-cdk.js" - } - - .. group-tab:: Java - - Create a **.gitignore** file with the following content: - - .. code-block:: sh - - .classpath.txt - target - .classpath - .project - .idea - .settings - .vscode - *.iml - - Use your favorite IDE to create a Maven-based empty Java 8 project. - - Set the Java **source** and **target** to 1.8 in your **pom.xml** file: - - .. code-block:: xml - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.1 - - 1.8 - 1.8 - - - - - -.. _manual_add_core: - -Adding the CDK Core as a Dependency ------------------------------------ - -Install the |cdk| core library (:py:mod:`@aws-cdk/cdk`) -This library includes the basic classes needed to write |cdk| stacks and apps. - -.. tabs:: - - .. group-tab:: C# - - Install the **Amazon.CDK NuGet** package: - - .. code-block:: sh - - dotnet add package Amazon.CDK - - .. group-tab:: JavaScript - - Install the **@aws-cdk/cdk** package: - - .. code-block:: sh - - npm install @aws-cdk/cdk - - .. group-tab:: TypeScript - - Install the **@aws-cdk/cdk** package: - - .. code-block:: sh - - npm install @aws-cdk/cdk - - .. group-tab:: Java - - Add the following to your project's `pom.xml` file: - - .. code-block:: xml - - - - software.amazon.awscdk - cdk - - - - -.. _manual_define_app: - -Defining the |cdk| App ----------------------- - -|cdk| apps are classes that extend the :py:class:`App <@aws-cdk/cdk.App>` -class. Create an empty **App**: - -.. tabs:: - - .. group-tab:: C# - - In **Program.cs** - - .. code-block:: c# - - using Amazon.CDK; - - namespace HelloCdk - { - class Program - { - static void Main(string[] args) - { - var myApp = new App(); - myApp.Run(); - } - } - } - - .. group-tab:: JavaScript - - Create the file **bin/hello-cdk.js**: - - .. code-block:: js - - const cdk = require('@aws-cdk/cdk'); - - class MyApp extends cdk.App { - constructor() { - super(); - } - } - - new MyApp().run(); - - .. group-tab:: TypeScript - - Create the file **bin/hello-cdk.ts**: - - .. code-block:: ts - - import cdk = require('@aws-cdk/cdk'); - import { HelloCdkStack } from '../lib/hello-cdkstack'; - - const app = new cdk.App(); - new HelloCdkStack(app, 'HelloCdkStack'); - app.run(); - - Create the file **lib/hello-cdkstack.ts**: - - .. code-block:: ts - - import cdk = require('@aws-cdk/cdk'); - - export class HelloCdkStack extends cdk.Stack { - constructor(parent: cdk.App, name: string, props?: cdk.StackProps) { - super(parent, name, props); - - // The code that defines your stack goes here - } - } - - .. group-tab:: Java - - In **src/main/java/com/acme/MyApp.java**: - - .. code-block:: java - - package com.acme; - - import software.amazon.awscdk.App; - - import java.util.Arrays; - - public class MyApp { - public static void main(final String argv[]) { - App app = new App(); - - app.run(); - } - } - -.. _manual_compile: - -Compiling the Code ------------------- - -If needed, compile the code: - -.. tabs:: - - .. group-tab:: C# - - Compile the code using your IDE or via the dotnet CLI: - - .. code-block:: sh - - dotnet build - - .. group-tab:: JavaScript - - No need to compile - - .. group-tab:: TypeScript - - To compile your program from **.ts** to **.js**: - - .. code-block:: sh - - npm run build - - You can also use the **watch** command to continuously compile your code - as it changes, so you don't have to invoke the compiler explicitly: - - .. code-block:: sh - - # run in another terminal session - npm run watch - - .. group-tab:: Java - - Compile your code using your IDE or via the command line via **mvn**: - - .. code-block:: sh - - mvn compile - -You have now created your first |cdk| app. - -.. _list_stacks: - -Listing the Stacks in the App -============================= - -Use the |cdk| toolkit's **ls** command to list the stacks in the app. - -.. code-block:: sh - - cdk ls - -The result is just the name of the stack: - -.. code-block:: sh - - HelloCdkStack - -.. note:: - - There is a known issue on Windows with the |cdk| .NET environment. - Whenever you use a **cdk** command, - it issues a node warning similar to the following: - - .. code-block:: sh - - (node:27508) UnhandledPromiseRejectionWarning: Unhandled promise rejection - (rejection id: 1): Error: EPIPE: broken pipe, write - (node:27508) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. - In the future, promise rejections that are not handled will terminate the - Node.js process with a non-zero exit code. - - You can safely ignore this warning. - -.. _define_stack: - -Define a Stack -============== - -Define a stack and add it to the app. - -.. tabs:: - - .. group-tab:: C# - - Create **MyStack.cs**: - - .. code-block:: c# - - using Amazon.CDK; - - namespace HelloCdk - { - public class MyStack: Stack - { - public MyStack(App parent, string name) : base(parent, name, null) - { - } - } - } - - In **Program.cs**: - - .. code-block:: c# - :emphasize-lines: 10 - - using Amazon.CDK; - - namespace HelloCdk - { - class Program - { - static void Main(string[] args) - { - var myApp = new App(); - new MyStack(myApp, "hello-cdk"); - myApp.Run(); - } - } - } - - .. group-tab:: JavaScript - - In **index.js**: - - .. code-block:: js - :emphasize-lines: 3,4,5,6,7,13 - - const cdk = require('@aws-cdk/cdk'); - - class MyStack extends cdk.Stack { - constructor(parent, id, props) { - super(parent, id, props); - } - } - - class MyApp extends cdk.App { - constructor(argv) { - super(argv); - - new MyStack(this, 'hello-cdk'); - } - } - - new MyApp().run(); - - .. group-tab:: TypeScript - - Nothing to do. - - .. group-tab:: Java - - In **src/main/java/com/acme/MyStack.java**: - - .. code-block:: java - - package com.acme; - - import software.amazon.awscdk.App; - import software.amazon.awscdk.Stack; - import software.amazon.awscdk.StackProps; - - public class MyStack extends Stack { - public MyStack(final App parent, final String name) { - this(parent, name, null); - } - - public MyStack(final App parent, final String name, final StackProps props) { - super(parent, name, props); - } - } - - In **src/main/java/com/acme/MyApp.java**: - - .. code-block:: java - :emphasize-lines: 12 - - package com.acme; - - import software.amazon.awscdk.App; - import java.util.Arrays; - - public class MyApp { - public static void main(final String argv[]) { - App app = new App(); - - new MyStack(app, "hello-cdk"); - - app.run(); - } - } - -The initializer signature of **cdk.Stack** includes the arguments: **parent**, -**id**, and **props**. This is the signature for every class in the |cdk| -framework. These classes are called **"constructs"** and they are composed -together into a tree: - -* **parent** represents the parent construct. By specifying the parent construct - upon initialization, constructs can obtain contextual information when they - are initialized. For example, the region a stack is deployed to can be - obtained via a call to :py:meth:`Stack.find(this).requireRegion() <@aws-cdk/cdk.Stack.requireRegion>`. - See :doc:`context` for more information. -* **id** is a string that locally identifies this construct within the tree. - Constructs must have a unique ID amongst their siblings. -* **props** is the set of initialization properties for this construct. - -Compile your program: - -.. tabs:: - - .. group-tab:: C# - - We configured *cdk.json* to run `dotnet run`, which - restores dependencies, builds, and runs your application, - run `cdk`. - - .. code-block:: sh - - cdk - - .. group-tab:: JavaScript - - Nothing to compile. - - .. group-tab:: TypeScript - - .. code-block:: sh - - npm run build - - .. group-tab:: Java - - .. code-block:: sh - - mvn compile - -.. _define_bucket: - -Define an |S3| Bucket -===================== - -Now, what can we do with this app? Nothing yet. Our stack is still empty, so -there's nothing to deploy. - -Let's define an |S3| bucket. - -Install the **@aws-cdk/aws-s3** package: - -.. tabs:: - - .. group-tab:: C# - - .. code-block:: sh - - dotnet add package Amazon.CDK.AWS.S3 - - .. group-tab:: JavaScript - - .. code-block:: sh - - npm install @aws-cdk/aws-s3 - - .. group-tab:: TypeScript - - .. code-block:: sh - - npm install @aws-cdk/aws-s3 - - .. group-tab:: Java - - Edit your **pom.xml** file: - - .. code-block:: sh - - - software.amazon.awscdk - s3 - - - -Next, define an |S3| bucket in the stack. |S3| buckets are represented by -the :py:class:`Bucket <@aws-cdk/aws-s3.Bucket>` class: - -.. tabs:: - - .. group-tab:: C# - - Create **MyStack.cs**: - - .. code-block:: c# - :emphasize-lines: 2,10,11,12,13 - - using Amazon.CDK; - using Amazon.CDK.AWS.S3; - - namespace HelloCdk - { - public class MyStack : Stack - { - public MyStack(App parent, string name) : base(parent, name, null) - { - new Bucket(this, "MyFirstBucket", new BucketProps - { - Versioned = true - }); - } - } - } - - .. group-tab:: JavaScript - - In **index.js**: - - .. code-block:: js - :emphasize-lines: 2,8,9,10 - - const cdk = require('@aws-cdk/cdk'); - const s3 = require('@aws-cdk/aws-s3'); - - class MyStack extends cdk.Stack { - constructor(parent, id, props) { - super(parent, id, props); - - new s3.Bucket(this, 'MyFirstBucket', { - versioned: true - }); - } - } - - .. group-tab:: TypeScript - - In **lib/**: - - .. code-block:: ts - :emphasize-lines: 2,8,9,10 - - import cdk = require('@aws-cdk/cdk'); - import s3 = require('@aws-cdk/aws-s3'); - - export class HelloCdkStack extends cdk.Stack { - constructor(parent: cdk.App, id: string, props?: cdk.StackProps) { - super(parent, id, props); - - new s3.Bucket(this, 'MyFirstBucket', { - versioned: true - }); - } - } - - .. group-tab:: Java - - In **src/main/java/com/acme/MyStack.java**: - - .. code-block:: java - :emphasize-lines: 6,7,13,14,15 - - package com.acme; - - import software.amazon.awscdk.App; - import software.amazon.awscdk.Stack; - import software.amazon.awscdk.StackProps; - import software.amazon.awscdk.services.s3.Bucket; - import software.amazon.awscdk.services.s3.BucketProps; - - public class MyStack extends Stack { - public MyStack(final App parent, final String name) { - this(parent, name, null); - } - - public MyStack(final App parent, final String name, final StackProps props) { - super(parent, name, props); - - new Bucket(this, "MyFirstBucket", BucketProps.builder() - .withVersioned(true) - .build()); - } - } - -A few things to notice: - -* :py:class:`Bucket <@aws-cdk/aws-s3.Bucket>` is a construct. - This means it's initialization signature has **parent**, **id**, and **props**. - In this case, the bucket is an immediate child of **MyStack**. -* ``MyFirstBucket`` is the **logical id** of the bucket construct, **not** the physical name of the - S3 bucket. The logical ID is used to uniquely identify resources in your stack - across deployments. See :doc:`logical-ids` for more details on how to work - with logical IDs. To specify a physical name for your bucket, you can set the - :py:meth:`bucketName <@aws-cdk/aws-s3.BucketProps.bucketName>` property when - you define your bucket. -* Since the bucket's :py:meth:`versioned <@aws-cdk/aws-s3.BucketProps.versioned>` - property is :code:`true`, `versioning `_ - is enabled on the bucket. - -Compile your program: - -.. tabs:: - - .. group-tab:: C# - - We configured *cdk.json* to run `dotnet run`, which - restores dependencies, builds, and runs your application, - run `cdk`. - - .. group-tab:: JavaScript - - Nothing to compile. - - .. group-tab:: TypeScript - - .. code-block:: sh - - npm run build - - .. group-tab:: Java - - .. code-block:: sh - - mvn compile - -.. _synthesize_template: - -Synthesize an |CFN| Template -============================ - -Synthesize a |cfn| template for the stack: - -.. code-block:: sh - - cdk synth HelloCdkStack - -.. note:: Since the |cdk| app only contains a single stack, you can omit :code:`HelloCdkStack`. - -This command executes the |cdk| app and synthesize an |CFN| template for the -**HelloCdkStack** stack. -You should see something similar to the following, -where VERSION is the version of the |cdk|. - -.. code-block:: yaml - - Resources: - MyFirstBucketB8884501: - Type: AWS::S3::Bucket - Properties: - VersioningConfiguration: - Status: Enabled - Metadata: - aws:cdk:path: HelloCdkStack/MyFirstBucket/Resource - CDKMetadata: - Type: AWS::CDK::Metadata - Properties: - Modules: "@aws-cdk/aws-codepipeline-api=VERSION,@aws-cdk/aws-events=VERSION,@aws-c\ - dk/aws-iam=VERSION,@aws-cdk/aws-kms=VERSION,@aws-cdk/aws-s3=VERSION,@aws-c\ - dk/aws-s3-notifications=VERSION,@aws-cdk/cdk=VERSION,@aws-cdk/cx-api=VERSION\ - .0,hello-cdk=0.1.0" - -You can see that the stack contains an **AWS::S3::Bucket** resource with the desired -versioning configuration. - -.. note:: - - The **AWS::CDK::Metadata** resource was automatically added to your template - by the toolkit. This allows us to learn which libraries were used in your - stack. See :ref:`version_reporting` for more details and how to - :ref:`opt-out `. - -.. _deploy_stack: - -Deploying the Stack -=================== - -Use **cdk deploy** to deploy the stack: - -.. code-block:: sh - - cdk deploy - -The **deploy** command synthesizes an |CFN| template from the stack -and then invokes the |CFN| create/update API to deploy it into your AWS -account. The command displays information as it progresses. - -.. _modify_cde: - -Modifying the Code -================== - -Configure the bucket to use KMS managed encryption: - -.. tabs:: - - .. group-tab:: C# - - .. code-block:: c# - :emphasize-lines: 4 - - new Bucket(this, "MyFirstBucket", new BucketProps - { - Versioned = true, - Encryption = BucketEncryption.KmsManaged - }); - - .. group-tab:: JavaScript - - .. code-block:: js - :emphasize-lines: 3 - - new s3.Bucket(this, 'MyFirstBucket', { - versioned: true, - encryption: s3.BucketEncryption.KmsManaged - }); - - .. group-tab:: TypeScript - - .. code-block:: ts - :emphasize-lines: 3 - - new s3.Bucket(this, 'MyFirstBucket', { - versioned: true, - encryption: s3.BucketEncryption.KmsManaged - }); - - .. group-tab:: Java - - .. code-block:: java - :emphasize-lines: 3 - - new Bucket(this, "MyFirstBucket", BucketProps.builder() - .withVersioned(true) - .withEncryption(BucketEncryption.KmsManaged) - .build()); - -Compile the program: - -.. tabs:: - - .. group-tab:: C# - - We configured *cdk.json* to run `dotnet run`, which - restores dependencies, builds, and runs your application, - run `cdk`. - - .. group-tab:: JavaScript - - Nothing to compile. - - .. group-tab:: TypeScript - - .. code-block:: sh - - npm run build - - .. group-tab:: Java - - .. code-block:: sh - - mvn compile - -.. _prepare_deployment: - -Preparing for Deployment -======================== - -Before you deploy the updated stack, use the ``cdk diff`` command to evaluate -the difference between the |cdk| app and the deployed stack: - -.. code-block:: sh - - cdk diff - -The toolkit queries your AWS account for the current |CFN| template for the -**hello-cdk** stack, and compares the result with the template synthesized from the app. -The output should look like the following: - -.. code-block:: sh - - [~] πŸ›  Updating MyFirstBucketB8884501 (type: AWS::S3::Bucket) - └─ [+] .BucketEncryption: - └─ New value: {"ServerSideEncryptionConfiguration":[{"ServerSideEncryptionByDefault":{"SSEAlgorithm":"aws:kms"}}]} - -As you can see, the diff indicates that the -**ServerSideEncryptionConfiguration** property of the bucket is now set to -enable server-side encryption. - -You can also see that the bucket is not going to be replaced but rather updated -("**Updating MyFirstBucketB8884501**"). - -Run **cdk deploy** to update the stack: - -.. code-block:: sh - - cdk deploy - -The toolkit updates the bucket configuration to enable server-side KMS -encryption for the bucket: - -.. code-block:: sh - - ⏳ Starting deployment of stack hello-cdk... - [0/2] UPDATE_IN_PROGRESS [AWS::S3::Bucket] MyFirstBucketB8884501 - [1/2] UPDATE_COMPLETE [AWS::S3::Bucket] MyFirstBucketB8884501 - [1/2] UPDATE_COMPLETE_CLEANUP_IN_PROGRESS [AWS::CloudFormation::Stack] hello-cdk - [2/2] UPDATE_COMPLETE [AWS::CloudFormation::Stack] hello-cdk - βœ… Deployment of stack hello-cdk completed successfully - -.. _whats_next: - -What Next? -========== - - * Learn more about :doc:`CDK Concepts ` - * Check out the `examples directory `_ in your GitHub repository - * Learn about the rich APIs offered by the :doc:`AWS Construct Library ` - * Work directly with CloudFormation using the :doc:`AWS CloudFormation Library ` - * Come talk to us on `Gitter `_ - diff --git a/docs/src/writing-constructs.rst b/docs/src/writing-constructs.rst deleted file mode 100644 index 2f875e23be225..0000000000000 --- a/docs/src/writing-constructs.rst +++ /dev/null @@ -1,236 +0,0 @@ -.. Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 - International License (the "License"). You may not use this file except in compliance with the - License. A copy of the License is located at http://creativecommons.org/licenses/by-nc-sa/4.0/. - - This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - either express or implied. See the License for the specific language governing permissions and - limitations under the License. - -.. _writing_constructs: - -######################### -Writing |cdk| Constructs -######################### - -This topic provides some tips writing idiomatic new constructs for the |cdk|. -The tips here apply equally to constructs written for inclusion in the AWS -Construct Library, purpose-built constructs to achieve a well-defined goal, -or constructs that serve as building blocks for assembling your cloud -applications. - -General Design Priciples -======================== - -* Favor composition over inheritance; most of the constructs should directly - extend the **Construct** class instead of some other construct. Inheritance - should mainly be used to allow polymorphism. Typically, you'll add a child - construct and expose any of its APIs and properties in the parent construct. -* Provide defaults for everything that a reasonable guess can be made for; - ideally, props should be optional and **new MyAwesomeConstruct(this, "Foo")** - should be enough to set up a reasonable variant of the construct. This does - not mean that the user should not have the opportunity to customize! Rather, - it means that the specific parameter should be optional and set to a - reasonable value if not supplied. This may involve creating other resources as - part of initializing this construct. For example, all resources that require a - role allow passing in a **Role** object (specifically, a **RoleRef** object), - but if the user does not supply one an appropriate **Role** object is - defined in-place. -* Use contextual defaulting between properties; the value of one property may - affect sensible defaults for other properties. *Example*: - **enableDnsHostnames** and **enableDnsSupport**. **dnsHostnames** requires - **dnsSupport**, only throw an error if the user has *explicitly* disabled DNS - Support, but tried to enable DNS Hostnames. A user expects things to just - work. If their intention is clear, we should do the right thing. -* Make the user think about intent, not implementation detail; for example, - if establishing an association between two resources (such as a **Topic** - and a **Queue**) requires multiple steps (in this case, creating a - **Subscription** but also setting appropriate IAM permissions), make - both things happen in a single call (to **subscribeQueue()**). The user - should be mentally thinking about the result they're trying to achieve, - not the various steps necessary to get there. -* Make sure you are not renaming concepts or terminology. For example don't be - temped to replace SQS's **dataKeyReusePeriod** with **keyRotation** because it - will be hard for people to diagnose problems. They won't be able to just - search for *sqs dataKeyReuse* and find topics on it. It is sometimes okay - to introduce a new concept that does not have a counterpart in CloudFormation, - especially if it directly maps onto the mental model that users already - have about a service. -* Optimize for the common case; example: **AutoScalingGroup** accepts a **VPC** - and deploys in the private subnet by default because that's the common case, - but has an option to **placementOptions** for the special case. -* If a class can have multiple modes/behaviors: prefer values over polymorphism. - Try switching behavior on property values first. Switch to multiple classes - with a shared base class/interface only if there value to be had from having - multiple classes (type safety, maybe one mode has different features/required - parameters). - -Implementation Details -====================== - -* Every construct consists of an exported class (**MyConstruct**) and an - exported interface (**MyConstructProps**) that defines the parameters for - these classes. The props argument is the 3rd to the construct (after the - mandatory **parent** and **id** arguments), and the entire parameter should be - optional if all of the properties on the props object are optional. -* Most of the logic happens in the constructor; the constructor will build up - the state of the construct (what children it has, which ones are always - there and which ones are optional, etc.). -* Validate as early as possible; throw an `Error` in the constructor if the - parameters don't make sense. Only if you want to validate mutations that can - occur after construction time, override the `validate()` method. The hierarchy - of validation: - * Best: Incorrect code won't compile, because of type safety guarantees. - * Good: Runtime check everything the type checker can't enforce and fail - early (error in the constructor). - * Okay: Synth-time validate everything that can't be checked at construction - time (error in ``validate()``). - * Not great: Fail with an error in CloudFormation (bad because the - CloudFormation deploy operation may take a long while, and the error - may take several minutes to surface). - * Very bad: Fail with a timeout during CloudFormation deployment (it may take - up to an hour for resource stabilization to timeout!) - * Worst: Don't fail the deployment at all, but fail at runtime. -* Avoid unneeded hierarchy in props; try to keep the props interface flat to - help discoverability. -* Constructs are classes that have a set of invariants they maintain over their - lifetime (such as which members are initialized and relationships between - properties as members are mutated). -* Constructs mostly have write-only scalar properties that are passed in the - constructor, but mutating functions for collections (for example, there will - be ``construct.addElement()`` or ``construct.onEvent())`` functions, but not - ``construct.setProperty()``. It is perfectly fine to deviate from this - convention as it makes sense for your own constructs. -* Don't expose **Tokens** to your consumers; tokens are an implementation - mechanism for one of two purpose: representing CloudFormation intrinsic - values, or representing lazily evaluated values. They can be used for - implementation purposes, but use more specific types as part of your public - API. -* **Tokens** are (mostly) only used in the implementation of an L2 to pass lazy - values to other constructs. For example, if you have an array that can be - mutated during the lifetime of your class, you pass it to an L1 construct - like so: ``new cdk.Token(() => this.myList)``. -* Be aware that you might not be able to usefully inspect all strings. Any - string passed into your construct may contain special markers that represent - values that will only be known at deploy time (for example, the ARN of a - resource that will be created during deployment). Those are *stringified - Tokens* and they look like ``"${TOKEN[Token.123]}"``. You will not be able to - validate those against a regular expression, for example, as their "real" - values are not known yet. To figure out if your string contains a special - marker, use ``cdk.unresolved(string)``. -* Indicate units of measurement in property names that don't use a strong type. - Use **milli**, **sec**, **min**, **hr**, **Bytes**, **KiB**, **MiB**, **GiB**, - etc. -* Be sure to define an **IMyResource** interface for your resources which - defines the API area that other constructs are going to use. Typical - capabilities on this interface are querying for a resource ARN and adding - resource permissions. -* Accept objects instead of ARNs or names; when accepting other resources as - parameters, declare your property as **resource: IMyResource** instead of - **resourceArn: string**. This makes snapping objects together feel natural to - consumers, and allows you to query/modify the incoming resource as well. The - latter is particularly useful if something about IAM permissions need to be - set, for example. -* Implement **export()** and **import()** functions for your resource; these - make it possible to interoperate with resources that are not defined in the - same CDK app (they may be manually created, created using raw CloudFormation, - or created in a completely unrelated CDK app). -* If your construct wraps a single (or most prominent) other construct, give it - an id of either **"Resource"** or **"Default"**; The "main" resource that an - AWS Construct represents should use the ID **Resource**, for higher-level - wrapping resources you will generally use **Default** (resources named - **Default** will inherit their parent's logical ID, while resources named - **Resource** will have a distinct logical ID but the human-readable part of it - will not show the "Resource" part). - - -Implementation Language -======================= - -In order for construct libraries to be reusable across programming languages, -they, they need to be authored in a language that can compile to a jsii -assembly. - -At the moment, the only supported language is TypeScript, so prefer TypeScript -unless you are planning to specifically isolate your constructs to a single -developer base. - - -Code Organization -================= - -Your package will roughly look like this: - -.. code:: - - your-package - β”œβ”€β”€ package.json - β”œβ”€β”€ README.md - β”œβ”€β”€ lib - β”‚Β Β  β”œβ”€β”€ index.ts - β”‚Β Β  β”œβ”€β”€ some-resource.ts - β”‚Β Β  └── some-other-resource.ts - └── test - Β  β”œβ”€β”€ integ.everything.lit.ts - Β Β  β”œβ”€β”€ test.some-resource.ts - Β Β  └── test.some-other-resource.ts - -* Your package is named ``@aws-cdk/aws-xxx`` if it represents the canonical AWS - Construct Library for this service; otherwise we recommend starting with - ``cdk-``, but you are free to pick a pleasing name. -* Code goes under **lib/**, tests go under **test/**. -* Entry point should be **lib/index.ts** and should only contain ``export`` s - for other files. -* No need to put every class in a separate file. Try to think of a - reader-friendly organization of your source files. -* If you want to make package-private utility functions, put them in a file - that is *not exported* from **index.ts** and use that file as normal. -* Free-floating functions (functions that are not part of a class definition) - cannot be accessed through **jsii** (i.e., from languages other than - TypeScript and JavaScript). Don't use them for public features of your - construct library. -* Document all public APIs with doc comments (JSdoc syntax). Document defaults - using the **@default** marker in doc comments. - -Testing -======= - -* Add unit tests for every construct (**test.xxx.ts**), relating the construct's - properties to the CloudFormation that gets generated. Use the - **@aws-cdk/assert** library to make it easier to write assertions on the - CloudFormation output. -* Try to test one concern per unit test. Even if you *could* test more than one - feature of the construct per test, it's better to write multiple tests, - one for each feature. A test should have one reason to break. -* Add integration tests (**integ.xxx.ts**) that are basically just CDK apps - which exercise the features of the construct, then load your shell with - credentials and run ``npm run integ`` to exercise them. You will also have to - run this if the CloudFormation output of the construct changes. -* If there are packages that you only depend on for testing, add them to - **devDependencies** (instead of regular **dependencies**). You're still - not allowed to create dependency cycles this way (from the root, run - ``scripts/find-cycles.sh`` to figure out if you have created any cycles). -* Try to make your integ test literate (**integ.xxx.lit.ts**) if possible - and link to it from the README. - -README -====== - -* Header should include maturity level. -* Header should start at H2, not H1. -* Include some example code for the simple use case near the very top. -* If there are multiple common use cases, provide an example for each one and - describe what happens under the hood at a high level (e.g. which resources are - created). -* Reference docs are not needed. -* Use literate (.lit.ts) integration tests into README file. - -Construct IDs -=================== - -* All children's construct IDs are part of your public contract; those IDs are - used to generate CloudFormation logical names for resources. If they change, - CloudFormation will replace the resource. This technically means that if you - change any ID of a child construct you will have to major-version-bump your - library. From fc164338565f27e908ba0fe5872d50714a9c5026 Mon Sep 17 00:00:00 2001 From: Jonathan Goldwasser Date: Tue, 5 Feb 2019 00:59:51 +0100 Subject: [PATCH 33/38] feat(aws-s3): add option to specify block public access settings (#1664) Adds option to enable all block public access settings on the bucket (`blockPublicAccess`). Also throws an error when trying to use grantPublicAccess if blockPublicAccess is set to true. https://docs.aws.amazon.com/AmazonS3/latest/dev/access-control-block-public-access.html --- packages/@aws-cdk/aws-s3/README.md | 30 +++++++ packages/@aws-cdk/aws-s3/lib/bucket.ts | 80 ++++++++++++++++++- packages/@aws-cdk/aws-s3/test/test.bucket.ts | 83 ++++++++++++++++++++ 3 files changed, 192 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-s3/README.md b/packages/@aws-cdk/aws-s3/README.md index 3de0245cdb76e..63d581b63ea73 100644 --- a/packages/@aws-cdk/aws-s3/README.md +++ b/packages/@aws-cdk/aws-s3/README.md @@ -208,3 +208,33 @@ bucket.onEvent(s3.EventType.ObjectRemoved, myQueue, { prefix: 'foo/', suffix: '. ``` [S3 Bucket Notifications]: https://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html + + +### Block Public Access + +Use `blockPublicAccess` to specify [block public access settings] on the bucket. + +Enable all block public access settings: +```ts +const bucket = new Bucket(this, 'MyBlockedBucket', { + blockPublicAccess: BlockPublicAccess.BlockAll +}); +``` + +Block and ignore public ACLs: +```ts +const bucket = new Bucket(this, 'MyBlockedBucket', { + blockPublicAccess: BlockPublicAccess.BlockAcls +}); +``` + +Alternatively, specify the settings manually: +```ts +const bucket = new Bucket(this, 'MyBlockedBucket', { + blockPublicAccess: new BlockPublicAccess({ blockPublicPolicy: true }) +}); +``` + +When `blockPublicPolicy` is set to `true`, `grantPublicRead()` throws an error. + +[block public access settings]: https://docs.aws.amazon.com/AmazonS3/latest/dev/access-control-block-public-access.html diff --git a/packages/@aws-cdk/aws-s3/lib/bucket.ts b/packages/@aws-cdk/aws-s3/lib/bucket.ts index e10dedb078c2f..df76d4520044f 100644 --- a/packages/@aws-cdk/aws-s3/lib/bucket.ts +++ b/packages/@aws-cdk/aws-s3/lib/bucket.ts @@ -293,6 +293,11 @@ export abstract class BucketBase extends cdk.Construct implements IBucket { */ protected abstract autoCreatePolicy = false; + /** + * Whether to disallow public access + */ + protected abstract disallowPublicAccess?: boolean; + /** * Exports this bucket from the stack. */ @@ -514,6 +519,10 @@ export abstract class BucketBase extends cdk.Construct implements IBucket { * @returns The `iam.PolicyStatement` object, which can be used to apply e.g. conditions. */ public grantPublicAccess(keyPrefix = '*', ...allowedActions: string[]): iam.PolicyStatement { + if (this.disallowPublicAccess) { + throw new Error("Cannot grant public access when 'blockPublicPolicy' is enabled"); + } + allowedActions = allowedActions.length > 0 ? allowedActions : [ 's3:GetObject' ]; const statement = new iam.PolicyStatement() @@ -555,6 +564,62 @@ export abstract class BucketBase extends cdk.Construct implements IBucket { } } +export interface BlockPublicAccessOptions { + /** + * Whether to block public ACLs + * + * @see https://docs.aws.amazon.com/AmazonS3/latest/dev/access-control-block-public-access.html#access-control-block-public-access-options + */ + blockPublicAcls?: boolean; + + /** + * Whether to block public policy + * + * @see https://docs.aws.amazon.com/AmazonS3/latest/dev/access-control-block-public-access.html#access-control-block-public-access-options + */ + blockPublicPolicy?: boolean; + + /** + * Whether to ignore public ACLs + * + * @see https://docs.aws.amazon.com/AmazonS3/latest/dev/access-control-block-public-access.html#access-control-block-public-access-options + */ + ignorePublicAcls?: boolean; + + /** + * Whether to restrict public access + * + * @see https://docs.aws.amazon.com/AmazonS3/latest/dev/access-control-block-public-access.html#access-control-block-public-access-options + */ + restrictPublicBuckets?: boolean; +} + +export class BlockPublicAccess { + public static readonly BlockAll = new BlockPublicAccess({ + blockPublicAcls: true, + blockPublicPolicy: true, + ignorePublicAcls: true, + restrictPublicBuckets: true + }); + + public static readonly BlockAcls = new BlockPublicAccess({ + blockPublicAcls: true, + ignorePublicAcls: true + }); + + public blockPublicAcls: boolean | undefined; + public blockPublicPolicy: boolean | undefined; + public ignorePublicAcls: boolean | undefined; + public restrictPublicBuckets: boolean | undefined; + + constructor(options: BlockPublicAccessOptions) { + this.blockPublicAcls = options.blockPublicAcls; + this.blockPublicPolicy = options.blockPublicPolicy; + this.ignorePublicAcls = options.ignorePublicAcls; + this.restrictPublicBuckets = options.restrictPublicBuckets; + } +} + export interface BucketProps { /** * The kind of server-side encryption to apply to this bucket. @@ -623,6 +688,13 @@ export interface BucketProps { * Similar to calling `bucket.grantPublicAccess()` */ publicReadAccess?: boolean; + + /** + * The block public access configuration of this bucket. + * + * @see https://docs.aws.amazon.com/AmazonS3/latest/dev/access-control-block-public-access.html + */ + blockPublicAccess?: BlockPublicAccess; } /** @@ -652,6 +724,7 @@ export class Bucket extends BucketBase { public readonly encryptionKey?: kms.IEncryptionKey; public policy?: BucketPolicy; protected autoCreatePolicy = true; + protected disallowPublicAccess?: boolean; private readonly lifecycleRules: LifecycleRule[] = []; private readonly versioned?: boolean; private readonly notifications: BucketNotifications; @@ -666,7 +739,8 @@ export class Bucket extends BucketBase { bucketEncryption, versioningConfiguration: props.versioned ? { status: 'Enabled' } : undefined, lifecycleConfiguration: new cdk.Token(() => this.parseLifecycleConfiguration()), - websiteConfiguration: this.renderWebsiteConfiguration(props) + websiteConfiguration: this.renderWebsiteConfiguration(props), + publicAccessBlockConfiguration: props.blockPublicAccess }); cdk.applyRemovalPolicy(resource, props.removalPolicy !== undefined ? props.removalPolicy : cdk.RemovalPolicy.Orphan); @@ -678,6 +752,7 @@ export class Bucket extends BucketBase { this.domainName = resource.bucketDomainName; this.bucketWebsiteUrl = resource.bucketWebsiteUrl; this.dualstackDomainName = resource.bucketDualStackDomainName; + this.disallowPublicAccess = props.blockPublicAccess && props.blockPublicAccess.blockPublicPolicy; // Add all lifecycle rules (props.lifecycleRules || []).forEach(this.addLifecycleRule.bind(this)); @@ -1042,6 +1117,8 @@ class ImportedBucket extends BucketBase { public policy?: BucketPolicy; protected autoCreatePolicy: boolean; + protected disallowPublicAccess?: boolean; + constructor(scope: cdk.Construct, id: string, private readonly props: BucketImportProps) { super(scope, id); @@ -1059,6 +1136,7 @@ class ImportedBucket extends BucketBase { ? false : props.bucketWebsiteNewUrlFormat; this.policy = undefined; + this.disallowPublicAccess = false; } /** diff --git a/packages/@aws-cdk/aws-s3/test/test.bucket.ts b/packages/@aws-cdk/aws-s3/test/test.bucket.ts index 19c10b0840951..e9363faddfdee 100644 --- a/packages/@aws-cdk/aws-s3/test/test.bucket.ts +++ b/packages/@aws-cdk/aws-s3/test/test.bucket.ts @@ -201,6 +201,76 @@ export = { test.done(); }, + 'bucket with block public access set to BlockAll'(test: Test) { + const stack = new cdk.Stack(); + new s3.Bucket(stack, 'MyBucket', { + blockPublicAccess: s3.BlockPublicAccess.BlockAll, + }); + + expect(stack).toMatch({ + "Resources": { + "MyBucketF68F3FF0": { + "Type": "AWS::S3::Bucket", + "Properties": { + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true, + } + }, + "DeletionPolicy": "Retain", + } + } + }); + test.done(); + }, + + 'bucket with block public access set to BlockAcls'(test: Test) { + const stack = new cdk.Stack(); + new s3.Bucket(stack, 'MyBucket', { + blockPublicAccess: s3.BlockPublicAccess.BlockAcls, + }); + + expect(stack).toMatch({ + "Resources": { + "MyBucketF68F3FF0": { + "Type": "AWS::S3::Bucket", + "Properties": { + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "IgnorePublicAcls": true, + } + }, + "DeletionPolicy": "Retain", + } + } + }); + test.done(); + }, + + 'bucket with custom block public access setting'(test: Test) { + const stack = new cdk.Stack(); + new s3.Bucket(stack, 'MyBucket', { + blockPublicAccess: new s3.BlockPublicAccess({ restrictPublicBuckets: true }) + }); + + expect(stack).toMatch({ + "Resources": { + "MyBucketF68F3FF0": { + "Type": "AWS::S3::Bucket", + "Properties": { + "PublicAccessBlockConfiguration": { + "RestrictPublicBuckets": true, + } + }, + "DeletionPolicy": "Retain", + } + } + }); + test.done(); + }, + 'permissions': { 'addPermission creates a bucket policy'(test: Test) { @@ -1175,6 +1245,19 @@ export = { "Version": "2012-10-17" } })); + test.done(); + }, + + 'throws when blockPublicPolicy is set to true'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const bucket = new s3.Bucket(stack, 'MyBucket', { + blockPublicAccess: new s3.BlockPublicAccess({ blockPublicPolicy: true }) + }); + + // THEN + test.throws(() => bucket.grantPublicAccess(), /blockPublicPolicy/); + test.done(); } }, From a91a4f1561ebf860da1229ac5d494197c9539235 Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Mon, 4 Feb 2019 21:25:53 -0800 Subject: [PATCH 34/38] refactor to eliminate TagAspectProps, requires TagManager to know Resource Type --- .../@aws-cdk/cdk/lib/aspects/tag-aspect.ts | 34 +++--------- packages/@aws-cdk/cdk/lib/core/tag-manager.ts | 45 +++++++++++++--- .../cdk/test/aspects/test.tag-aspect.ts | 53 ++----------------- .../cdk/test/core/test.tag-manager.ts | 38 ++++++++----- tools/cfn2ts/lib/codegen.ts | 7 +-- 5 files changed, 79 insertions(+), 98 deletions(-) diff --git a/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts b/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts index 34cd8899215f1..45abee70023ec 100644 --- a/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts +++ b/packages/@aws-cdk/cdk/lib/aspects/tag-aspect.ts @@ -13,13 +13,11 @@ export abstract class TagBase implements IAspect { */ public readonly key: string; - private readonly includeResourceTypes: string[]; - private readonly excludeResourceTypes: string[]; + protected readonly props: TagProps; constructor(key: string, props: TagProps = {}) { this.key = key; - this.includeResourceTypes = props.includeResourceTypes || []; - this.excludeResourceTypes = props.excludeResourceTypes || []; + this.props = props; } public visit(construct: IConstruct): void { @@ -28,14 +26,6 @@ export abstract class TagBase implements IAspect { } const resource = construct as Resource; if (Resource.isTaggable(resource)) { - if (this.excludeResourceTypes.length !== 0 && - this.excludeResourceTypes.indexOf(resource.resourceType) !== -1) { - return; - } - if (this.includeResourceTypes.length !== 0 && - this.includeResourceTypes.indexOf(resource.resourceType) === -1) { - return; - } this.applyTag(resource); } } @@ -53,13 +43,10 @@ export class Tag extends TagBase { */ public readonly value: string; - private readonly applyToLaunchedInstances: boolean; - private readonly priority: number; - constructor(key: string, value: string, props: TagProps = {}) { - super(key, {...props}); - this.applyToLaunchedInstances = props.applyToLaunchedInstances !== false; - this.priority = props.priority === undefined ? 0 : props.priority; + super(key, props); + this.props.applyToLaunchedInstances = props.applyToLaunchedInstances !== false; + this.props.priority = props.priority === undefined ? 0 : props.priority; if (value === undefined) { throw new Error('Tag must have a value'); } @@ -67,10 +54,7 @@ export class Tag extends TagBase { } protected applyTag(resource: ITaggable) { - resource.tags.setTag(this.key, this.value!, { - applyToLaunchedInstances: this.applyToLaunchedInstances, - priority: this.priority, - }); + resource.tags.setTag(this.key, this.value!, this.props); } } @@ -79,15 +63,13 @@ export class Tag extends TagBase { */ export class RemoveTag extends TagBase { - private readonly priority: number; - constructor(key: string, props: TagProps = {}) { super(key, props); - this.priority = props.priority === undefined ? 1 : props.priority; + this.props.priority = props.priority === undefined ? 1 : props.priority; } protected applyTag(resource: ITaggable): void { - resource.tags.removeTag(this.key, this.priority); + resource.tags.removeTag(this.key, this.props); return; } } diff --git a/packages/@aws-cdk/cdk/lib/core/tag-manager.ts b/packages/@aws-cdk/cdk/lib/core/tag-manager.ts index 45125ade9a5f4..55bcdeb74717e 100644 --- a/packages/@aws-cdk/cdk/lib/core/tag-manager.ts +++ b/packages/@aws-cdk/cdk/lib/core/tag-manager.ts @@ -14,13 +14,32 @@ export interface TagProps { */ applyToLaunchedInstances?: boolean; + /** + * An array of Resource Types that will not receive this tag + * + * An empty array will allow this tag to be applied to all resources. A + * non-empty array will apply this tag only if the Resource type is not in + * this array. + * @default [] + */ + excludeResourceTypes?: string[]; + + /** + * An array of Resource Types that will receive this tag + * + * An empty array will match any Resource. A non-empty array will apply this + * tag only to Resource types that are included in this array. + * @default [] + */ + includeResourceTypes?: string[]; + /** * Higher or equal priority tags will take precedence * * Setting priority will enable the user to control tags when they need to not * follow the default precedence pattern of last applied and closest to the * construct in the tree. - * @default 0 for Tag 1 for Remove Tag + * @default 0 for Tag 1 for RemoveTag */ priority?: number; } @@ -34,7 +53,7 @@ export class TagManager { private readonly removedTags: {[key: string]: number} = {}; - constructor(private readonly tagType: TagType) { } + constructor(private readonly tagType: TagType, private readonly resourceTypeName: string) { } /** * Adds the specified tag to the array of tags @@ -45,9 +64,8 @@ export class TagManager { */ public setTag(key: string, value: string, props?: TagProps): void { const tagProps: TagProps = props || {}; - tagProps.priority = tagProps.priority === undefined ? 0 : tagProps.priority; - if (!this.hasHigherPriority(key, tagProps.priority)) { + if (!this.canApplyTag(key, tagProps)) { // tag is blocked by a remove return; } @@ -62,8 +80,10 @@ export class TagManager { * * @param key The key of the tag to remove */ - public removeTag(key: string, priority: number = 0): void { - if (!this.hasHigherPriority(key, priority)) { + public removeTag(key: string, props?: TagProps): void { + const tagProps = props || {}; + const priority = tagProps.priority === undefined ? 0 : tagProps.priority; + if (!this.canApplyTag(key, tagProps)) { // tag is blocked by a remove return; } @@ -108,7 +128,18 @@ export class TagManager { } } - private hasHigherPriority(key: string, priority: number): boolean { + private canApplyTag(key: string, props: TagProps): boolean { + const include = props.includeResourceTypes || []; + const exclude = props.excludeResourceTypes || []; + const priority = props.priority === undefined ? 0 : props.priority; + if (exclude.length !== 0 && + exclude.indexOf(this.resourceTypeName) !== -1) { + return false; + } + if (include.length !== 0 && + include.indexOf(this.resourceTypeName) === -1) { + return false; + } if (this.tags[key]) { if (this.tags[key].props.priority !== undefined) { return priority >= this.tags[key].props.priority!; diff --git a/packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts b/packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts index f4d6e7ff1f597..542692c92666d 100644 --- a/packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts +++ b/packages/@aws-cdk/cdk/test/aspects/test.tag-aspect.ts @@ -2,18 +2,18 @@ import { Test } from 'nodeunit'; import { RemoveTag, Resource, Stack, Tag, TagManager, TagType } from '../../lib'; class TaggableResource extends Resource { - public readonly tags = new TagManager(TagType.Standard); + public readonly tags = new TagManager(TagType.Standard, 'AWS::Fake::Resource'); public testProperties() { return this.properties; } } class AsgTaggableResource extends TaggableResource { - public readonly tags = new TagManager(TagType.AutoScalingGroup); + public readonly tags = new TagManager(TagType.AutoScalingGroup, 'AWS::Fake::Resource'); } class MapTaggableResource extends TaggableResource { - public readonly tags = new TagManager(TagType.Map); + public readonly tags = new TagManager(TagType.Map, 'AWS::Fake::Resource'); } export = { @@ -100,30 +100,6 @@ export = { test.deepEqual(res.tags.renderTags(), [{key: 'foo', value: 'bar'}]); test.done(); }, - 'include restricts tag application to resources types in the list'(test: Test) { - const root = new Stack(); - const res = new TaggableResource(root, 'FakeResource', { - type: 'AWS::Fake::Thing', - }); - const res2 = new TaggableResource(res, 'FakeResource', { - type: 'AWS::Fake::Thing', - }); - const asg = new AsgTaggableResource(res, 'AsgFakeResource', { - type: 'AWS::Fake::Asg', - }); - - const map = new MapTaggableResource(res, 'MapFakeResource', { - type: 'AWS::Fake::Map', - }); - res.apply(new Tag('foo', 'bar', {includeResourceTypes: ['AWS::Fake::Asg']})); - root.node.prepareTree(); - test.deepEqual(res.tags.renderTags(), undefined); - test.deepEqual(map.tags.renderTags(), undefined); - test.deepEqual(res2.tags.renderTags(), undefined); - test.deepEqual(asg.tags.renderTags(), [{key: 'foo', value: 'bar', propagateAtLaunch: true}]); - test.deepEqual(map.testProperties().tags, undefined); - test.done(); - }, 'removeTag Aspects by default will override child Tag Aspects'(test: Test) { const root = new Stack(); const res = new TaggableResource(root, 'FakeResource', { @@ -154,29 +130,6 @@ export = { test.deepEqual(res2.tags.renderTags(), [{key: 'key', value: 'value'}]); test.done(); }, - 'exclude prevents tag application to resource types in the list'(test: Test) { - const root = new Stack(); - const res = new TaggableResource(root, 'FakeResource', { - type: 'AWS::Fake::Thing', - }); - const res2 = new TaggableResource(res, 'FakeResource', { - type: 'AWS::Fake::Thing', - }); - const asg = new AsgTaggableResource(res, 'AsgFakeResource', { - type: 'AWS::Fake::Asg', - }); - - const map = new MapTaggableResource(res, 'MapFakeResource', { - type: 'AWS::Fake::Map', - }); - res.apply(new Tag('foo', 'bar', {excludeResourceTypes: ['AWS::Fake::Asg']})); - root.node.prepareTree(); - test.deepEqual(res.tags.renderTags(), [{key: 'foo', value: 'bar'}]); - test.deepEqual(res2.tags.renderTags(), [{key: 'foo', value: 'bar'}]); - test.deepEqual(asg.tags.renderTags(), undefined); - test.deepEqual(map.tags.renderTags(), {foo: 'bar'}); - test.done(); - }, 'Aspects are mutually exclusive with tags created by L1 Constructor'(test: Test) { const root = new Stack(); const aspectBranch = new TaggableResource(root, 'FakeBranchA', { diff --git a/packages/@aws-cdk/cdk/test/core/test.tag-manager.ts b/packages/@aws-cdk/cdk/test/core/test.tag-manager.ts index cb8dc9c5479e0..647b7a9784a79 100644 --- a/packages/@aws-cdk/cdk/test/core/test.tag-manager.ts +++ b/packages/@aws-cdk/cdk/test/core/test.tag-manager.ts @@ -4,19 +4,19 @@ import { TagManager } from '../../lib/core/tag-manager'; export = { '#setTag() supports setting a tag regardless of Type'(test: Test) { - const notTaggable = new TagManager(TagType.NotTaggable); + const notTaggable = new TagManager(TagType.NotTaggable, ''); notTaggable.setTag('key', 'value'); test.deepEqual(notTaggable.renderTags(), undefined); test.done(); }, 'when a tag does not exist': { '#removeTag() does not throw an error'(test: Test) { - const mgr = new TagManager(TagType.Standard); + const mgr = new TagManager(TagType.Standard, ''); test.doesNotThrow(() => (mgr.removeTag('dne'))); test.done(); }, '#setTag() creates the tag'(test: Test) { - const mgr = new TagManager(TagType.Standard); + const mgr = new TagManager(TagType.Standard, ''); mgr.setTag('dne', 'notanymore'); test.deepEqual(mgr.renderTags(), [{key: 'dne', value: 'notanymore'}]); test.done(); @@ -24,14 +24,14 @@ export = { }, 'when a tag does exist': { '#removeTag() deletes the tag'(test: Test) { - const mgr = new TagManager(TagType.Standard); + const mgr = new TagManager(TagType.Standard, ''); mgr.setTag('dne', 'notanymore'); mgr.removeTag('dne'); test.deepEqual(mgr.renderTags(), undefined); test.done(); }, '#setTag() overwrites the tag'(test: Test) { - const mgr = new TagManager(TagType.Standard); + const mgr = new TagManager(TagType.Standard, ''); mgr.setTag('dne', 'notanymore'); mgr.setTag('dne', 'iwin'); test.deepEqual(mgr.renderTags(), [{key: 'dne', value: 'iwin'}]); @@ -40,16 +40,16 @@ export = { }, 'when there are no tags': { '#renderTags() returns undefined'(test: Test) { - const mgr = new TagManager(TagType.Standard); + const mgr = new TagManager(TagType.Standard, ''); test.deepEqual(mgr.renderTags(), undefined ); test.done(); }, }, '#renderTags() handles standard, map, and ASG tag formats'(test: Test) { const tagged: TagManager[] = []; - const standard = new TagManager(TagType.Standard); - const asg = new TagManager(TagType.AutoScalingGroup); - const mapper = new TagManager(TagType.Map); + const standard = new TagManager(TagType.Standard, ''); + const asg = new TagManager(TagType.AutoScalingGroup, ''); + const mapper = new TagManager(TagType.Map, ''); tagged.push(standard); tagged.push(asg); tagged.push(mapper); @@ -72,19 +72,33 @@ export = { test.done(); }, 'tags with higher or equal priority always take precedence'(test: Test) { - const mgr = new TagManager(TagType.Standard); + const mgr = new TagManager(TagType.Standard, ''); mgr.setTag('key', 'myVal', { priority: 2, }); mgr.setTag('key', 'newVal', { priority: 1, }); - mgr.removeTag('key', 1); + mgr.removeTag('key', {priority: 1}); test.deepEqual(mgr.renderTags(), [ {key: 'key', value: 'myVal'}, ]); - mgr.removeTag('key', 2); + mgr.removeTag('key', {priority: 2}); test.deepEqual(mgr.renderTags(), undefined); test.done(); }, + 'excludeResourceTypes only tags resources that do not match'(test: Test) { + const mgr = new TagManager(TagType.Standard, 'AWS::Fake::Resource'); + mgr.setTag('key', 'value', {excludeResourceTypes: ['AWS::Fake::Resource']}); + mgr.setTag('sticky', 'value', {excludeResourceTypes: ['AWS::Wrong::Resource']}); + test.deepEqual(mgr.renderTags(), [{key: 'sticky', value: 'value'}]); + test.done(); + }, + 'includeResourceTypes only tags resources that match'(test: Test) { + const mgr = new TagManager(TagType.Standard, 'AWS::Fake::Resource'); + mgr.setTag('key', 'value', {includeResourceTypes: ['AWS::Fake::Resource']}); + mgr.setTag('sticky', 'value', {includeResourceTypes: ['AWS::Wrong::Resource']}); + test.deepEqual(mgr.renderTags(), [{key: 'key', value: 'value'}]); + test.done(); + } }; diff --git a/tools/cfn2ts/lib/codegen.ts b/tools/cfn2ts/lib/codegen.ts index dfdccc10674d2..30255c621c1f4 100644 --- a/tools/cfn2ts/lib/codegen.ts +++ b/tools/cfn2ts/lib/codegen.ts @@ -188,10 +188,11 @@ export default class CodeGenerator { // Static inspectors. // + const resourceTypeName = `${JSON.stringify(resourceName.specName!.fqn)}`; this.code.line('/**'); this.code.line(` * The CloudFormation resource type name for this resource class.`); this.code.line(' */'); - this.code.line(`public static readonly resourceTypeName = ${JSON.stringify(resourceName.specName!.fqn)};`); + this.code.line(`public static readonly resourceTypeName = ${resourceTypeName};`); if (spec.RequiredTransform) { this.code.line('/**'); @@ -242,10 +243,10 @@ export default class CodeGenerator { this.code.line(' *'); this.code.line(' * Tags should be managed either passing them as properties during'); this.code.line(' * initiation or by calling methods on this object. If both techniques are'); - this.code.line(' * used only the tags from the TagManager will be used. ``TagAspects``'); + this.code.line(' * used only the tags from the TagManager will be used. ``Tag`` (aspect)'); this.code.line(' * will use the manager.'); this.code.line(' */'); - this.code.line(`public readonly tags = new ${TAG_MANAGER}(${tagEnum});`); + this.code.line(`public readonly tags = new ${TAG_MANAGER}(${tagEnum}, ${resourceTypeName});`); } // From 5970c0b40cad12b47999b1cc135c5064d4aa44cd Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Mon, 4 Feb 2019 22:47:17 -0800 Subject: [PATCH 35/38] feat(cdk): aspect framework and tag implementation #1451 A generalized aspect framework is added. Aspects can be used to affect the construct tree without modifying the class hierarchy. This framework is designed to be generic for future use cases. Tagging is the first implementation. Tagging has been reimplemented to leverage the aspect framework and simplify the original tag design. Tag Manager still exists, but is no longer intended for use by L2 construct authors. There are two new classes `cdk.Tag` and `cdk.RemoveTag`. As new resources are added tag support will be automatic as long as they implement one of the existing tag specifications. All L2 constructs have removed the TagManager. Code generation has been modified to add tag support to any CloudFormation Resource with a matching specification, by creating a Tag Manager for that resource as a `tags` attribute. The schema code now includes the ability to detect 3 forms of tags which include the current CloudFormation Resources. BREAKING CHANGE: if you are using TagManager the API for this object has completely changed. You should no longer use TagManager directly, but instead replace this with Tag Aspects. `cdk.Tag` has been renamed to `cdk.CfnTag` to enable `cdk.Tag` to be the Tag Aspect. Fixes #1136. --- .../hello-cdk-ecs-tags/index.ts | 4 +- packages/@aws-cdk/cdk/README.md | 60 ++++++++++++++----- .../cdk/lib/cloudformation/resource.ts | 2 +- 3 files changed, 47 insertions(+), 19 deletions(-) diff --git a/examples/cdk-examples-typescript/hello-cdk-ecs-tags/index.ts b/examples/cdk-examples-typescript/hello-cdk-ecs-tags/index.ts index 998fdc76e751d..4747306f17c9b 100644 --- a/examples/cdk-examples-typescript/hello-cdk-ecs-tags/index.ts +++ b/examples/cdk-examples-typescript/hello-cdk-ecs-tags/index.ts @@ -4,7 +4,7 @@ import cdk = require('@aws-cdk/cdk'); const COST_CENTER_KEY = 'CostCenter'; -class MarketingFargate extends cdk.Stack { +class MarketingDepartmentStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); @@ -48,6 +48,6 @@ const app = new cdk.App(); // by default bill everything to marketing overrides are in the stack app.apply(new cdk.Tag(COST_CENTER_KEY, 'Marketing')); -new MarketingFargate(app, 'Bonjour'); +new MarketingDepartmentStack(app, 'Bonjour'); app.run(); diff --git a/packages/@aws-cdk/cdk/README.md b/packages/@aws-cdk/cdk/README.md index 01c199fda05c7..79400c9645d52 100644 --- a/packages/@aws-cdk/cdk/README.md +++ b/packages/@aws-cdk/cdk/README.md @@ -6,29 +6,30 @@ the [AWS Cloud Development Kit](https://github.com/awslabs/aws-cdk) (AWS CDK). ## Aspects Aspects are a mechanism to extend the CDK without having to directly impact the -class hierarchy. We have implemented Aspects using the [Visitor +class hierarchy. We have implemented aspects using the [Visitor Pattern](https://en.wikipedia.org/wiki/Visitor_pattern). An aspect in the CDK is defined by this [interface](lib/aspects/aspect.ts) Aspects can be applied to any construct. During the tree -preparation phase the aspect will visit each construct in the tree one -time. Aspects are invoked in the order they were added to the construct, -starting at the `App` (root of the tree) and progressing in order to the leaf -nodes (most commonly the CloudFormation Resource). Aspect authors will implement -the `visit(IConstruct)` function and can inspect the `Construct` for specific characteristics. -Such as, is this construct a CloudFormation Resource? +"prepare" phase the aspect will visit each construct in the tree once. +Aspects are invoked in the order they were added to the construct. They +traverse the construct tree in a breadth first order starting at the `App` +ending at the leaf nodes (most commonly the CloudFormation Resource). Aspect +authors implement the `visit(IConstruct)` function and can inspect the +`Construct` for specific characteristics. Such as, is this construct a +CloudFormation Resource? ## Tagging -Tags are implemented using Aspects. +Tags are implemented using aspects. Tags can be applied to any construct. Tags are inherited, based on the scope. If you tag construct A, and A contains construct B, construct B inherits the tag. The Tag API supports: - * Add (apply) a tag, either to specific resources or all but specific resources - * Remove a tag, again either from specific resources or all but specific resources + * `Tag` add (apply) a tag, either to specific resources or all but specific resources + * `RemoveTag` remove a tag, again either from specific resources or all but specific resources A simple example, if you create a stack and want anything in the stack to receive a tag: @@ -43,11 +44,11 @@ theBestStack.apply(new cdk.Tag('StackType', 'TheBest')); // any resources added that support tags will get them ``` -The goal was to enable the ability to define tags in one place and have them -applied consistently for all resources that support tagging. In addition -the developer should not have to know if the resource supports tags. The -developer defines the tagging intents for all resources within a path. -If the resources support tags they are added, else no action is taken. +> The goal was to enable the ability to define tags in one place and have them +> applied consistently for all resources that support tagging. In addition +> the developer should not have to know if the resource supports tags. The +> developer defines the tagging intents for all resources within a path. +> If the resources support tags they are added, else no action is taken. ### Tag Example with ECS @@ -118,7 +119,7 @@ has a few features that are covered later to explain how this works. In order to enable additional controls a Tags can specifically include or exclude a CloudFormation Resource Type, propagate tags for an autoscaling group, -and use priority to override the default precedence. See the `TagAspectProps` +and use priority to override the default precedence. See the `TagProps` interface for more details. #### applyToLaunchedInstances @@ -127,6 +128,13 @@ This property is a boolean that defaults to `true`. When `true` and the aspect visits an AutoScalingGroup resource the `PropagateAtLaunch` property is set to true. If false the property is set accordingly. +```ts +// ... snip +const vpc = new ec2.VpcNetwork(this, 'MyVpc', { ... }); +vpc.apply(new cdk.Tag('MyKey', 'MyValue', { applyToLaunchedInstances: false })); +// ... snip +``` + #### includeResourceTypes Include is an array property that contains strings of CloudFormation Resource @@ -134,6 +142,13 @@ Types. As the aspect visits nodes it only takes action if node is one of the resource types in the array. By default the array is empty and an empty array is interpreted as apply to any resource type. +```ts +// ... snip +const vpc = new ec2.VpcNetwork(this, 'MyVpc', { ... }); +vpc.apply(new cdk.Tag('MyKey', 'MyValue', { includeResourceTypes: ['AWS::EC2::Subnet']})); +// ... snip +``` + #### excludeResourceTypes Exclude is the inverse of include. Exclude is also an array of CloudFormation @@ -142,6 +157,13 @@ one of the resource types in the array. By default the array is empty and an empty array is interpreted to match no resource type. Exclude takes precedence over include in the event of a collision. +```ts +// ... snip +const vpc = new ec2.VpcNetwork(this, 'MyVpc', { ... }); +vpc.apply(new cdk.Tag('MyKey', 'MyValue', { exludeResourceTypes: ['AWS::EC2::Subnet']})); +// ... snip +``` + #### priority Priority is used to control precedence when the default pattern does not work. @@ -149,3 +171,9 @@ In general users should try to avoid using priority, but in some situations it is required. In the example above, this is how `RemoveTag` works. The default setting for removing tags uses a higher priority than the standard tag. +```ts +// ... snip +const vpc = new ec2.VpcNetwork(this, 'MyVpc', { ... }); +vpc.apply(new cdk.Tag('MyKey', 'MyValue', { priority: 2 })); +// ... snip +``` diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/resource.ts b/packages/@aws-cdk/cdk/lib/cloudformation/resource.ts index 263fb57015dfd..79ef4aec3bd97 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/resource.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/resource.ts @@ -55,7 +55,7 @@ export class Resource extends Referenceable { /** * Check whether the given construct is Taggable */ - public static isTaggable(construct: IConstruct): construct is ITaggable { + public static isTaggable(construct: any): construct is ITaggable { return (construct as any).tags !== undefined; } From bafff0f4549c08b0bdff8ec10d2b58255601e204 Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Tue, 5 Feb 2019 00:15:06 -0800 Subject: [PATCH 36/38] minor fix to the example --- examples/cdk-examples-typescript/hello-cdk-ecs-tags/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/cdk-examples-typescript/hello-cdk-ecs-tags/index.ts b/examples/cdk-examples-typescript/hello-cdk-ecs-tags/index.ts index 4747306f17c9b..af9a3e0245707 100644 --- a/examples/cdk-examples-typescript/hello-cdk-ecs-tags/index.ts +++ b/examples/cdk-examples-typescript/hello-cdk-ecs-tags/index.ts @@ -19,7 +19,7 @@ class MarketingDepartmentStack extends cdk.Stack { // Create a load-balanced Fargate service and make it public const b2b = new ecs.LoadBalancedFargateService(this, 'B2BService', { - cluster: cluster, // Required + cluster, // Required cpu: '512', // Default is 256 desiredCount: 6, // Default is 1 image: ecs.ContainerImage.fromDockerHub('amazon/amazon-ecs-sample'), // Required @@ -29,7 +29,7 @@ class MarketingDepartmentStack extends cdk.Stack { // Create a load-balanced Fargate service and make it public const b2c = new ecs.LoadBalancedFargateService(this, 'B2CService', { - cluster: cluster, // Required + cluster, // Required cpu: '512', // Default is 256 desiredCount: 6, // Default is 1 image: ecs.ContainerImage.fromDockerHub('amazon/amazon-ecs-sample'), // Required From fd940302f34675ba9dcfc9236c8a240c9d684094 Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Tue, 5 Feb 2019 08:34:52 -0800 Subject: [PATCH 37/38] fix test in tag-manager to add resource type string for clarity --- .../cdk/test/core/test.tag-manager.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/@aws-cdk/cdk/test/core/test.tag-manager.ts b/packages/@aws-cdk/cdk/test/core/test.tag-manager.ts index 647b7a9784a79..89ba6c7dac9b4 100644 --- a/packages/@aws-cdk/cdk/test/core/test.tag-manager.ts +++ b/packages/@aws-cdk/cdk/test/core/test.tag-manager.ts @@ -4,19 +4,19 @@ import { TagManager } from '../../lib/core/tag-manager'; export = { '#setTag() supports setting a tag regardless of Type'(test: Test) { - const notTaggable = new TagManager(TagType.NotTaggable, ''); + const notTaggable = new TagManager(TagType.NotTaggable, 'AWS::Resource::Type'); notTaggable.setTag('key', 'value'); test.deepEqual(notTaggable.renderTags(), undefined); test.done(); }, 'when a tag does not exist': { '#removeTag() does not throw an error'(test: Test) { - const mgr = new TagManager(TagType.Standard, ''); + const mgr = new TagManager(TagType.Standard, 'AWS::Resource::Type'); test.doesNotThrow(() => (mgr.removeTag('dne'))); test.done(); }, '#setTag() creates the tag'(test: Test) { - const mgr = new TagManager(TagType.Standard, ''); + const mgr = new TagManager(TagType.Standard, 'AWS::Resource::Type'); mgr.setTag('dne', 'notanymore'); test.deepEqual(mgr.renderTags(), [{key: 'dne', value: 'notanymore'}]); test.done(); @@ -24,14 +24,14 @@ export = { }, 'when a tag does exist': { '#removeTag() deletes the tag'(test: Test) { - const mgr = new TagManager(TagType.Standard, ''); + const mgr = new TagManager(TagType.Standard, 'AWS::Resource::Type'); mgr.setTag('dne', 'notanymore'); mgr.removeTag('dne'); test.deepEqual(mgr.renderTags(), undefined); test.done(); }, '#setTag() overwrites the tag'(test: Test) { - const mgr = new TagManager(TagType.Standard, ''); + const mgr = new TagManager(TagType.Standard, 'AWS::Resource::Type'); mgr.setTag('dne', 'notanymore'); mgr.setTag('dne', 'iwin'); test.deepEqual(mgr.renderTags(), [{key: 'dne', value: 'iwin'}]); @@ -40,16 +40,16 @@ export = { }, 'when there are no tags': { '#renderTags() returns undefined'(test: Test) { - const mgr = new TagManager(TagType.Standard, ''); + const mgr = new TagManager(TagType.Standard, 'AWS::Resource::Type'); test.deepEqual(mgr.renderTags(), undefined ); test.done(); }, }, '#renderTags() handles standard, map, and ASG tag formats'(test: Test) { const tagged: TagManager[] = []; - const standard = new TagManager(TagType.Standard, ''); - const asg = new TagManager(TagType.AutoScalingGroup, ''); - const mapper = new TagManager(TagType.Map, ''); + const standard = new TagManager(TagType.Standard, 'AWS::Resource::Type'); + const asg = new TagManager(TagType.AutoScalingGroup, 'AWS::Resource::Type'); + const mapper = new TagManager(TagType.Map, 'AWS::Resource::Type'); tagged.push(standard); tagged.push(asg); tagged.push(mapper); @@ -72,7 +72,7 @@ export = { test.done(); }, 'tags with higher or equal priority always take precedence'(test: Test) { - const mgr = new TagManager(TagType.Standard, ''); + const mgr = new TagManager(TagType.Standard, 'AWS::Resource::Type'); mgr.setTag('key', 'myVal', { priority: 2, }); From d0fd751d3cdbaaba39609c1e7fe890c49f1ffe76 Mon Sep 17 00:00:00 2001 From: Mike Cowgill Date: Wed, 6 Feb 2019 00:15:39 -0800 Subject: [PATCH 38/38] updating integ test that now tags the lambda function --- .../aws-ecs/test/ec2/integ.event-task.lit.expected.json | 6 ++++++ 1 file changed, 6 insertions(+) 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 c3b9d654c394a..364dfeb0633f4 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 @@ -452,6 +452,12 @@ } } }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-ecs/EcsCluster/DefaultAutoScalingGroup" + } + ], "Timeout": 310 }, "DependsOn": [