From cad5bc148bfdd088c1307932b919899a98fd860a Mon Sep 17 00:00:00 2001 From: flyingImer Date: Wed, 18 Dec 2019 11:11:17 -0800 Subject: [PATCH 01/80] feat(core): add support for the ref intrinsic function (#5468) (#5470) --- packages/@aws-cdk/core/lib/cfn-fn.ts | 24 ++++++++++++++++++++++++ packages/@aws-cdk/core/test/test.fn.ts | 9 +++++++++ 2 files changed, 33 insertions(+) diff --git a/packages/@aws-cdk/core/lib/cfn-fn.ts b/packages/@aws-cdk/core/lib/cfn-fn.ts index 1f775abee6671..31619d551424e 100644 --- a/packages/@aws-cdk/core/lib/cfn-fn.ts +++ b/packages/@aws-cdk/core/lib/cfn-fn.ts @@ -13,6 +13,15 @@ import { Token } from './token'; * http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html */ export class Fn { + /** + * The ``Ref`` intrinsic function returns the value of the specified parameter or resource. + * Note that it doesn't validate the logicalName, it mainly serves paremeter/resource reference defined in a ``CfnInclude`` template. + * @param logicalName The logical name of a parameter/resource for which you want to retrieve its value. + */ + public static ref(logicalName: string): string { + return new FnRef(logicalName).toString(); + } + /** * The ``Fn::GetAtt`` intrinsic function returns the value of an attribute * from a resource in the template. @@ -311,6 +320,21 @@ class FnBase extends Intrinsic { } } +/** + * The intrinsic function ``Ref`` returns the value of the specified parameter or resource. + * When you specify a parameter's logical name, it returns the value of the parameter. + * When you specify a resource's logical name, it returns a value that you can typically use to refer to that resource, such as a physical ID. + */ +class FnRef extends FnBase { + /** + * Creates an ``Ref`` function. + * @param logicalName The logical name of a parameter/resource for which you want to retrieve its value. + */ + constructor(logicalName: string) { + super('Ref', logicalName); + } +} + /** * The intrinsic function ``Fn::FindInMap`` returns the value corresponding to keys in a two-level * map that is declared in the Mappings section. diff --git a/packages/@aws-cdk/core/test/test.fn.ts b/packages/@aws-cdk/core/test/test.fn.ts index 134775b13af2d..7a5b05e08524f 100644 --- a/packages/@aws-cdk/core/test/test.fn.ts +++ b/packages/@aws-cdk/core/test/test.fn.ts @@ -168,6 +168,15 @@ export = nodeunit.testCase({ }); }), }, + 'Ref': { + 'returns a reference given a logical name'(test: nodeunit.Test) { + const stack = new Stack(); + test.deepEqual(stack.resolve(Fn.ref('hello')), { + Ref: 'hello' + }); + test.done(); + } + } }); function stringListToken(o: any): string[] { From d5ed97a84d91e4eb7b13c11c4b0b826625f564d4 Mon Sep 17 00:00:00 2001 From: Joe Hillenbrand Date: Thu, 19 Dec 2019 05:25:38 -0800 Subject: [PATCH 02/80] fix(ec2): allow ingress to VPC interface endpoints (#4938) Automatically allow ingress to VPC endpoints by default, from traffic originating in the VPC. This fixes the 99% case where interface endpoints are used to allow ISOLATED instances to access AWS services, out of the box. People that need more control over the security policy rules can specify `open: false` to disable this behavior and use the `.connections` object for fine-grained access control. Fixes #4937 --- .../@aws-cdk/aws-ec2/lib/security-group.ts | 8 +++- packages/@aws-cdk/aws-ec2/lib/vpc-endpoint.ts | 16 +++++++ packages/@aws-cdk/aws-ec2/lib/vpc.ts | 44 +++++++++++++++++++ .../test/integ.vpc-endpoint.lit.expected.json | 23 +++++++++- .../aws-ec2/test/integ.vpc-endpoint.lit.ts | 12 ++--- .../aws-ec2/test/test.vpc-endpoint.ts | 26 ++++++++++- packages/@aws-cdk/cx-api/lib/context/vpc.ts | 14 ++++++ .../aws-cdk/lib/context-providers/vpcs.ts | 12 +++-- .../context-providers/test.asymmetric-vpcs.ts | 36 +++++++++++---- .../test/context-providers/test.vpcs.ts | 5 ++- 10 files changed, 172 insertions(+), 24 deletions(-) diff --git a/packages/@aws-cdk/aws-ec2/lib/security-group.ts b/packages/@aws-cdk/aws-ec2/lib/security-group.ts index faade40da9f1a..48179656f64f2 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, IResource, Lazy, Resource, ResourceProps, Stack } from '@aws-cdk/core'; +import { Construct, IResource, Lazy, Resource, ResourceProps, Stack, Token } from '@aws-cdk/core'; import { Connections } from './connections'; import { CfnSecurityGroup, CfnSecurityGroupEgress, CfnSecurityGroupIngress } from './ec2.generated'; import { IPeer } from './peer'; @@ -170,10 +170,14 @@ function determineRuleScope( return [peer, `${group.uniqueId}:${connection} ${reversedFromTo}`]; } else { // Regular (do old ID escaping to in order to not disturb existing deployments) - return [group, `${fromTo} ${peer.uniqueId}:${connection}`.replace('/', '_')]; + return [group, `${fromTo} ${renderPeer(peer)}:${connection}`.replace('/', '_')]; } } +function renderPeer(peer: IPeer) { + return Token.isUnresolved(peer.uniqueId) ? `{IndirectPeer}` : peer.uniqueId; +} + function differentStacks(group1: SecurityGroupBase, group2: SecurityGroupBase) { return Stack.of(group1) !== Stack.of(group2); } diff --git a/packages/@aws-cdk/aws-ec2/lib/vpc-endpoint.ts b/packages/@aws-cdk/aws-ec2/lib/vpc-endpoint.ts index c60507afccda9..16510f853325c 100644 --- a/packages/@aws-cdk/aws-ec2/lib/vpc-endpoint.ts +++ b/packages/@aws-cdk/aws-ec2/lib/vpc-endpoint.ts @@ -2,6 +2,7 @@ import * as iam from '@aws-cdk/aws-iam'; import { Aws, Construct, IResource, Lazy, Resource } from '@aws-cdk/core'; import { Connections, IConnectable } from './connections'; import { CfnVPCEndpoint } from './ec2.generated'; +import { Peer } from './peer'; import { Port } from './port'; import { ISecurityGroup, SecurityGroup } from './security-group'; import { allRouteTableIds } from './util'; @@ -294,6 +295,16 @@ export interface InterfaceVpcEndpointOptions { * @default - a new security group is created */ readonly securityGroups?: ISecurityGroup[]; + + /** + * Whether to automatically allow VPC traffic to the endpoint + * + * If enabled, all traffic to the endpoint from within the VPC will be + * automatically allowed. This is done based on the VPC's CIDR range. + * + * @default true + */ + readonly open?: boolean; } /** @@ -382,12 +393,17 @@ export class InterfaceVpcEndpoint extends VpcEndpoint implements IInterfaceVpcEn const securityGroups = props.securityGroups || [new SecurityGroup(this, 'SecurityGroup', { vpc: props.vpc })]; + this.securityGroupId = securityGroups[0].securityGroupId; this.connections = new Connections({ defaultPort: Port.tcp(props.service.port), securityGroups }); + if (props.open !== false) { + this.connections.allowDefaultPortFrom(Peer.ipv4(props.vpc.vpcCidrBlock)); + } + const subnets = props.vpc.selectSubnets({ ...props.subnets, onePerAz: true }); const subnetIds = subnets.subnetIds; diff --git a/packages/@aws-cdk/aws-ec2/lib/vpc.ts b/packages/@aws-cdk/aws-ec2/lib/vpc.ts index ad4794bf0ff56..fccb942078f0b 100644 --- a/packages/@aws-cdk/aws-ec2/lib/vpc.ts +++ b/packages/@aws-cdk/aws-ec2/lib/vpc.ts @@ -61,6 +61,13 @@ export interface IVpc extends IResource { */ readonly vpcId: string; + /** + * CIDR range for this VPC + * + * @attribute + */ + readonly vpcCidrBlock: string; + /** * List of public subnets in this VPC */ @@ -260,6 +267,11 @@ abstract class VpcBase extends Resource implements IVpc { */ public abstract readonly vpcId: string; + /** + * CIDR range for this VPC + */ + public abstract readonly vpcCidrBlock: string; + /** * List of public subnets in this VPC */ @@ -443,6 +455,13 @@ export interface VpcAttributes { */ readonly vpcId: string; + /** + * VPC's CIDR range + * + * @default - Retrieving the CIDR from the VPC will fail + */ + readonly vpcCidrBlock?: string; + /** * List of availability zones for the subnets in this VPC. */ @@ -1578,11 +1597,13 @@ class ImportedVpc extends VpcBase { public readonly availabilityZones: string[]; public readonly vpnGatewayId?: string; public readonly internetConnectivityEstablished: IDependable = new ConcreteDependable(); + private readonly cidr?: string | undefined; constructor(scope: Construct, id: string, props: VpcAttributes, isIncomplete: boolean) { super(scope, id); this.vpcId = props.vpcId; + this.cidr = props.vpcCidrBlock; this.availabilityZones = props.availabilityZones; this.vpnGatewayId = props.vpnGatewayId; this.incompleteSubnetDefinition = isIncomplete; @@ -1597,6 +1618,13 @@ class ImportedVpc extends VpcBase { this.privateSubnets = priv.import(this); this.isolatedSubnets = iso.import(this); } + + public get vpcCidrBlock(): string { + if (this.cidr === undefined) { + throw new Error(`Cannot perform this operation: 'vpcCidrBlock' was not supplied when creating this VPC`); + } + return this.cidr; + } } class LookedUpVpc extends VpcBase { @@ -1607,11 +1635,13 @@ class LookedUpVpc extends VpcBase { public readonly publicSubnets: ISubnet[]; public readonly privateSubnets: ISubnet[]; public readonly isolatedSubnets: ISubnet[]; + private readonly cidr?: string | undefined; constructor(scope: Construct, id: string, props: cxapi.VpcContextResponse, isIncomplete: boolean) { super(scope, id); this.vpcId = props.vpcId; + this.cidr = props.vpcCidrBlock; this.vpnGatewayId = props.vpnGatewayId; this.incompleteSubnetDefinition = isIncomplete; @@ -1627,6 +1657,15 @@ class LookedUpVpc extends VpcBase { this.isolatedSubnets = this.extractSubnetsOfType(subnetGroups, cxapi.VpcSubnetGroupType.ISOLATED); } + public get vpcCidrBlock(): string { + if (this.cidr === undefined) { + // Value might be cached from an old CLI version, so bumping the CX API protocol to + // force the value to exist would not have helped. + throw new Error(`Cannot perform this operation: 'vpcCidrBlock' was not found when looking up this VPC. Use a newer version of the CDK CLI and clear the old context value.`); + } + return this.cidr; + } + private extractSubnetsOfType(subnetGroups: cxapi.VpcSubnetGroup[], subnetGroupType: cxapi.VpcSubnetGroupType): ISubnet[] { return flatMap(subnetGroups.filter(subnetGroup => subnetGroup.type === subnetGroupType), subnetGroup => this.subnetGroupToSubnets(subnetGroup)); @@ -1758,6 +1797,7 @@ function determineNatGatewayCount(requestedCount: number | undefined, subnetConf */ const DUMMY_VPC_PROPS: cxapi.VpcContextResponse = { availabilityZones: [], + vpcCidrBlock: '1.2.3.4/5', isolatedSubnetIds: undefined, isolatedSubnetNames: undefined, isolatedSubnetRouteTableIds: undefined, @@ -1776,11 +1816,13 @@ const DUMMY_VPC_PROPS: cxapi.VpcContextResponse = { availabilityZone: 'dummy-1a', subnetId: 's-12345', routeTableId: 'rtb-12345s', + cidr: '1.2.3.4/5', }, { availabilityZone: 'dummy-1b', subnetId: 's-67890', routeTableId: 'rtb-67890s', + cidr: '1.2.3.4/5', }, ], }, @@ -1792,11 +1834,13 @@ const DUMMY_VPC_PROPS: cxapi.VpcContextResponse = { availabilityZone: 'dummy-1a', subnetId: 'p-12345', routeTableId: 'rtb-12345p', + cidr: '1.2.3.4/5', }, { availabilityZone: 'dummy-1b', subnetId: 'p-67890', routeTableId: 'rtb-57890p', + cidr: '1.2.3.4/5', }, ], }, diff --git a/packages/@aws-cdk/aws-ec2/test/integ.vpc-endpoint.lit.expected.json b/packages/@aws-cdk/aws-ec2/test/integ.vpc-endpoint.lit.expected.json index 0d2cb6f4ba154..e464307a32b8e 100644 --- a/packages/@aws-cdk/aws-ec2/test/integ.vpc-endpoint.lit.expected.json +++ b/packages/@aws-cdk/aws-ec2/test/integ.vpc-endpoint.lit.expected.json @@ -605,8 +605,27 @@ ], "SecurityGroupIngress": [ { - "CidrIp": "0.0.0.0/0", - "Description": "from 0.0.0.0/0:443", + "CidrIp": { + "Fn::GetAtt": [ + "MyVpcF9F0CA6F", + "CidrBlock" + ] + }, + "Description": { + "Fn::Join": [ + "", + [ + "from ", + { + "Fn::GetAtt": [ + "MyVpcF9F0CA6F", + "CidrBlock" + ] + }, + ":443" + ] + ] + }, "FromPort": 443, "IpProtocol": "tcp", "ToPort": 443 diff --git a/packages/@aws-cdk/aws-ec2/test/integ.vpc-endpoint.lit.ts b/packages/@aws-cdk/aws-ec2/test/integ.vpc-endpoint.lit.ts index 7eb1d1ea0097b..669826ccb6c7f 100644 --- a/packages/@aws-cdk/aws-ec2/test/integ.vpc-endpoint.lit.ts +++ b/packages/@aws-cdk/aws-ec2/test/integ.vpc-endpoint.lit.ts @@ -32,13 +32,13 @@ class VpcEndpointStack extends cdk.Stack { })); // Add an interface endpoint - const ecrDockerEndpoint = vpc.addInterfaceEndpoint('EcrDockerEndpoint', { - service: ec2.InterfaceVpcEndpointAwsService.ECR_DOCKER - }); + vpc.addInterfaceEndpoint('EcrDockerEndpoint', { + service: ec2.InterfaceVpcEndpointAwsService.ECR_DOCKER, - // When working with an interface endpoint, use the connections object to - // allow traffic to flow to the endpoint. - ecrDockerEndpoint.connections.allowDefaultPortFromAnyIpv4(); + // Uncomment the following to allow more fine-grained control over + // who can access the endpoint via the '.connections' object. + // open: false + }); /// !hide } } diff --git a/packages/@aws-cdk/aws-ec2/test/test.vpc-endpoint.ts b/packages/@aws-cdk/aws-ec2/test/test.vpc-endpoint.ts index 36436e194c3ad..a2fcaa97b9164 100644 --- a/packages/@aws-cdk/aws-ec2/test/test.vpc-endpoint.ts +++ b/packages/@aws-cdk/aws-ec2/test/test.vpc-endpoint.ts @@ -1,4 +1,4 @@ -import { expect, haveResource } from '@aws-cdk/assert'; +import { expect, haveResource, haveResourceLike } from '@aws-cdk/assert'; import { AnyPrincipal, PolicyStatement } from '@aws-cdk/aws-iam'; import { Stack } from '@aws-cdk/core'; import { Test } from 'nodeunit'; @@ -307,6 +307,30 @@ export = { SecurityGroupIds: ['existing-id'], })); + test.done(); + }, + 'security group has ingress by default'(test: Test) { + // GIVEN + const stack = new Stack(); + const vpc = new Vpc(stack, 'VpcNetwork'); + + // WHEN + vpc.addInterfaceEndpoint('SecretsManagerEndpoint', { + service: InterfaceVpcEndpointAwsService.SECRETS_MANAGER, + }); + + // THEN + expect(stack).to(haveResourceLike('AWS::EC2::SecurityGroup', { + SecurityGroupIngress: [ + { + CidrIp: { "Fn::GetAtt": [ "VpcNetworkB258E83A", "CidrBlock" ] }, + FromPort: 443, + IpProtocol: "tcp", + ToPort: 443 + } + ] + }, )); + test.done(); } } diff --git a/packages/@aws-cdk/cx-api/lib/context/vpc.ts b/packages/@aws-cdk/cx-api/lib/context/vpc.ts index ef0f779e0f402..245ea842f735a 100644 --- a/packages/@aws-cdk/cx-api/lib/context/vpc.ts +++ b/packages/@aws-cdk/cx-api/lib/context/vpc.ts @@ -73,6 +73,13 @@ export interface VpcSubnet { /** The identifier of the route table for this subnet. */ readonly routeTableId: string; + + /** + * CIDR range of the subnet + * + * @default - CIDR information not available + */ + readonly cidr?: string; } /** @@ -108,6 +115,13 @@ export interface VpcContextResponse { */ readonly vpcId: string; + /** + * VPC cidr + * + * @default - CIDR information not available + */ + readonly vpcCidrBlock?: string; + /** * AZs */ diff --git a/packages/aws-cdk/lib/context-providers/vpcs.ts b/packages/aws-cdk/lib/context-providers/vpcs.ts index 77e27f17e879c..1708d78d4a2c3 100644 --- a/packages/aws-cdk/lib/context-providers/vpcs.ts +++ b/packages/aws-cdk/lib/context-providers/vpcs.ts @@ -20,7 +20,7 @@ export class VpcNetworkContextProviderPlugin implements ContextProviderPlugin { return await this.readVpcProps(ec2, vpcId, args); } - private async findVpc(ec2: AWS.EC2, args: cxapi.VpcContextQuery): Promise { + private async findVpc(ec2: AWS.EC2, args: cxapi.VpcContextQuery): Promise { // Build request filter (map { Name -> Value } to list of [{ Name, Values }]) const filters: AWS.EC2.Filter[] = Object.entries(args.filter).map(([tag, value]) => ({ Name: tag, Values: [value] })); @@ -35,10 +35,12 @@ export class VpcNetworkContextProviderPlugin implements ContextProviderPlugin { throw new Error(`Found ${vpcs.length} VPCs matching ${JSON.stringify(args)}; please narrow the search criteria`); } - return vpcs[0].VpcId!; + return vpcs[0]; } - private async readVpcProps(ec2: AWS.EC2, vpcId: string, args: cxapi.VpcContextQuery): Promise { + private async readVpcProps(ec2: AWS.EC2, vpc: AWS.EC2.Vpc, args: cxapi.VpcContextQuery): Promise { + const vpcId = vpc.VpcId!; + debug(`Describing VPC ${vpcId}`); const filters = { Filters: [{ Name: 'vpc-id', Values: [vpcId] }] }; @@ -80,6 +82,7 @@ export class VpcNetworkContextProviderPlugin implements ContextProviderPlugin { return { az: subnet.AvailabilityZone!, + cidr: subnet.CidrBlock!, type, name, subnetId: subnet.SubnetId!, @@ -120,6 +123,7 @@ export class VpcNetworkContextProviderPlugin implements ContextProviderPlugin { return { vpcId, + vpcCidrBlock: vpc.CidrBlock!, availabilityZones: grouped.azs, isolatedSubnetIds: collapse(flatMap(findGroups(SubnetType.Isolated, grouped), group => group.subnets.map(s => s.subnetId))), isolatedSubnetNames: collapse(flatMap(findGroups(SubnetType.Isolated, grouped), group => group.name ? [group.name] : [])), @@ -224,6 +228,7 @@ function groupAsymmetricSubnets(subnets: Subnet[]): cxapi.VpcSubnetGroup[] { type: subnetTypeToVpcSubnetType(subnetArray[0].type), subnets: subnetArray.map(subnet => ({ subnetId: subnet.subnetId, + cidr: subnet.cidr, availabilityZone: subnet.az, routeTableId: subnet.routeTableId, })), @@ -253,6 +258,7 @@ function isValidSubnetType(val: string): val is SubnetType { interface Subnet { az: string; + cidr: string; type: SubnetType; name: string; routeTableId: string; diff --git a/packages/aws-cdk/test/context-providers/test.asymmetric-vpcs.ts b/packages/aws-cdk/test/context-providers/test.asymmetric-vpcs.ts index d9aa97ff4011a..c9be332202f63 100644 --- a/packages/aws-cdk/test/context-providers/test.asymmetric-vpcs.ts +++ b/packages/aws-cdk/test/context-providers/test.asymmetric-vpcs.ts @@ -23,8 +23,8 @@ export = nodeunit.testCase({ async 'looks up the requested (symmetric) VPC'(test: nodeunit.Test) { mockVpcLookup(test, { subnets: [ - { SubnetId: 'sub-123456', AvailabilityZone: 'bermuda-triangle-1337', MapPublicIpOnLaunch: true }, - { SubnetId: 'sub-789012', AvailabilityZone: 'bermuda-triangle-1337', MapPublicIpOnLaunch: false } + { SubnetId: 'sub-123456', AvailabilityZone: 'bermuda-triangle-1337', MapPublicIpOnLaunch: true, CidrBlock: '1.1.1.1/24' }, + { SubnetId: 'sub-789012', AvailabilityZone: 'bermuda-triangle-1337', MapPublicIpOnLaunch: false, CidrBlock: '1.1.2.1/24' } ], routeTables: [ { Associations: [{ SubnetId: 'sub-123456' }], RouteTableId: 'rtb-123456', }, @@ -41,6 +41,7 @@ export = nodeunit.testCase({ test.deepEqual(result, { availabilityZones: [], + vpcCidrBlock: '1.1.1.1/16', isolatedSubnetIds: undefined, isolatedSubnetNames: undefined, isolatedSubnetRouteTableIds: undefined, @@ -59,6 +60,7 @@ export = nodeunit.testCase({ subnetId: 'sub-123456', availabilityZone: 'bermuda-triangle-1337', routeTableId: 'rtb-123456', + cidr: '1.1.1.1/24', }, ], }, @@ -70,6 +72,7 @@ export = nodeunit.testCase({ subnetId: 'sub-789012', availabilityZone: 'bermuda-triangle-1337', routeTableId: 'rtb-789012', + cidr: '1.1.2.1/24', }, ], }, @@ -129,8 +132,8 @@ export = nodeunit.testCase({ async 'uses the VPC main route table when a subnet has no specific association'(test: nodeunit.Test) { mockVpcLookup(test, { subnets: [ - { SubnetId: 'sub-123456', AvailabilityZone: 'bermuda-triangle-1337', MapPublicIpOnLaunch: true }, - { SubnetId: 'sub-789012', AvailabilityZone: 'bermuda-triangle-1337', MapPublicIpOnLaunch: false } + { SubnetId: 'sub-123456', AvailabilityZone: 'bermuda-triangle-1337', MapPublicIpOnLaunch: true, CidrBlock: '1.1.1.1/24' }, + { SubnetId: 'sub-789012', AvailabilityZone: 'bermuda-triangle-1337', MapPublicIpOnLaunch: false, CidrBlock: '1.1.2.1/24' } ], routeTables: [ { Associations: [{ SubnetId: 'sub-123456' }], RouteTableId: 'rtb-123456', }, @@ -146,6 +149,7 @@ export = nodeunit.testCase({ test.deepEqual(result, { availabilityZones: [], + vpcCidrBlock: '1.1.1.1/16', isolatedSubnetIds: undefined, isolatedSubnetNames: undefined, isolatedSubnetRouteTableIds: undefined, @@ -164,6 +168,7 @@ export = nodeunit.testCase({ subnetId: 'sub-123456', availabilityZone: 'bermuda-triangle-1337', routeTableId: 'rtb-123456', + cidr: '1.1.1.1/24', }, ], }, @@ -175,6 +180,7 @@ export = nodeunit.testCase({ subnetId: 'sub-789012', availabilityZone: 'bermuda-triangle-1337', routeTableId: 'rtb-789012', + cidr: '1.1.2.1/24', }, ], }, @@ -230,6 +236,7 @@ export = nodeunit.testCase({ // THEN test.deepEqual(result, { availabilityZones: [], + vpcCidrBlock: '1.1.1.1/16', isolatedSubnetIds: undefined, isolatedSubnetNames: undefined, isolatedSubnetRouteTableIds: undefined, @@ -248,6 +255,7 @@ export = nodeunit.testCase({ subnetId: 'sub-123456', availabilityZone: 'bermuda-triangle-1337', routeTableId: 'rtb-123456', + cidr: undefined, }, ], }, @@ -264,10 +272,10 @@ export = nodeunit.testCase({ // GIVEN mockVpcLookup(test, { subnets: [ - { SubnetId: 'pri-sub-in-1b', AvailabilityZone: 'us-west-1b', MapPublicIpOnLaunch: false }, - { SubnetId: 'pub-sub-in-1c', AvailabilityZone: 'us-west-1c', MapPublicIpOnLaunch: true }, - { SubnetId: 'pub-sub-in-1b', AvailabilityZone: 'us-west-1b', MapPublicIpOnLaunch: true }, - { SubnetId: 'pub-sub-in-1a', AvailabilityZone: 'us-west-1a', MapPublicIpOnLaunch: true }, + { SubnetId: 'pri-sub-in-1b', AvailabilityZone: 'us-west-1b', MapPublicIpOnLaunch: false, CidrBlock: '1.1.1.1/24', }, + { SubnetId: 'pub-sub-in-1c', AvailabilityZone: 'us-west-1c', MapPublicIpOnLaunch: true, CidrBlock: '1.1.2.1/24' }, + { SubnetId: 'pub-sub-in-1b', AvailabilityZone: 'us-west-1b', MapPublicIpOnLaunch: true, CidrBlock: '1.1.3.1/24' }, + { SubnetId: 'pub-sub-in-1a', AvailabilityZone: 'us-west-1a', MapPublicIpOnLaunch: true, CidrBlock: '1.1.4.1/24' }, ], routeTables: [ { Associations: [{ Main: true }], RouteTableId: 'rtb-123' }, @@ -283,6 +291,7 @@ export = nodeunit.testCase({ // THEN test.deepEqual(result, { availabilityZones: [], + vpcCidrBlock: '1.1.1.1/16', isolatedSubnetIds: undefined, isolatedSubnetNames: undefined, isolatedSubnetRouteTableIds: undefined, @@ -301,6 +310,7 @@ export = nodeunit.testCase({ subnetId: 'pri-sub-in-1b', availabilityZone: 'us-west-1b', routeTableId: 'rtb-123', + cidr: '1.1.1.1/24', }, ], }, @@ -312,16 +322,19 @@ export = nodeunit.testCase({ subnetId: 'pub-sub-in-1a', availabilityZone: 'us-west-1a', routeTableId: 'rtb-123', + cidr: '1.1.4.1/24', }, { subnetId: 'pub-sub-in-1b', availabilityZone: 'us-west-1b', routeTableId: 'rtb-123', + cidr: '1.1.3.1/24', }, { subnetId: 'pub-sub-in-1c', availabilityZone: 'us-west-1c', routeTableId: 'rtb-123', + cidr: '1.1.2.1/24', }, ], }, @@ -368,6 +381,7 @@ export = nodeunit.testCase({ test.deepEqual(result, { availabilityZones: [], + vpcCidrBlock: '1.1.1.1/16', isolatedSubnetIds: undefined, isolatedSubnetNames: undefined, isolatedSubnetRouteTableIds: undefined, @@ -386,6 +400,7 @@ export = nodeunit.testCase({ subnetId: 'pri-sub-in-1b', availabilityZone: 'us-west-1b', routeTableId: 'rtb-123', + cidr: undefined, }, ], }, @@ -397,16 +412,19 @@ export = nodeunit.testCase({ subnetId: 'pub-sub-in-1a', availabilityZone: 'us-west-1a', routeTableId: 'rtb-123', + cidr: undefined, }, { subnetId: 'pub-sub-in-1b', availabilityZone: 'us-west-1b', routeTableId: 'rtb-123', + cidr: undefined, }, { subnetId: 'pub-sub-in-1c', availabilityZone: 'us-west-1c', routeTableId: 'rtb-123', + cidr: undefined, }, ], }, @@ -431,7 +449,7 @@ function mockVpcLookup(test: nodeunit.Test, options: VpcLookupOptions) { AWS.mock('EC2', 'describeVpcs', (params: aws.EC2.DescribeVpcsRequest, cb: AwsCallback) => { test.deepEqual(params.Filters, [{ Name: 'foo', Values: ['bar'] }]); - return cb(null, { Vpcs: [{ VpcId }] }); + return cb(null, { Vpcs: [{ VpcId, CidrBlock: '1.1.1.1/16' }] }); }); AWS.mock('EC2', 'describeSubnets', (params: aws.EC2.DescribeSubnetsRequest, cb: AwsCallback) => { diff --git a/packages/aws-cdk/test/context-providers/test.vpcs.ts b/packages/aws-cdk/test/context-providers/test.vpcs.ts index 0e95c11ca3c11..93acf1e364097 100644 --- a/packages/aws-cdk/test/context-providers/test.vpcs.ts +++ b/packages/aws-cdk/test/context-providers/test.vpcs.ts @@ -44,6 +44,7 @@ export = nodeunit.testCase({ // THEN test.deepEqual(result, { vpcId: 'vpc-1234567', + vpcCidrBlock: '1.1.1.1/16', availabilityZones: ['bermuda-triangle-1337'], isolatedSubnetIds: undefined, isolatedSubnetNames: undefined, @@ -129,6 +130,7 @@ export = nodeunit.testCase({ // THEN test.deepEqual(result, { vpcId: 'vpc-1234567', + vpcCidrBlock: '1.1.1.1/16', availabilityZones: ['bermuda-triangle-1337'], isolatedSubnetIds: undefined, isolatedSubnetNames: undefined, @@ -190,6 +192,7 @@ export = nodeunit.testCase({ // THEN test.deepEqual(result, { vpcId: 'vpc-1234567', + vpcCidrBlock: '1.1.1.1/16', availabilityZones: ['bermuda-triangle-1337'], isolatedSubnetIds: undefined, isolatedSubnetNames: undefined, @@ -220,7 +223,7 @@ function mockVpcLookup(test: nodeunit.Test, options: VpcLookupOptions) { AWS.mock('EC2', 'describeVpcs', (params: aws.EC2.DescribeVpcsRequest, cb: AwsCallback) => { test.deepEqual(params.Filters, [{ Name: 'foo', Values: ['bar'] }]); - return cb(null, { Vpcs: [{ VpcId }] }); + return cb(null, { Vpcs: [{ VpcId, CidrBlock: '1.1.1.1/16' }] }); }); AWS.mock('EC2', 'describeSubnets', (params: aws.EC2.DescribeSubnetsRequest, cb: AwsCallback) => { From 01800cfd848b84a3d7646738128946ef58eebf60 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Thu, 19 Dec 2019 14:54:46 +0100 Subject: [PATCH 03/80] fix(tests): flaky integration tests in release pipeline (#5485) If multiple similar pipelines (patch pipelines, test pipelines) are running the same CodeBuild test, they interfere with each other. In particular, the 'test' pipeline (which tests modifications to the pipeline before they are applied to the real pipeline) is triggered off of the same branch and takes about as long as the real pipeline, so tests are likely to execute at the same time and touch the same stacks, thereby causing one of the tests to fail (and our release pipeline to be flaky). They two pipelines will have different CodeBuild projects though, so name the stacks that are being touched after the CodeBuild project, thereby ensuring isolation. --- packages/aws-cdk/test/integ/cli/common.bash | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/aws-cdk/test/integ/cli/common.bash b/packages/aws-cdk/test/integ/cli/common.bash index ea127f1a72031..2f86a892e857f 100644 --- a/packages/aws-cdk/test/integ/cli/common.bash +++ b/packages/aws-cdk/test/integ/cli/common.bash @@ -13,7 +13,16 @@ fi if [[ "${STACK_NAME_PREFIX:-}" == "" ]]; then - if ${IS_CANARY:-false}; then + # Make the stack names unique based on the codebuild project name + # (if it exists). This prevents multiple codebuild projects stomping + # on each other's stacks and failing them. + # + # The get codebuild project name from the ID: PROJECT_NAME:1238a83 + CODEBUILD_PROJECT=$(echo ${CODEBUILD_BUILD_ID:-} | cut -d: -f 1) + + if [[ "${CODEBUILD_PROJECT:-}" != "" ]]; then + export STACK_NAME_PREFIX="${CODEBUILD_PROJECT}" + elif ${IS_CANARY:-false}; then export STACK_NAME_PREFIX=cdk-toolkit-canary else export STACK_NAME_PREFIX=cdk-toolkit-integration From 746ba3247a86a0cca60a1bb7897dd12848d904e9 Mon Sep 17 00:00:00 2001 From: Jonathan Goldwasser Date: Thu, 19 Dec 2019 19:57:47 +0100 Subject: [PATCH 04/80] feat(lambda): configuration for async invocations (#5299) * partial import with failures to address * Rewrite incomplete types to "Json" Incomplete type definitions are unusable, so we translate them into "Json" to be able to something semi-useful. Fix some broken terminology in the specs and code generator. * replace incomplete types as empty object * clean up CHANGELOG * remove conditional flow check for complex types without properties * update tests * fix codebuild tests * feat(lambda): configuration options for async invocations Add support for destinations, maximum event age and maximum retry attempts for asynchronous invocations. See https://docs.aws.amazon.com/lambda/latest/dg/invocation-async.html * add test for default event bus * add scope * PR feedback * update package.json * update README * maxEventAge and retryAttempts * integ test coverage * rename to XxxDestination * invoke config on version * setAsyncInvokeConfig * README * unnecessary default on qualifier * JSDoc * CF dependency for alias + integ test * addVersion * configureAsyncInvoke + imported alias/version * better error message for configureAsyncInvoke * 1.19.0 in aws-lambda-destinations * addVersion signature * remove apigateway file * remove committed merge conflict * eslint in lambda-destinations * Update documentation for addVersion() * Add node's path to the error message * Change qualifier to protected access * Missed changing access in a few more places * missed one more place where qualifier needs to be protected --- .../aws-lambda-destinations/.gitignore | 16 + .../aws-lambda-destinations/.npmignore | 20 + .../@aws-cdk/aws-lambda-destinations/LICENSE | 201 ++++++++++ .../@aws-cdk/aws-lambda-destinations/NOTICE | 2 + .../aws-lambda-destinations/README.md | 39 ++ .../lib/event-bridge.ts | 32 ++ .../aws-lambda-destinations/lib/index.ts | 4 + .../aws-lambda-destinations/lib/lambda.ts | 22 ++ .../aws-lambda-destinations/lib/sns.ts | 23 ++ .../aws-lambda-destinations/lib/sqs.ts | 23 ++ .../aws-lambda-destinations/package.json | 103 +++++ .../test/destinations.test.ts | 234 ++++++++++++ .../test/integ.destinations.expected.json | 358 ++++++++++++++++++ .../test/integ.destinations.ts | 67 ++++ packages/@aws-cdk/aws-lambda/lib/alias.ts | 19 +- .../@aws-cdk/aws-lambda/lib/destination.ts | 22 ++ .../aws-lambda/lib/event-invoke-config.ts | 96 +++++ .../@aws-cdk/aws-lambda/lib/function-base.ts | 37 ++ packages/@aws-cdk/aws-lambda/lib/function.ts | 22 +- packages/@aws-cdk/aws-lambda/lib/index.ts | 2 + .../@aws-cdk/aws-lambda/lib/lambda-version.ts | 28 +- packages/@aws-cdk/aws-lambda/package.json | 3 +- .../@aws-cdk/aws-lambda/test/test.alias.ts | 95 +++++ .../aws-lambda/test/test.lambda-version.ts | 85 ++++- .../@aws-cdk/aws-lambda/test/test.lambda.ts | 109 ++++++ packages/decdk/package.json | 1 + 26 files changed, 1650 insertions(+), 13 deletions(-) create mode 100644 packages/@aws-cdk/aws-lambda-destinations/.gitignore create mode 100644 packages/@aws-cdk/aws-lambda-destinations/.npmignore create mode 100644 packages/@aws-cdk/aws-lambda-destinations/LICENSE create mode 100644 packages/@aws-cdk/aws-lambda-destinations/NOTICE create mode 100644 packages/@aws-cdk/aws-lambda-destinations/README.md create mode 100644 packages/@aws-cdk/aws-lambda-destinations/lib/event-bridge.ts create mode 100644 packages/@aws-cdk/aws-lambda-destinations/lib/index.ts create mode 100644 packages/@aws-cdk/aws-lambda-destinations/lib/lambda.ts create mode 100644 packages/@aws-cdk/aws-lambda-destinations/lib/sns.ts create mode 100644 packages/@aws-cdk/aws-lambda-destinations/lib/sqs.ts create mode 100644 packages/@aws-cdk/aws-lambda-destinations/package.json create mode 100644 packages/@aws-cdk/aws-lambda-destinations/test/destinations.test.ts create mode 100644 packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.expected.json create mode 100644 packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.ts create mode 100644 packages/@aws-cdk/aws-lambda/lib/destination.ts create mode 100644 packages/@aws-cdk/aws-lambda/lib/event-invoke-config.ts diff --git a/packages/@aws-cdk/aws-lambda-destinations/.gitignore b/packages/@aws-cdk/aws-lambda-destinations/.gitignore new file mode 100644 index 0000000000000..205e21fe7353b --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-destinations/.gitignore @@ -0,0 +1,16 @@ +*.js +tsconfig.json +tslint.json +*.js.map +*.d.ts +*.generated.ts +dist +lib/generated/resources.ts +.jsii + +.LAST_BUILD +.nyc_output +coverage +.nycrc +.LAST_PACKAGE +*.snk \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda-destinations/.npmignore b/packages/@aws-cdk/aws-lambda-destinations/.npmignore new file mode 100644 index 0000000000000..5f2fbb23042e4 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-destinations/.npmignore @@ -0,0 +1,20 @@ +# Don't include original .ts files when doing `npm pack` +*.ts +!*.d.ts +coverage +.nyc_output +*.tgz + +dist +.LAST_PACKAGE +.LAST_BUILD +!*.js + +# Include .jsii +!.jsii + +*.snk + +*.tsbuildinfo + +tsconfig.json \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda-destinations/LICENSE b/packages/@aws-cdk/aws-lambda-destinations/LICENSE new file mode 100644 index 0000000000000..46c185646b439 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-destinations/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License 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. diff --git a/packages/@aws-cdk/aws-lambda-destinations/NOTICE b/packages/@aws-cdk/aws-lambda-destinations/NOTICE new file mode 100644 index 0000000000000..8585168af8b7d --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-destinations/NOTICE @@ -0,0 +1,2 @@ +AWS Cloud Development Kit (AWS CDK) +Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/@aws-cdk/aws-lambda-destinations/README.md b/packages/@aws-cdk/aws-lambda-destinations/README.md new file mode 100644 index 0000000000000..dba4ad1b4590f --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-destinations/README.md @@ -0,0 +1,39 @@ +## Amazon Lambda Destinations Library + + +--- + +![Stability: Stable](https://img.shields.io/badge/stability-Stable-success.svg?style=for-the-badge) + + +--- + + +This library provides constructs for adding destinations to a Lambda function. +Destinations can be added by specifying the `onFailure` or `onSuccess` props when creating a function or alias. + +## Destinations + +The following destinations are supported + +* Lambda function +* SQS queue +* SNS topic +* EventBridge event bus + +Example with a SNS topic for sucessful invocations: + +```ts +import lambda = require('@aws-cdk/aws-lambda'); +import destinations = require('@aws-cdk/aws-lambda-destinations'); +import sns = require('@aws-cdk/aws-sns'); + +const myTopic = new sns.Topic(this, 'Topic'); + +const myFn = new lambda.Function(this, 'Fn', { + // other props + onSuccess: new destinations.SnsDestionation(myTopic) +}) +``` + +See also [Configuring Destinations for Asynchronous Invocation](https://docs.aws.amazon.com/lambda/latest/dg/invocation-async.html#invocation-async-destinations). diff --git a/packages/@aws-cdk/aws-lambda-destinations/lib/event-bridge.ts b/packages/@aws-cdk/aws-lambda-destinations/lib/event-bridge.ts new file mode 100644 index 0000000000000..b030feee09f94 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-destinations/lib/event-bridge.ts @@ -0,0 +1,32 @@ +import * as events from '@aws-cdk/aws-events'; +import * as lambda from '@aws-cdk/aws-lambda'; +import { Construct, Stack } from '@aws-cdk/core'; + +/** + * Use an Event Bridge event bus as a Lambda destination. + * + * If no event bus is specified, the default event bus is used. + */ +export class EventBridgeDestination implements lambda.IDestination { + /** + * @default - use the default event bus + */ + constructor(private readonly eventBus?: events.IEventBus) { + } + + /** + * Returns a destination configuration + */ + public bind(_scope: Construct, fn: lambda.IFunction): lambda.DestinationConfig { + // deduplicated automatically + events.EventBus.grantPutEvents(fn); // Cannot restrict to a specific resource + + return { + destination: this.eventBus && this.eventBus.eventBusArn || Stack.of(fn).formatArn({ + service: 'events', + resource: 'event-bus', + resourceName: 'default' + }) + }; + } +} diff --git a/packages/@aws-cdk/aws-lambda-destinations/lib/index.ts b/packages/@aws-cdk/aws-lambda-destinations/lib/index.ts new file mode 100644 index 0000000000000..cd7ea172266aa --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-destinations/lib/index.ts @@ -0,0 +1,4 @@ +export * from './event-bridge'; +export * from './lambda'; +export * from './sns'; +export * from './sqs'; diff --git a/packages/@aws-cdk/aws-lambda-destinations/lib/lambda.ts b/packages/@aws-cdk/aws-lambda-destinations/lib/lambda.ts new file mode 100644 index 0000000000000..2d52a4d1f17a1 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-destinations/lib/lambda.ts @@ -0,0 +1,22 @@ +import * as lambda from '@aws-cdk/aws-lambda'; +import { Construct } from '@aws-cdk/core'; + +/** + * Use a Lambda function as a Lambda destination + */ +export class LambdaDestination implements lambda.IDestination { + constructor(private readonly fn: lambda.IFunction) { + } + + /** + * Returns a destination configuration + */ + public bind(_scope: Construct, fn: lambda.IFunction): lambda.DestinationConfig { + // deduplicated automatically + this.fn.grantInvoke(fn); + + return { + destination: this.fn.functionArn + }; + } +} diff --git a/packages/@aws-cdk/aws-lambda-destinations/lib/sns.ts b/packages/@aws-cdk/aws-lambda-destinations/lib/sns.ts new file mode 100644 index 0000000000000..ba796c8831505 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-destinations/lib/sns.ts @@ -0,0 +1,23 @@ +import * as lambda from '@aws-cdk/aws-lambda'; +import * as sns from '@aws-cdk/aws-sns'; +import { Construct } from '@aws-cdk/core'; + +/** + * Use a SNS topic as a Lambda destination + */ +export class SnsDestination implements lambda.IDestination { + constructor(private readonly topic: sns.ITopic) { + } + + /** + * Returns a destination configuration + */ + public bind(_scope: Construct, fn: lambda.IFunction): lambda.DestinationConfig { + // deduplicated automatically + this.topic.grantPublish(fn); + + return { + destination: this.topic.topicArn + }; + } +} diff --git a/packages/@aws-cdk/aws-lambda-destinations/lib/sqs.ts b/packages/@aws-cdk/aws-lambda-destinations/lib/sqs.ts new file mode 100644 index 0000000000000..e317d4bc36709 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-destinations/lib/sqs.ts @@ -0,0 +1,23 @@ +import * as lambda from '@aws-cdk/aws-lambda'; +import * as sqs from '@aws-cdk/aws-sqs'; +import { Construct } from '@aws-cdk/core'; + +/** + * Use a SQS queue as a Lambda destination + */ +export class SqsDestination implements lambda.IDestination { + constructor(private readonly queue: sqs.IQueue) { + } + + /** + * Returns a destination configuration + */ + public bind(_scope: Construct, fn: lambda.IFunction): lambda.DestinationConfig { + // deduplicated automatically + this.queue.grantSendMessages(fn); + + return { + destination: this.queue.queueArn + }; + } +} diff --git a/packages/@aws-cdk/aws-lambda-destinations/package.json b/packages/@aws-cdk/aws-lambda-destinations/package.json new file mode 100644 index 0000000000000..e2d3958cf4a26 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-destinations/package.json @@ -0,0 +1,103 @@ +{ + "name": "@aws-cdk/aws-lambda-destinations", + "version": "1.19.0", + "description": "CDK Destinations Constructs for AWS Lambda", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "jsii": { + "outdir": "dist", + "targets": { + "java": { + "package": "software.amazon.awscdk.services.lambda.destinations", + "maven": { + "groupId": "software.amazon.awscdk", + "artifactId": "lambda-destinations" + } + }, + "dotnet": { + "namespace": "Amazon.CDK.AWS.Lambda.Destinations", + "packageId": "Amazon.CDK.AWS.Lambda.Destinations", + "signAssembly": true, + "assemblyOriginatorKeyFile": "../../key.snk", + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/master/logo/default-256-dark.png" + }, + "python": { + "distName": "aws-cdk.aws-lambda-destinations", + "module": "aws_cdk.aws_lambda_destinations" + } + } + }, + "repository": { + "type": "git", + "url": "https://github.com/aws/aws-cdk.git", + "directory": "packages/@aws-cdk/aws-lambda-destinations" + }, + "scripts": { + "build": "cdk-build", + "watch": "cdk-watch", + "lint": "cdk-lint", + "test": "cdk-test", + "integ": "cdk-integ", + "pkglint": "pkglint -f", + "package": "cdk-package", + "awslint": "cdk-awslint", + "cfn2ts": "cfn2ts", + "build+test+package": "npm run build+test && npm run package", + "build+test": "npm run build && npm test", + "compat": "cdk-compat" + }, + "keywords": [ + "aws", + "cdk", + "constructs", + "lambda" + ], + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com", + "organization": true + }, + "jest": { + "moduleFileExtensions": [ + "js" + ], + "coverageThreshold": { + "global": { + "branches": 60, + "statements": 80 + } + } + }, + "license": "Apache-2.0", + "devDependencies": { + "@aws-cdk/assert": "1.19.0", + "cdk-build-tools": "1.19.0", + "cdk-integ-tools": "1.19.0", + "cfn2ts": "1.19.0", + "jest": "^24.9.0", + "pkglint": "1.19.0" + }, + "dependencies": { + "@aws-cdk/aws-events": "1.19.0", + "@aws-cdk/aws-lambda": "1.19.0", + "@aws-cdk/aws-sns": "1.19.0", + "@aws-cdk/aws-sqs": "1.19.0", + "@aws-cdk/core": "1.19.0" + }, + "homepage": "https://github.com/aws/aws-cdk", + "peerDependencies": { + "@aws-cdk/aws-events": "1.19.0", + "@aws-cdk/aws-lambda": "1.19.0", + "@aws-cdk/aws-sns": "1.19.0", + "@aws-cdk/aws-sqs": "1.19.0", + "@aws-cdk/core": "1.19.0" + }, + "engines": { + "node": ">= 10.3.0" + }, + "stability": "stable", + "awslint": { + "exclude": [ + ] + } +} diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.test.ts b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.test.ts new file mode 100644 index 0000000000000..6e725b9ada507 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.test.ts @@ -0,0 +1,234 @@ +import '@aws-cdk/assert/jest'; +import * as events from '@aws-cdk/aws-events'; +import * as lambda from '@aws-cdk/aws-lambda'; +import * as sns from '@aws-cdk/aws-sns'; +import * as sqs from '@aws-cdk/aws-sqs'; +import { Stack } from '@aws-cdk/core'; +import * as destinations from '../lib'; + +let stack: Stack; +beforeEach(() => { + stack = new Stack(); +}); + +const lambdaProps = { + code: new lambda.InlineCode('foo'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_10_X, +}; + +test('event bus as destination', () => { + // GIVEN + const eventBus = new events.EventBus(stack, 'EventBus'); + + // WHEN + new lambda.Function(stack, 'Function', { + ...lambdaProps, + onSuccess: new destinations.EventBridgeDestination(eventBus) + }); + + // THEN + expect(stack).toHaveResource('AWS::Lambda::EventInvokeConfig', { + DestinationConfig: { + OnSuccess: { + Destination: { + 'Fn::GetAtt': [ + 'EventBus7B8748AA', + 'Arn' + ] + } + } + } + }); + + expect(stack).toHaveResource('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'events:PutEvents', + Effect: 'Allow', + Resource: '*' + } + ], + Version: '2012-10-17' + } + }); +}); + +test('event bus as destination defaults to default event bus', () => { + // WHEN + new lambda.Function(stack, 'Function', { + ...lambdaProps, + onSuccess: new destinations.EventBridgeDestination() + }); + + // THEN + expect(stack).toHaveResource('AWS::Lambda::EventInvokeConfig', { + DestinationConfig: { + OnSuccess: { + Destination: { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition' + }, + ':events:', + { + Ref: 'AWS::Region' + }, + ':', + { + Ref: 'AWS::AccountId' + }, + ':event-bus/default' + ] + ] + } + } + } + }); + + expect(stack).toHaveResource('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'events:PutEvents', + Effect: 'Allow', + Resource: '*' + } + ], + Version: '2012-10-17' + } + }); +}); + +test('lambda as destination', () => { + // GIVEN + const successLambda = new lambda.Function(stack, 'SuccessFunction', { + ...lambdaProps, + }); + + // WHEN + new lambda.Function(stack, 'Function', { + ...lambdaProps, + onSuccess: new destinations.LambdaDestination(successLambda) + }); + + // THEN + expect(stack).toHaveResource('AWS::Lambda::EventInvokeConfig', { + DestinationConfig: { + OnSuccess: { + Destination: { + 'Fn::GetAtt': [ + 'SuccessFunction93C61D39', + 'Arn' + ] + } + } + } + }); + + expect(stack).toHaveResource('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'lambda:InvokeFunction', + Effect: 'Allow', + Resource: { + 'Fn::GetAtt': [ + 'SuccessFunction93C61D39', + 'Arn' + ] + } + } + ], + Version: '2012-10-17' + } + }); +}); + +test('sns as destination', () => { + // GIVEN + const topic = new sns.Topic(stack, 'Topic'); + + // WHEN + new lambda.Function(stack, 'Function', { + ...lambdaProps, + onSuccess: new destinations.SnsDestination(topic) + }); + + // THEN + expect(stack).toHaveResource('AWS::Lambda::EventInvokeConfig', { + DestinationConfig: { + OnSuccess: { + Destination: { + Ref: 'TopicBFC7AF6E' + } + } + } + }); + + expect(stack).toHaveResource('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'sns:Publish', + Effect: 'Allow', + Resource: { + Ref: 'TopicBFC7AF6E' + } + } + ], + Version: '2012-10-17' + } + }); +}); + +test('sqs as destination', () => { + // GIVEN + const queue = new sqs.Queue(stack, 'Queue'); + + // WHEN + new lambda.Function(stack, 'Function', { + ...lambdaProps, + onSuccess: new destinations.SqsDestination(queue) + }); + + // THEN + expect(stack).toHaveResource('AWS::Lambda::EventInvokeConfig', { + DestinationConfig: { + OnSuccess: { + Destination: { + 'Fn::GetAtt': [ + 'Queue4A7E3555', + 'Arn' + ] + } + } + } + }); + + expect(stack).toHaveResource('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: [ + 'sqs:SendMessage', + 'sqs:GetQueueAttributes', + 'sqs:GetQueueUrl' + ], + Effect: 'Allow', + Resource: { + 'Fn::GetAtt': [ + 'Queue4A7E3555', + 'Arn' + ] + } + } + ], + Version: '2012-10-17' + } + }); +}); diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.expected.json b/packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.expected.json new file mode 100644 index 0000000000000..c9c1d8cd64563 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.expected.json @@ -0,0 +1,358 @@ +{ + "Resources": { + "TopicBFC7AF6E": { + "Type": "AWS::SNS::Topic" + }, + "Queue4A7E3555": { + "Type": "AWS::SQS::Queue" + }, + "SnsSqsServiceRole869866F7": { + "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" + ] + ] + } + ] + } + }, + "SnsSqsServiceRoleDefaultPolicy82E7B09F": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sns:Publish", + "Effect": "Allow", + "Resource": { + "Ref": "TopicBFC7AF6E" + } + }, + { + "Action": [ + "sqs:SendMessage", + "sqs:GetQueueAttributes", + "sqs:GetQueueUrl" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "Queue4A7E3555", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "SnsSqsServiceRoleDefaultPolicy82E7B09F", + "Roles": [ + { + "Ref": "SnsSqsServiceRole869866F7" + } + ] + } + }, + "SnsSqsC4810B27": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "exports.handler = async (event) => {\n if (event === 'OK') return 'success';\n throw new Error('failure');\n };" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "SnsSqsServiceRole869866F7", + "Arn" + ] + }, + "Runtime": "nodejs10.x" + }, + "DependsOn": [ + "SnsSqsServiceRoleDefaultPolicy82E7B09F", + "SnsSqsServiceRole869866F7" + ] + }, + "SnsSqsEventInvokeConfigDC664368": { + "Type": "AWS::Lambda::EventInvokeConfig", + "Properties": { + "FunctionName": { + "Ref": "SnsSqsC4810B27" + }, + "Qualifier": "$LATEST", + "DestinationConfig": { + "OnFailure": { + "Destination": { + "Ref": "TopicBFC7AF6E" + } + }, + "OnSuccess": { + "Destination": { + "Fn::GetAtt": [ + "Queue4A7E3555", + "Arn" + ] + } + } + }, + "MaximumEventAgeInSeconds": 10800, + "MaximumRetryAttempts": 1 + } + }, + "SnsSqsVersionMySpecialVersion08136BD6": { + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "SnsSqsC4810B27" + } + } + }, + "OnSuccesServiceRole75E399CF": { + "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" + ] + ] + } + ] + } + }, + "OnSucces8F9C946B": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "exports.handler = async (event) => {\n console.log(event);\n };" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "OnSuccesServiceRole75E399CF", + "Arn" + ] + }, + "Runtime": "nodejs10.x" + }, + "DependsOn": [ + "OnSuccesServiceRole75E399CF" + ] + }, + "EventBusLambdaServiceRole9BC8901F": { + "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" + ] + ] + } + ] + } + }, + "EventBusLambdaServiceRoleDefaultPolicy5BB7FEB6": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "events:PutEvents", + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": "lambda:InvokeFunction", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "OnSucces8F9C946B", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "EventBusLambdaServiceRoleDefaultPolicy5BB7FEB6", + "Roles": [ + { + "Ref": "EventBusLambdaServiceRole9BC8901F" + } + ] + } + }, + "EventBusLambda4AA26499": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "exports.handler = async (event) => {\n if (event === 'OK') return 'success';\n throw new Error('failure');\n };" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "EventBusLambdaServiceRole9BC8901F", + "Arn" + ] + }, + "Runtime": "nodejs10.x" + }, + "DependsOn": [ + "EventBusLambdaServiceRoleDefaultPolicy5BB7FEB6", + "EventBusLambdaServiceRole9BC8901F" + ] + }, + "EventBusLambdaEventInvokeConfig52CF8B9B": { + "Type": "AWS::Lambda::EventInvokeConfig", + "Properties": { + "FunctionName": { + "Ref": "EventBusLambda4AA26499" + }, + "Qualifier": "$LATEST", + "DestinationConfig": { + "OnFailure": { + "Destination": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":events:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":event-bus/default" + ] + ] + } + }, + "OnSuccess": { + "Destination": { + "Fn::GetAtt": [ + "OnSucces8F9C946B", + "Arn" + ] + } + } + } + } + }, + "MySpecialAliasC0F04207": { + "Type": "AWS::Lambda::Alias", + "Properties": { + "FunctionName": { + "Ref": "SnsSqsC4810B27" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "SnsSqsVersionMySpecialVersion08136BD6", + "Version" + ] + }, + "Name": "MySpecialAlias" + } + }, + "MySpecialAliasEventInvokeConfig05FF4E2F": { + "Type": "AWS::Lambda::EventInvokeConfig", + "Properties": { + "FunctionName": { + "Ref": "SnsSqsC4810B27" + }, + "Qualifier": { + "Fn::Select": [ + 7, + { + "Fn::Split": [ + ":", + { + "Ref": "MySpecialAliasC0F04207" + } + ] + } + ] + }, + "DestinationConfig": { + "OnFailure": { + "Destination": { + "Ref": "TopicBFC7AF6E" + } + }, + "OnSuccess": { + "Destination": { + "Fn::GetAtt": [ + "Queue4A7E3555", + "Arn" + ] + } + } + }, + "MaximumEventAgeInSeconds": 7200, + "MaximumRetryAttempts": 0 + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.ts b/packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.ts new file mode 100644 index 0000000000000..425b2d9cd001e --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.ts @@ -0,0 +1,67 @@ +import * as lambda from '@aws-cdk/aws-lambda'; +import * as sns from '@aws-cdk/aws-sns'; +import * as sqs from '@aws-cdk/aws-sqs'; +import { App, Construct, Duration, Stack, StackProps } from '@aws-cdk/core'; +import * as destinations from '../lib'; + +// After deploy, test with: +// aws lambda invoke --function-name --invocation-type Event --payload '"OK"' response.json +// aws lambda invoke --function-name --invocation-type Event --payload '"NOT OK"' response.json + +class TestStack extends Stack { + constructor(scope: Construct, id: string, props?: StackProps) { + super(scope, id, props); + + const topic = new sns.Topic(this, 'Topic'); + const queue = new sqs.Queue(this, 'Queue'); + + const fn = new lambda.Function(this, 'SnsSqs', { + runtime: lambda.Runtime.NODEJS_10_X, + handler: 'index.handler', + code: lambda.Code.fromInline(`exports.handler = async (event) => { + if (event === 'OK') return 'success'; + throw new Error('failure'); + };`), + onFailure: new destinations.SnsDestination(topic), + onSuccess: new destinations.SqsDestination(queue), + maxEventAge: Duration.hours(3), + retryAttempts: 1 + }); + + const onSuccessLambda = new lambda.Function(this, 'OnSucces', { + runtime: lambda.Runtime.NODEJS_10_X, + handler: 'index.handler', + code: lambda.Code.fromInline(`exports.handler = async (event) => { + console.log(event); + };`), + }); + + new lambda.Function(this, 'EventBusLambda', { + runtime: lambda.Runtime.NODEJS_10_X, + handler: 'index.handler', + code: lambda.Code.fromInline(`exports.handler = async (event) => { + if (event === 'OK') return 'success'; + throw new Error('failure'); + };`), + onFailure: new destinations.EventBridgeDestination(), + onSuccess: new destinations.LambdaDestination(onSuccessLambda), + }); + + const version = fn.addVersion('MySpecialVersion'); + + new lambda.Alias(this, 'MySpecialAlias', { + aliasName: 'MySpecialAlias', + version, + onSuccess: new destinations.SqsDestination(queue), + onFailure: new destinations.SnsDestination(topic), + maxEventAge: Duration.hours(2), + retryAttempts: 0 + }); + } +} + +const app = new App(); + +new TestStack(app, 'aws-cdk-lambda-destinations'); + +app.synth(); diff --git a/packages/@aws-cdk/aws-lambda/lib/alias.ts b/packages/@aws-cdk/aws-lambda/lib/alias.ts index ac348654e00ea..d4dc36cb670c3 100644 --- a/packages/@aws-cdk/aws-lambda/lib/alias.ts +++ b/packages/@aws-cdk/aws-lambda/lib/alias.ts @@ -1,7 +1,8 @@ import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import { Construct } from '@aws-cdk/core'; +import { EventInvokeConfigOptions } from './event-invoke-config'; import { IFunction, QualifiedFunctionBase } from './function-base'; -import { IVersion } from './lambda-version'; +import { extractQualifierFromArn, IVersion } from './lambda-version'; import { CfnAlias } from './lambda.generated'; export interface IAlias extends IFunction { @@ -21,7 +22,7 @@ export interface IAlias extends IFunction { /** * Properties for a new Lambda alias */ -export interface AliasProps { +export interface AliasProps extends EventInvokeConfigOptions { /** * Description for the alias * @@ -88,6 +89,7 @@ export class Alias extends QualifiedFunctionBase implements IAlias { public readonly role = attrs.aliasVersion.role; protected readonly canCreatePermissions = false; + protected readonly qualifier = attrs.aliasName; } return new Imported(scope, id); } @@ -118,6 +120,8 @@ export class Alias extends QualifiedFunctionBase implements IAlias { */ public readonly functionArn: string; + protected readonly qualifier: string; + protected readonly canCreatePermissions: boolean = true; constructor(scope: Construct, id: string, props: AliasProps) { @@ -145,6 +149,17 @@ export class Alias extends QualifiedFunctionBase implements IAlias { sep: ':', }); + this.qualifier = extractQualifierFromArn(alias.ref); + + if (props.onFailure || props.onSuccess || props.maxEventAge || props.retryAttempts !== undefined) { + this.configureAsyncInvoke({ + onFailure: props.onFailure, + onSuccess: props.onSuccess, + maxEventAge: props.maxEventAge, + retryAttempts: props.retryAttempts, + }); + } + // ARN parsing splits on `:`, so we can only get the function's name from the ARN as resourceName... // And we're parsing it out (instead of using the underlying function directly) in order to have use of it incur // an implicit dependency on the resource. diff --git a/packages/@aws-cdk/aws-lambda/lib/destination.ts b/packages/@aws-cdk/aws-lambda/lib/destination.ts new file mode 100644 index 0000000000000..a7bce8862d406 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda/lib/destination.ts @@ -0,0 +1,22 @@ +import { Construct } from '@aws-cdk/core'; +import { IFunction } from './function-base'; + +/** + * A destination configuration + */ +export interface DestinationConfig { + /** + * The Amazon Resource Name (ARN) of the destination resource + */ + readonly destination: string; +} + +/** + * A Lambda destination + */ +export interface IDestination { + /** + * Binds this destination to the Lambda function + */ + bind(scope: Construct, fn: IFunction): DestinationConfig; +} diff --git a/packages/@aws-cdk/aws-lambda/lib/event-invoke-config.ts b/packages/@aws-cdk/aws-lambda/lib/event-invoke-config.ts new file mode 100644 index 0000000000000..64198e57deacf --- /dev/null +++ b/packages/@aws-cdk/aws-lambda/lib/event-invoke-config.ts @@ -0,0 +1,96 @@ +import { Construct, Duration, Resource } from '@aws-cdk/core'; +import { IDestination } from './destination'; +import { IFunction } from './function-base'; +import { CfnEventInvokeConfig } from './lambda.generated'; + +/** + * Options to add an EventInvokeConfig to a function. + */ +export interface EventInvokeConfigOptions { + /** + * The destination for failed invocations. + * + * @default - no destination + */ + readonly onFailure?: IDestination; + + /** + * The destination for successful invocations. + * + * @default - no destination + */ + readonly onSuccess?: IDestination; + + /** + * The maximum age of a request that Lambda sends to a function for + * processing. + * + * Minimum: 60 seconds + * Maximum: 6 hours + * + * @default Duration.hours(6) + */ + readonly maxEventAge?: Duration; + + /** + * The maximum number of times to retry when the function returns an error. + * + * Minimum: 0 + * Maximum: 2 + * + * @default 2 + */ + readonly retryAttempts?: number; +} + +/** + * Properties for an EventInvokeConfig + */ +export interface EventInvokeConfigProps extends EventInvokeConfigOptions { + /** + * The Lambda function + */ + readonly function: IFunction; + + /** + * The qualifier + * + * @default - latest version + */ + readonly qualifier?: string; +} + +/** + * Configure options for asynchronous invocation on a version or an alias + * + * By default, Lambda retries an asynchronous invocation twice if the function + * returns an error. It retains events in a queue for up to six hours. When an + * event fails all processing attempts or stays in the asynchronous invocation + * queue for too long, Lambda discards it. + */ +export class EventInvokeConfig extends Resource { + constructor(scope: Construct, id: string, props: EventInvokeConfigProps) { + super(scope, id); + + if (props.maxEventAge && (props.maxEventAge.toSeconds() < 60 || props.maxEventAge.toSeconds() > 21600)) { + throw new Error('`maximumEventAge` must represent a `Duration` that is between 60 and 21600 seconds.'); + } + + if (props.retryAttempts && (props.retryAttempts < 0 || props.retryAttempts > 2)) { + throw new Error('`retryAttempts` must be between 0 and 2.'); + } + + new CfnEventInvokeConfig(this, 'Resource', { + destinationConfig: props.onFailure || props.onSuccess + ? { + ...props.onFailure ? { onFailure: props.onFailure.bind(this, props.function) } : {}, + ...props.onSuccess ? { onSuccess: props.onSuccess.bind(this, props.function) } : {}, + } + : undefined, + functionName: props.function.functionName, + maximumEventAgeInSeconds: props.maxEventAge && props.maxEventAge.toSeconds(), + maximumRetryAttempts: props.retryAttempts !== undefined ? props.retryAttempts : undefined, + qualifier: props.qualifier || '$LATEST', + }); + } +} diff --git a/packages/@aws-cdk/aws-lambda/lib/function-base.ts b/packages/@aws-cdk/aws-lambda/lib/function-base.ts index 02a6fd06e787a..2207db443f1b4 100644 --- a/packages/@aws-cdk/aws-lambda/lib/function-base.ts +++ b/packages/@aws-cdk/aws-lambda/lib/function-base.ts @@ -2,6 +2,7 @@ import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; import { ConstructNode, IResource, Resource } from '@aws-cdk/core'; +import { EventInvokeConfig, EventInvokeConfigOptions } from './event-invoke-config'; import { IEventSource } from './event-source'; import { EventSourceMapping, EventSourceMappingOptions } from './event-source-mapping'; import { IVersion } from './lambda-version'; @@ -97,6 +98,11 @@ export interface IFunction extends IResource, ec2.IConnectable, iam.IGrantable { metricThrottles(props?: cloudwatch.MetricOptions): cloudwatch.Metric; addEventSource(source: IEventSource): void; + + /** + * Configures options for asynchronous invocation. + */ + configureAsyncInvoke(options: EventInvokeConfigOptions): void } /** @@ -290,6 +296,17 @@ export abstract class FunctionBase extends Resource implements IFunction { source.bind(this); } + public configureAsyncInvoke(options: EventInvokeConfigOptions): void { + if (this.node.tryFindChild('EventInvokeConfig') !== undefined) { + throw new Error(`An EventInvokeConfig has already been configured for the function at ${this.node.path}`); + } + + new EventInvokeConfig(this, 'EventInvokeConfig', { + function: this, + ...options + }); + } + private parsePermissionPrincipal(principal?: iam.IPrincipal) { if (!principal) { return undefined; @@ -315,11 +332,31 @@ export abstract class FunctionBase extends Resource implements IFunction { export abstract class QualifiedFunctionBase extends FunctionBase { public abstract readonly lambda: IFunction; + public readonly permissionsNode = this.node; + /** + * The qualifier of the version or alias of this function. + * A qualifier is the identifier that's appended to a version or alias ARN. + * @see https://docs.aws.amazon.com/lambda/latest/dg/API_GetFunctionConfiguration.html#API_GetFunctionConfiguration_RequestParameters + */ + protected abstract readonly qualifier: string; + public get latestVersion() { return this.lambda.latestVersion; } + + public configureAsyncInvoke(options: EventInvokeConfigOptions): void { + if (this.node.tryFindChild('EventInvokeConfig') !== undefined) { + throw new Error(`An EventInvokeConfig has already been configured for the qualified function at ${this.node.path}`); + } + + new EventInvokeConfig(this, 'EventInvokeConfig', { + function: this.lambda, + qualifier: this.qualifier, + ...options + }); + } } /** diff --git a/packages/@aws-cdk/aws-lambda/lib/function.ts b/packages/@aws-cdk/aws-lambda/lib/function.ts index fd071d9ee8690..6b3f9901b93ec 100644 --- a/packages/@aws-cdk/aws-lambda/lib/function.ts +++ b/packages/@aws-cdk/aws-lambda/lib/function.ts @@ -5,6 +5,7 @@ import * as logs from '@aws-cdk/aws-logs'; import * as sqs from '@aws-cdk/aws-sqs'; import { Construct, Duration, Fn, Lazy } from '@aws-cdk/core'; import { Code, CodeConfig } from './code'; +import { EventInvokeConfigOptions } from './event-invoke-config'; import { IEventSource } from './event-source'; import { FunctionAttributes, FunctionBase, IFunction } from './function-base'; import { Version } from './lambda-version'; @@ -33,7 +34,7 @@ export enum Tracing { DISABLED = "Disabled" } -export interface FunctionProps { +export interface FunctionProps extends EventInvokeConfigOptions { /** * The source code of your Lambda function. You can point to a file in an * Amazon Simple Storage Service (Amazon S3) bucket or specify your source @@ -494,6 +495,16 @@ export class Function extends FunctionBase { } props.code.bindToResource(resource); + + // Event Invoke Config + if (props.onFailure || props.onSuccess || props.maxEventAge || props.retryAttempts !== undefined) { + this.configureAsyncInvoke({ + onFailure: props.onFailure, + onSuccess: props.onSuccess, + maxEventAge: props.maxEventAge, + retryAttempts: props.retryAttempts, + }); + } } /** @@ -542,14 +553,21 @@ export class Function extends FunctionBase { * omit to skip validation. * @param description A description for this version. * @param provisionedExecutions A provisioned concurrency configuration for a function's version. + * @param asyncInvokeConfig configuration for this version when it is invoked asynchronously. * @returns A new Version object. */ - public addVersion(name: string, codeSha256?: string, description?: string, provisionedExecutions?: number): Version { + public addVersion( + name: string, + codeSha256?: string, + description?: string, + provisionedExecutions?: number, + asyncInvokeConfig: EventInvokeConfigOptions = {}): Version { return new Version(this, 'Version' + name, { lambda: this, codeSha256, description, provisionedConcurrentExecutions: provisionedExecutions, + ...asyncInvokeConfig, }); } diff --git a/packages/@aws-cdk/aws-lambda/lib/index.ts b/packages/@aws-cdk/aws-lambda/lib/index.ts index 7c3f5a48e8e87..f0a693da65974 100644 --- a/packages/@aws-cdk/aws-lambda/lib/index.ts +++ b/packages/@aws-cdk/aws-lambda/lib/index.ts @@ -9,6 +9,8 @@ export * from './lambda-version'; export * from './singleton-lambda'; export * from './event-source'; export * from './event-source-mapping'; +export * from './destination'; +export * from './event-invoke-config'; export * from './log-retention'; diff --git a/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts b/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts index 66b36aa0fc73d..bd5c132f89e44 100644 --- a/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts +++ b/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts @@ -1,5 +1,6 @@ import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import { Construct, Fn } from '@aws-cdk/core'; +import { EventInvokeConfigOptions } from './event-invoke-config'; import { Function } from './function'; import { IFunction, QualifiedFunctionBase } from './function-base'; import { CfnVersion } from './lambda.generated'; @@ -20,7 +21,7 @@ export interface IVersion extends IFunction { /** * Properties for a new Lambda version */ -export interface VersionProps { +export interface VersionProps extends EventInvokeConfigOptions { /** * SHA256 of the version of the Lambda source code * @@ -88,7 +89,7 @@ export class Version extends QualifiedFunctionBase implements IVersion { * @param versionArn The version ARN to create this version from */ public static fromVersionArn(scope: Construct, id: string, versionArn: string): IVersion { - const version = extractVersionFromArn(versionArn); + const version = extractQualifierFromArn(versionArn); const lambda = Function.fromFunctionArn(scope, `${id}Function`, versionArn); class Import extends QualifiedFunctionBase implements IVersion { @@ -99,6 +100,7 @@ export class Version extends QualifiedFunctionBase implements IVersion { public readonly grantPrincipal = lambda.grantPrincipal; public readonly role = lambda.role; + protected readonly qualifier = version; protected readonly canCreatePermissions = false; } return new Import(scope, id); @@ -113,6 +115,7 @@ export class Version extends QualifiedFunctionBase implements IVersion { public readonly grantPrincipal = attrs.lambda.grantPrincipal; public readonly role = attrs.lambda.role; + protected readonly qualifier = attrs.version; protected readonly canCreatePermissions = false; } return new Import(scope, id); @@ -123,6 +126,7 @@ export class Version extends QualifiedFunctionBase implements IVersion { public readonly functionArn: string; public readonly functionName: string; + protected readonly qualifier: string; protected readonly canCreatePermissions = true; constructor(scope: Construct, id: string, props: VersionProps) { @@ -140,6 +144,16 @@ export class Version extends QualifiedFunctionBase implements IVersion { this.version = version.attrVersion; this.functionArn = version.ref; this.functionName = `${this.lambda.functionName}:${this.version}`; + this.qualifier = version.attrVersion; + + if (props.onFailure || props.onSuccess || props.maxEventAge || props.retryAttempts !== undefined) { + this.configureAsyncInvoke({ + onFailure: props.onFailure, + onSuccess: props.onSuccess, + maxEventAge: props.maxEventAge, + retryAttempts: props.retryAttempts, + }); + } } public get grantPrincipal() { @@ -183,18 +197,18 @@ export class Version extends QualifiedFunctionBase implements IVersion { } /** - * Given an opaque (token) ARN, returns a CloudFormation expression that extracts the version - * name from the ARN. + * Given an opaque (token) ARN, returns a CloudFormation expression that extracts the + * qualifier (= version or alias) from the ARN. * * Version ARNs look like this: * - * arn:aws:lambda:region:account-id:function:function-name:version + * arn:aws:lambda:region:account-id:function:function-name:qualifier * - * ..which means that in order to extract the `version` component from the ARN, we can + * ..which means that in order to extract the `qualifier` component from the ARN, we can * split the ARN using ":" and select the component in index 7. * * @returns `FnSelect(7, FnSplit(':', arn))` */ -function extractVersionFromArn(arn: string) { +export function extractQualifierFromArn(arn: string) { return Fn.select(7, Fn.split(':', arn)); } diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json index df39d2ff80edb..8f7c8d6507a53 100644 --- a/packages/@aws-cdk/aws-lambda/package.json +++ b/packages/@aws-cdk/aws-lambda/package.json @@ -181,7 +181,8 @@ "props-default-doc:@aws-cdk/aws-lambda.Permission.sourceAccount", "props-default-doc:@aws-cdk/aws-lambda.Permission.sourceArn", "docs-public-apis:@aws-cdk/aws-lambda.ResourceBindOptions", - "docs-public-apis:@aws-cdk/aws-lambda.VersionAttributes" + "docs-public-apis:@aws-cdk/aws-lambda.VersionAttributes", + "props-physical-name:@aws-cdk/aws-lambda.EventInvokeConfigProps" ] }, "stability": "stable" diff --git a/packages/@aws-cdk/aws-lambda/test/test.alias.ts b/packages/@aws-cdk/aws-lambda/test/test.alias.ts index aa5c21083a8f5..b8088bf76f3a8 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.alias.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.alias.ts @@ -333,6 +333,101 @@ export = { ] }); + test.done(); + }, + + 'with event invoke config'(test: Test) { + // GIVEN + const stack = new Stack(); + const fn = new lambda.Function(stack, 'fn', { + code: new lambda.InlineCode('foo'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_10_X, + }); + const version = fn.addVersion('1'); + + // WHEN + new lambda.Alias(stack, 'Alias', { + aliasName: 'prod', + version, + onSuccess: { + bind: () => ({ + destination: 'on-success-arn' + }) + } + }); + + // THEN + expect(stack).to(haveResource('AWS::Lambda::EventInvokeConfig', { + FunctionName: { + Ref: 'fn5FF616E3' + }, + Qualifier: { + 'Fn::Select': [ + 7, + { + 'Fn::Split': [ + ':', + { + Ref: 'Alias325C5727' + } + ] + } + ] + }, + DestinationConfig: { + OnSuccess: { + Destination: 'on-success-arn' + } + } + })); + + test.done(); + }, + + 'throws when calling configureAsyncInvoke on already configured alias'(test: Test) { + // GIVEN + const stack = new Stack(); + const fn = new lambda.Function(stack, 'fn', { + code: new lambda.InlineCode('foo'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_10_X, + }); + const version = fn.addVersion('1'); + const alias = new lambda.Alias(stack, 'Alias', { + aliasName: 'prod', + version, + onSuccess: { + bind: () => ({ + destination: 'on-success-arn' + }) + } + }); + + // THEN + test.throws(() => alias.configureAsyncInvoke({ retryAttempts: 0 }), /An EventInvokeConfig has already been configured/); + + test.done(); + }, + + 'event invoke config on imported alias'(test: Test) { + // GIVEN + const stack = new Stack(); + const fn = lambda.Version.fromVersionArn(stack, 'Fn', 'arn:aws:lambda:region:account-id:function:function-name:version'); + const alias = lambda.Alias.fromAliasAttributes(stack, 'Alias', { aliasName: 'alias-name', aliasVersion: fn }); + + // WHEN + alias.configureAsyncInvoke({ + retryAttempts: 1 + }); + + // THEN + expect(stack).to(haveResource('AWS::Lambda::EventInvokeConfig', { + FunctionName: 'function-name', + Qualifier: 'alias-name', + MaximumRetryAttempts: 1 + })); + test.done(); } }; diff --git a/packages/@aws-cdk/aws-lambda/test/test.lambda-version.ts b/packages/@aws-cdk/aws-lambda/test/test.lambda-version.ts index b0007cb707e46..80c02e56c9966 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.lambda-version.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.lambda-version.ts @@ -1,4 +1,4 @@ -import { expect } from '@aws-cdk/assert'; +import { expect, haveResource } from '@aws-cdk/assert'; import * as cdk from '@aws-cdk/core'; import { Test } from 'nodeunit'; import * as lambda from '../lib'; @@ -30,4 +30,87 @@ export = { test.done(); }, + + 'create a version with event invoke config'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const fn = new lambda.Function(stack, 'Fn', { + runtime: lambda.Runtime.NODEJS_10_X, + handler: 'index.handler', + code: lambda.Code.fromInline('foo'), + }); + + // WHEN + new lambda.Version(stack, 'Version', { + lambda: fn, + maxEventAge: cdk.Duration.hours(1), + retryAttempts: 0 + }); + + // THEN + expect(stack).to(haveResource('AWS::Lambda::EventInvokeConfig', { + FunctionName: { + Ref: 'Fn9270CBC0' + }, + Qualifier: { + 'Fn::GetAtt': [ + 'Version6A868472', + 'Version' + ] + }, + MaximumEventAgeInSeconds: 3600, + MaximumRetryAttempts: 0 + })); + + test.done(); + }, + + 'throws when calling configureAsyncInvoke on already configured version'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const fn = new lambda.Function(stack, 'Fn', { + runtime: lambda.Runtime.NODEJS_10_X, + handler: 'index.handler', + code: lambda.Code.fromInline('foo'), + }); + const version = new lambda.Version(stack, 'Version', { + lambda: fn, + maxEventAge: cdk.Duration.hours(1), + retryAttempts: 0 + }); + + // THEN + test.throws(() => version.configureAsyncInvoke({ retryAttempts: 1 }), /An EventInvokeConfig has already been configured/); + + test.done(); + }, + + 'event invoke config on imported versions'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const version1 = lambda.Version.fromVersionArn(stack, 'Version1', 'arn:aws:lambda:region:account-id:function:function-name:version1'); + const version2 = lambda.Version.fromVersionArn(stack, 'Version2', 'arn:aws:lambda:region:account-id:function:function-name:version2'); + + // WHEN + version1.configureAsyncInvoke({ + retryAttempts: 1 + }); + version2.configureAsyncInvoke({ + retryAttempts: 0 + }); + + // THEN + expect(stack).to(haveResource('AWS::Lambda::EventInvokeConfig', { + FunctionName: 'function-name', + Qualifier: 'version1', + MaximumRetryAttempts: 1 + })); + expect(stack).to(haveResource('AWS::Lambda::EventInvokeConfig', { + FunctionName: 'function-name', + Qualifier: 'version2', + MaximumRetryAttempts: 0 + })); + + test.done(); + } }; diff --git a/packages/@aws-cdk/aws-lambda/test/test.lambda.ts b/packages/@aws-cdk/aws-lambda/test/test.lambda.ts index 34c19c1b239c1..89ed61bf93eb6 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.lambda.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.lambda.ts @@ -1463,6 +1463,115 @@ export = { GroupId: 'sg-123456789', })); + test.done(); + }, + + 'with event invoke config'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + new lambda.Function(stack, 'fn', { + code: new lambda.InlineCode('foo'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_10_X, + onFailure: { + bind: () => ({ destination: 'on-failure-arn' }), + }, + onSuccess: { + bind: () => ({ destination: 'on-success-arn' }), + }, + maxEventAge: cdk.Duration.hours(1), + retryAttempts: 0 + }); + + // THEN + expect(stack).to(haveResource('AWS::Lambda::EventInvokeConfig', { + FunctionName: { + Ref: 'fn5FF616E3' + }, + Qualifier: '$LATEST', + DestinationConfig: { + OnFailure: { + Destination: 'on-failure-arn' + }, + OnSuccess: { + Destination: 'on-success-arn' + }, + }, + MaximumEventAgeInSeconds: 3600, + MaximumRetryAttempts: 0 + })); + + test.done(); + }, + + 'throws when calling configureAsyncInvoke on already configured function'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const fn = new lambda.Function(stack, 'fn', { + code: new lambda.InlineCode('foo'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_10_X, + maxEventAge: cdk.Duration.hours(1), + }); + + // THEN + test.throws(() => fn.configureAsyncInvoke({ retryAttempts: 0 }), /An EventInvokeConfig has already been configured/); + + test.done(); + }, + + 'event invoke config on imported lambda'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const fn = lambda.Function.fromFunctionAttributes(stack, 'fn', { + functionArn: 'arn:aws:lambda:us-east-1:123456789012:function:my-function' + }); + + // WHEN + fn.configureAsyncInvoke({ + retryAttempts: 1 + }); + + // THEN + expect(stack).to(haveResource('AWS::Lambda::EventInvokeConfig', { + FunctionName: 'my-function', + Qualifier: '$LATEST', + MaximumRetryAttempts: 1 + })); + + test.done(); + }, + + 'add a version with event invoke config'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const fn = new lambda.Function(stack, 'fn', { + code: new lambda.InlineCode('foo'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_10_X, + }); + + // WHEN + fn.addVersion('1', 'sha256', 'desc', undefined, { + retryAttempts: 0 + }); + + // THEN + expect(stack).to(haveResource('AWS::Lambda::EventInvokeConfig', { + FunctionName: { + Ref: 'fn5FF616E3' + }, + Qualifier: { + 'Fn::GetAtt': [ + 'fnVersion197FA813F', + 'Version' + ] + }, + MaximumRetryAttempts: 0 + })); + test.done(); } }; diff --git a/packages/decdk/package.json b/packages/decdk/package.json index 40636ad49d4fb..52e603a9c891a 100644 --- a/packages/decdk/package.json +++ b/packages/decdk/package.json @@ -105,6 +105,7 @@ "@aws-cdk/aws-kms": "1.19.0", "@aws-cdk/aws-lakeformation": "1.19.0", "@aws-cdk/aws-lambda": "1.19.0", + "@aws-cdk/aws-lambda-destinations": "1.19.0", "@aws-cdk/aws-lambda-event-sources": "1.19.0", "@aws-cdk/aws-logs": "1.19.0", "@aws-cdk/aws-logs-destinations": "1.19.0", From 93c2ddb49d190d77476bd3a3e4689b4d9192eaca Mon Sep 17 00:00:00 2001 From: Jonathan Goldwasser Date: Thu, 19 Dec 2019 21:27:25 +0100 Subject: [PATCH 05/80] docs(core): add missing cfnOptions in README (#5490) `condition` is available under `cfnOptions` in `CfnResource`. --- packages/@aws-cdk/core/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/core/README.md b/packages/@aws-cdk/core/README.md index 17ed0de19d151..479bf203ff9a9 100644 --- a/packages/@aws-cdk/core/README.md +++ b/packages/@aws-cdk/core/README.md @@ -235,7 +235,7 @@ stack.partition; CloudFormation resources can also specify [resource attributes][cfn-resource-attributes]. The `CfnResource` class allows -accessing those though the `cfnOptions` property: +accessing those through the `cfnOptions` property: ```ts const rawBucket = new s3.CfnBucket(this, 'Bucket', { /* ... */ }); @@ -243,7 +243,7 @@ const rawBucket = new s3.CfnBucket(this, 'Bucket', { /* ... */ }); const rawBucket = bucket.node.defaultChild as s3.CfnBucket; // then -rawBucket.condition = new CfnCondition(this, 'EnableBucket', { /* ... */ }); +rawBucket.cfnOptions.condition = new CfnCondition(this, 'EnableBucket', { /* ... */ }); rawBucket.cfnOptions.metadata = { metadataKey: 'MetadataValue', }; From ea095f0198ebe1efd2f20621cac111b71ee68bd7 Mon Sep 17 00:00:00 2001 From: Wenqian Wang <51062650+wqzoww@users.noreply.github.com> Date: Fri, 20 Dec 2019 01:57:39 -0800 Subject: [PATCH 06/80] feat(aws-stepfunctions): support StateMachineType (#5398) Closes #5397 --- .../aws-stepfunctions/lib/state-machine.ts | 35 ++++++++++ .../test/test.state-machine.ts | 67 +++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 packages/@aws-cdk/aws-stepfunctions/test/test.state-machine.ts diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts b/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts index 0fd3204a101a6..980fd1266d22f 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts @@ -5,6 +5,25 @@ import { StateGraph } from './state-graph'; import { CfnStateMachine } from './stepfunctions.generated'; import { IChainable } from './types'; +/** + * Two types of state machines are available in AWS Step Functions: EXPRESS AND STANDARD. + * + * @see https://docs.aws.amazon.com/step-functions/latest/dg/concepts-standard-vs-express.html + * + * @default STANDARD + */ +export enum StateMachineType { + /** + * Express Workflows are ideal for high-volume, event processing workloads. + */ + EXPRESS = 'EXPRESS', + + /** + * Standard Workflows are ideal for long-running, durable, and auditable workflows. + */ + STANDARD = 'STANDARD' +} + /** * Properties for defining a State Machine */ @@ -34,6 +53,13 @@ export interface StateMachineProps { * @default No timeout */ readonly timeout?: Duration; + + /** + * Type of the state machine + * + * @default StateMachineType.STANDARD + */ + readonly stateMachineType?: StateMachineType; } /** @@ -86,6 +112,12 @@ export class StateMachine extends StateMachineBase { */ public readonly stateMachineArn: string; + /** + * Type of the state machine + * @attribute + */ + public readonly stateMachineType: StateMachineType; + constructor(scope: Construct, id: string, props: StateMachineProps) { super(scope, id, { physicalName: props.stateMachineName, @@ -98,8 +130,11 @@ export class StateMachine extends StateMachineBase { const graph = new StateGraph(props.definition.startState, `State Machine ${id} definition`); graph.timeout = props.timeout; + this.stateMachineType = props.stateMachineType ? props.stateMachineType : StateMachineType.STANDARD; + const resource = new CfnStateMachine(this, 'Resource', { stateMachineName: this.physicalName, + stateMachineType: props.stateMachineType ? props.stateMachineType : undefined, roleArn: this.role.roleArn, definitionString: Stack.of(this).toJsonString(graph.toGraphJson()), }); diff --git a/packages/@aws-cdk/aws-stepfunctions/test/test.state-machine.ts b/packages/@aws-cdk/aws-stepfunctions/test/test.state-machine.ts new file mode 100644 index 0000000000000..2aab5ea1070d8 --- /dev/null +++ b/packages/@aws-cdk/aws-stepfunctions/test/test.state-machine.ts @@ -0,0 +1,67 @@ +import { expect, haveResource } from '@aws-cdk/assert'; +import * as cdk from '@aws-cdk/core'; +import { Test } from 'nodeunit'; +import * as stepfunctions from '../lib'; + +export = { + 'Instantiate Default State Machine'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + new stepfunctions.StateMachine(stack, 'MyStateMachine', { + stateMachineName: "MyStateMachine", + definition: stepfunctions.Chain.start(new stepfunctions.Pass(stack, 'Pass')) + }); + + // THEN + expect(stack).to(haveResource('AWS::StepFunctions::StateMachine', { + StateMachineName: "MyStateMachine", + DefinitionString: "{\"StartAt\":\"Pass\",\"States\":{\"Pass\":{\"Type\":\"Pass\",\"End\":true}}}" + })); + + test.done(); + }, + + 'Instantiate Standard State Machine'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + new stepfunctions.StateMachine(stack, 'MyStateMachine', { + stateMachineName: "MyStateMachine", + definition: stepfunctions.Chain.start(new stepfunctions.Pass(stack, 'Pass')), + stateMachineType: stepfunctions.StateMachineType.STANDARD + }); + + // THEN + expect(stack).to(haveResource('AWS::StepFunctions::StateMachine', { + StateMachineName: "MyStateMachine", + StateMachineType: "STANDARD", + DefinitionString: "{\"StartAt\":\"Pass\",\"States\":{\"Pass\":{\"Type\":\"Pass\",\"End\":true}}}" + })); + + test.done(); + }, + + 'Instantiate Express State Machine'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + new stepfunctions.StateMachine(stack, 'MyStateMachine', { + stateMachineName: "MyStateMachine", + definition: stepfunctions.Chain.start(new stepfunctions.Pass(stack, 'Pass')), + stateMachineType: stepfunctions.StateMachineType.EXPRESS + }); + + // THEN + expect(stack).to(haveResource('AWS::StepFunctions::StateMachine', { + StateMachineName: "MyStateMachine", + StateMachineType: "EXPRESS", + DefinitionString: "{\"StartAt\":\"Pass\",\"States\":{\"Pass\":{\"Type\":\"Pass\",\"End\":true}}}" + })); + + test.done(); + } +}; From ac748c1786e68774f5d0ea9cfbec439034166c40 Mon Sep 17 00:00:00 2001 From: Ed Epstein Date: Fri, 20 Dec 2019 01:58:43 -0800 Subject: [PATCH 07/80] feat(cli): support custom CA certificate bundles Some large orgs enforce HTTPS proxies to communicate with services, which means they often have internal certificate authorities that generate leaf certificates on the fly. This commit adds basic support for specifying a root CA certificate for trust. Fixes #5294 --- packages/aws-cdk/bin/cdk.ts | 2 + packages/aws-cdk/lib/api/util/sdk.ts | 62 ++++++++++++++++++++-------- packages/aws-cdk/package.json | 1 - 3 files changed, 47 insertions(+), 18 deletions(-) diff --git a/packages/aws-cdk/bin/cdk.ts b/packages/aws-cdk/bin/cdk.ts index 8c3d9f3309d11..6e4eacb20293e 100644 --- a/packages/aws-cdk/bin/cdk.ts +++ b/packages/aws-cdk/bin/cdk.ts @@ -36,6 +36,7 @@ async function parseCommandLineArguments() { .option('verbose', { type: 'boolean', alias: 'v', desc: 'Show debug logs', default: false }) .option('profile', { type: 'string', desc: 'Use the indicated AWS profile as the default environment', requiresArg: true }) .option('proxy', { type: 'string', desc: 'Use the indicated proxy. Will read from HTTPS_PROXY environment variable if not specified.', requiresArg: true }) + .option('ca-bundle-path', { type: 'string', desc: 'Path to CA certificate to use when validating HTTPS requests. Will read from AWS_CA_BUNDLE environment variable if not specified.', requiresArg: true }) .option('ec2creds', { type: 'boolean', alias: 'i', default: undefined, desc: 'Force trying to fetch EC2 instance credentials. Default: guess EC2 instance status.' }) .option('version-reporting', { type: 'boolean', desc: 'Include the "AWS::CDK::Metadata" resource in synthesized templates (enabled by default)', default: undefined }) .option('path-metadata', { type: 'boolean', desc: 'Include "aws:cdk:path" CloudFormation metadata for each resource (enabled by default)', default: true }) @@ -107,6 +108,7 @@ async function initCommandLine() { const aws = new SDK({ profile: argv.profile, proxyAddress: argv.proxy, + caBundlePath: argv['ca-bundle-path'], ec2creds: argv.ec2creds, }); diff --git a/packages/aws-cdk/lib/api/util/sdk.ts b/packages/aws-cdk/lib/api/util/sdk.ts index 0546987b7dcd4..8ecb566450c28 100644 --- a/packages/aws-cdk/lib/api/util/sdk.ts +++ b/packages/aws-cdk/lib/api/util/sdk.ts @@ -2,6 +2,7 @@ import * as cxapi from '@aws-cdk/cx-api'; import * as AWS from 'aws-sdk'; import * as child_process from 'child_process'; import * as fs from 'fs-extra'; +import * as https from 'https'; import * as os from 'os'; import * as path from 'path'; import * as util from 'util'; @@ -53,6 +54,13 @@ export interface SDKOptions { * @default Automatically determine. */ ec2creds?: boolean; + + /** + * A path to a certificate bundle that contains a cert to be trusted. + * + * @default No certificate bundle + */ + caBundlePath?: string; } /** @@ -88,23 +96,7 @@ export class SDK implements ISDK { const defaultCredentialProvider = makeCLICompatibleCredentialProvider(options.profile, options.ec2creds); - // Find the package.json from the main toolkit - const pkg = (require.main as any).require('../package.json'); - AWS.config.update({ - customUserAgent: `${pkg.name}/${pkg.version}` - }); - - // https://aws.amazon.com/blogs/developer/using-the-aws-sdk-for-javascript-from-behind-a-proxy/ - if (options.proxyAddress === undefined) { - options.proxyAddress = httpsProxyFromEnvironment(); - } - if (options.proxyAddress) { // Ignore empty string on purpose - debug('Using proxy server: %s', options.proxyAddress); - AWS.config.update({ - // eslint-disable-next-line @typescript-eslint/no-require-imports - httpOptions: { agent: require('proxy-agent')(options.proxyAddress) } - }); - } + this.configureSDKHttpOptions(options); this.defaultAwsAccount = new DefaultAWSAccount(defaultCredentialProvider, getCLICompatibleDefaultRegionGetter(this.profile)); this.credentialsCache = new CredentialsCache(this.defaultAwsAccount, defaultCredentialProvider); @@ -196,6 +188,29 @@ export class SDK implements ISDK { return environment; } + private async configureSDKHttpOptions(options: SDKOptions) { + const config: {[k: string]: any} = {}; + const httpOptions: {[k: string]: any} = {}; + // Find the package.json from the main toolkit + const pkg = (require.main as any).require('../package.json'); + config.customUserAgent = `${pkg.name}/${pkg.version}`; + + // https://aws.amazon.com/blogs/developer/using-the-aws-sdk-for-javascript-from-behind-a-proxy/ + options.proxyAddress = options.proxyAddress || httpsProxyFromEnvironment(); + options.caBundlePath = options.caBundlePath || caBundlePathFromEnvironment(); + + if (options.proxyAddress) { // Ignore empty string on purpose + debug('Using proxy server: %s', options.proxyAddress); + httpOptions.proxy = options.proxyAddress; + } + if (options.caBundlePath) { + debug('Using ca bundle path: %s', options.caBundlePath); + httpOptions.agent = new https.Agent({ca: await readIfPossible(options.caBundlePath)}); + } + config.httpOptions = httpOptions; + + AWS.config.update(config); + } } /** @@ -438,6 +453,19 @@ function httpsProxyFromEnvironment(): string | undefined { return undefined; } +/** + * Find and return a CA certificate bundle path to be passed into the SDK. + */ +function caBundlePathFromEnvironment(): string | undefined { + if (process.env.aws_ca_bundle) { + return process.env.aws_ca_bundle; + } + if (process.env.AWS_CA_BUNDLE) { + return process.env.AWS_CA_BUNDLE; + } + return undefined; +} + /** * Return whether it looks like we'll have ECS credentials available */ diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index f1c456eadae1b..de81ee942db80 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -82,7 +82,6 @@ "json-diff": "^0.5.4", "minimatch": ">=3.0", "promptly": "^3.0.3", - "proxy-agent": "^3.1.1", "request": "^2.88.0", "semver": "^7.1.1", "source-map-support": "^0.5.16", From b700b778e74822e520867dbc5ff5524fb0c82eaf Mon Sep 17 00:00:00 2001 From: Jonathan Goldwasser Date: Fri, 20 Dec 2019 11:02:36 +0100 Subject: [PATCH 08/80] feat(rds): more extensive secret rotation support (#5281) Add support for Redshift clusters, DocumentDB databases and the multi user rotation scheme. Move `SecretRotation` from `aws-rds` to `aws-secretsmanager`. Add resource policy for secrets and use it to prevent deletion of secrets for which rotation is enabled. Update instance class to `t3` in `aws-rds` integration tests (`t2` is being deprecated and Oracle `t2` instances cannot be created anymore). Closes #5194 BREAKING CHANGE: `addRotationSingleUser(id: string, options: SecretRotationOptions)` is now `addRotationSingleUser(automaticallyAfter?: Duration)` --- packages/@aws-cdk/aws-rds/README.md | 40 +- packages/@aws-cdk/aws-rds/lib/cluster.ts | 56 ++- packages/@aws-cdk/aws-rds/lib/index.ts | 1 - packages/@aws-cdk/aws-rds/lib/instance.ts | 81 ++-- packages/@aws-cdk/aws-rds/lib/props.ts | 54 ++- .../@aws-cdk/aws-rds/lib/secret-rotation.ts | 146 ------- packages/@aws-cdk/aws-rds/package.json | 2 - .../integ.cluster-rotation.lit.expected.json | 120 +++--- .../test/integ.cluster-rotation.lit.ts | 4 +- .../aws-rds/test/integ.cluster.expected.json | 4 +- .../@aws-cdk/aws-rds/test/integ.cluster.ts | 2 +- .../test/integ.instance.lit.expected.json | 120 +++--- .../aws-rds/test/integ.instance.lit.ts | 4 +- .../@aws-cdk/aws-rds/test/test.cluster.ts | 48 ++- .../@aws-cdk/aws-rds/test/test.instance.ts | 40 +- .../aws-rds/test/test.secret-rotation.ts | 365 ------------------ .../@aws-cdk/aws-secretsmanager/README.md | 42 +- .../@aws-cdk/aws-secretsmanager/lib/index.ts | 2 + .../@aws-cdk/aws-secretsmanager/lib/policy.ts | 33 ++ .../aws-secretsmanager/lib/secret-rotation.ts | 261 +++++++++++++ .../@aws-cdk/aws-secretsmanager/lib/secret.ts | 102 ++++- .../@aws-cdk/aws-secretsmanager/package.json | 5 +- .../test/test.secret-rotation.ts | 294 ++++++++++++++ .../aws-secretsmanager/test/test.secret.ts | 83 +++- 24 files changed, 1174 insertions(+), 735 deletions(-) delete mode 100644 packages/@aws-cdk/aws-rds/lib/secret-rotation.ts delete mode 100644 packages/@aws-cdk/aws-rds/test/test.secret-rotation.ts create mode 100644 packages/@aws-cdk/aws-secretsmanager/lib/policy.ts create mode 100644 packages/@aws-cdk/aws-secretsmanager/lib/secret-rotation.ts create mode 100644 packages/@aws-cdk/aws-secretsmanager/test/test.secret-rotation.ts diff --git a/packages/@aws-cdk/aws-rds/README.md b/packages/@aws-cdk/aws-rds/README.md index 89fc1011c464b..6c9f86df82f7f 100644 --- a/packages/@aws-cdk/aws-rds/README.md +++ b/packages/@aws-cdk/aws-rds/README.md @@ -108,32 +108,36 @@ For an instance database: const address = instance.instanceEndpoint.socketAddress; // "HOSTNAME:PORT" ``` -### Rotating master password +### Rotating credentials When the master password is generated and stored in AWS Secrets Manager, it can be rotated automatically: +```ts +instance.addRotationSingleUser(); // Will rotate automatically after 30 days +``` [example of setting up master password rotation for a cluster](test/integ.cluster-rotation.lit.ts) -Rotation of the master password is also supported for an existing cluster: +The multi user rotation scheme is also available: ```ts -new SecretRotation(stack, 'Rotation', { - secret: importedSecret, - application: SecretRotationApplication.ORACLE_ROTATION_SINGLE_USER - target: importedCluster, // or importedInstance - vpc: importedVpc, -}) +instance.addRotationMultiUser('MyUser', { + secret: myImportedSecret +}); ``` -The `importedSecret` must be a JSON string with the following format: -```json -{ - "engine": "", - "host": "", - "username": "", - "password": "", - "dbname": "", - "port": "" -} +It's also possible to create user credentials together with the instance/cluster and add rotation: +```ts +const myUserSecret = new rds.DatabaseSecret(this, 'MyUserSecret', { + username: 'myuser' +}); +const myUserSecretAttached = myUserSecret.attach(instance); // Adds DB connections information in the secret + +instance.addRotationMultiUser('MyUser', { // Add rotation using the multi user scheme + secret: myUserSecretAttached +}); ``` +**Note**: This user must be created manually in the database using the master credentials. +The rotation will start as soon as this user exists. + +See also [@aws-cdk/aws-secretsmanager](https://github.com/aws/aws-cdk/blob/master/packages/%40aws-cdk/aws-secretsmanager/README.md) for credentials rotation of existing clusters/instances. ### Metrics Database instances expose metrics (`cloudwatch.Metric`): diff --git a/packages/@aws-cdk/aws-rds/lib/cluster.ts b/packages/@aws-cdk/aws-rds/lib/cluster.ts index c3add38545f8d..79e1264a682cc 100644 --- a/packages/@aws-cdk/aws-rds/lib/cluster.ts +++ b/packages/@aws-cdk/aws-rds/lib/cluster.ts @@ -7,9 +7,8 @@ import { DatabaseClusterAttributes, IDatabaseCluster } from './cluster-ref'; import { DatabaseSecret } from './database-secret'; import { Endpoint } from './endpoint'; import { IParameterGroup } from './parameter-group'; -import { BackupProps, DatabaseClusterEngine, InstanceProps, Login } from './props'; +import { BackupProps, DatabaseClusterEngine, InstanceProps, Login, RotationMultiUserOptions } from './props'; import { CfnDBCluster, CfnDBInstance, CfnDBSubnetGroup } from './rds.generated'; -import { SecretRotation, SecretRotationApplication, SecretRotationOptions } from './secret-rotation'; /** * Properties for a new database cluster @@ -190,7 +189,7 @@ abstract class DatabaseClusterBase extends Resource implements IDatabaseCluster public asSecretAttachmentTarget(): secretsmanager.SecretAttachmentTargetProps { return { targetId: this.clusterIdentifier, - targetType: secretsmanager.AttachmentTargetType.CLUSTER + targetType: secretsmanager.AttachmentTargetType.RDS_DB_CLUSTER }; } } @@ -262,10 +261,8 @@ export class DatabaseCluster extends DatabaseClusterBase { */ public readonly secret?: secretsmanager.ISecret; - /** - * The database engine of this cluster - */ - private readonly secretRotationApplication: SecretRotationApplication; + private readonly singleUserRotationApplication: secretsmanager.SecretRotationApplication; + private readonly multiUserRotationApplication: secretsmanager.SecretRotationApplication; /** * The VPC where the DB subnet group is created. @@ -302,7 +299,7 @@ export class DatabaseCluster extends DatabaseClusterBase { }); this.securityGroupId = securityGroup.securityGroupId; - let secret; + let secret: DatabaseSecret | undefined; if (!props.masterUser.password) { secret = new DatabaseSecret(this, 'Secret', { username: props.masterUser.username, @@ -310,7 +307,8 @@ export class DatabaseCluster extends DatabaseClusterBase { }); } - this.secretRotationApplication = props.engine.secretRotationApplication; + this.singleUserRotationApplication = props.engine.singleUserRotationApplication; + this.multiUserRotationApplication = props.engine.multiUserRotationApplication; const cluster = new CfnDBCluster(this, 'Resource', { // Basic @@ -349,9 +347,7 @@ export class DatabaseCluster extends DatabaseClusterBase { this.clusterReadEndpoint = new Endpoint(cluster.attrReadEndpointAddress, portAttribute); if (secret) { - this.secret = secret.addTargetAttachment('AttachedSecret', { - target: this - }); + this.secret = secret.attach(this); } const instanceCount = props.instances != null ? props.instances : 2; @@ -415,18 +411,46 @@ export class DatabaseCluster extends DatabaseClusterBase { /** * Adds the single user rotation of the master password to this cluster. + * + * @param [automaticallyAfter=Duration.days(30)] Specifies the number of days after the previous rotation + * before Secrets Manager triggers the next automatic rotation. */ - public addRotationSingleUser(id: string, options: SecretRotationOptions = {}): SecretRotation { + public addRotationSingleUser(automaticallyAfter?: Duration): secretsmanager.SecretRotation { if (!this.secret) { throw new Error('Cannot add single user rotation for a cluster without secret.'); } - return new SecretRotation(this, id, { + + const id = 'RotationSingleUser'; + const existing = this.node.tryFindChild(id); + if (existing) { + throw new Error('A single user rotation was already added to this cluster.'); + } + + return new secretsmanager.SecretRotation(this, id, { secret: this.secret, - application: this.secretRotationApplication, + automaticallyAfter, + application: this.singleUserRotationApplication, + vpc: this.vpc, + vpcSubnets: this.vpcSubnets, + target: this, + }); + } + + /** + * Adds the multi user rotation to this cluster. + */ + public addRotationMultiUser(id: string, options: RotationMultiUserOptions): secretsmanager.SecretRotation { + if (!this.secret) { + throw new Error('Cannot add multi user rotation for a cluster without secret.'); + } + return new secretsmanager.SecretRotation(this, id, { + secret: options.secret, + masterSecret: this.secret, + automaticallyAfter: options.automaticallyAfter, + application: this.multiUserRotationApplication, vpc: this.vpc, vpcSubnets: this.vpcSubnets, target: this, - ...options }); } } diff --git a/packages/@aws-cdk/aws-rds/lib/index.ts b/packages/@aws-cdk/aws-rds/lib/index.ts index 116e542651658..83e4a99bc4a07 100644 --- a/packages/@aws-cdk/aws-rds/lib/index.ts +++ b/packages/@aws-cdk/aws-rds/lib/index.ts @@ -2,7 +2,6 @@ export * from './cluster'; export * from './cluster-ref'; export * from './props'; export * from './parameter-group'; -export * from './secret-rotation'; export * from './database-secret'; export * from './endpoint'; export * from './option-group'; diff --git a/packages/@aws-cdk/aws-rds/lib/instance.ts b/packages/@aws-cdk/aws-rds/lib/instance.ts index d753387002780..6e6396aa69e74 100644 --- a/packages/@aws-cdk/aws-rds/lib/instance.ts +++ b/packages/@aws-cdk/aws-rds/lib/instance.ts @@ -10,9 +10,8 @@ import { DatabaseSecret } from './database-secret'; import { Endpoint } from './endpoint'; import { IOptionGroup } from './option-group'; import { IParameterGroup } from './parameter-group'; -import { DatabaseClusterEngine } from './props'; +import { DatabaseClusterEngine, RotationMultiUserOptions } from './props'; import { CfnDBInstance, CfnDBInstanceProps, CfnDBSubnetGroup } from './rds.generated'; -import { SecretRotation, SecretRotationApplication, SecretRotationOptions } from './secret-rotation'; /** * A database instance @@ -144,7 +143,7 @@ export abstract class DatabaseInstanceBase extends Resource implements IDatabase public asSecretAttachmentTarget(): secretsmanager.SecretAttachmentTargetProps { return { targetId: this.instanceIdentifier, - targetType: secretsmanager.AttachmentTargetType.INSTANCE + targetType: secretsmanager.AttachmentTargetType.RDS_DB_INSTANCE }; } } @@ -154,17 +153,19 @@ export abstract class DatabaseInstanceBase extends Resource implements IDatabase * secret rotation. */ export class DatabaseInstanceEngine extends DatabaseClusterEngine { - public static readonly MARIADB = new DatabaseInstanceEngine('mariadb', SecretRotationApplication.MARIADB_ROTATION_SINGLE_USER); - public static readonly MYSQL = new DatabaseInstanceEngine('mysql', SecretRotationApplication.MYSQL_ROTATION_SINGLE_USER); - public static readonly ORACLE_EE = new DatabaseInstanceEngine('oracle-ee', SecretRotationApplication.ORACLE_ROTATION_SINGLE_USER); - public static readonly ORACLE_SE2 = new DatabaseInstanceEngine('oracle-se2', SecretRotationApplication.ORACLE_ROTATION_SINGLE_USER); - public static readonly ORACLE_SE1 = new DatabaseInstanceEngine('oracle-se1', SecretRotationApplication.ORACLE_ROTATION_SINGLE_USER); - public static readonly ORACLE_SE = new DatabaseInstanceEngine('oracle-se', SecretRotationApplication.ORACLE_ROTATION_SINGLE_USER); - public static readonly POSTGRES = new DatabaseInstanceEngine('postgres', SecretRotationApplication.POSTGRES_ROTATION_SINGLE_USER); - public static readonly SQL_SERVER_EE = new DatabaseInstanceEngine('sqlserver-ee', SecretRotationApplication.SQLSERVER_ROTATION_SINGLE_USER); - public static readonly SQL_SERVER_SE = new DatabaseInstanceEngine('sqlserver-se', SecretRotationApplication.SQLSERVER_ROTATION_SINGLE_USER); - public static readonly SQL_SERVER_EX = new DatabaseInstanceEngine('sqlserver-ex', SecretRotationApplication.SQLSERVER_ROTATION_SINGLE_USER); - public static readonly SQL_SERVER_WEB = new DatabaseInstanceEngine('sqlserver-web', SecretRotationApplication.SQLSERVER_ROTATION_SINGLE_USER); + /* tslint:disable max-line-length */ + public static readonly MARIADB = new DatabaseInstanceEngine('mariadb', secretsmanager.SecretRotationApplication.MARIADB_ROTATION_SINGLE_USER, secretsmanager.SecretRotationApplication.MARIADB_ROTATION_MULTI_USER); + public static readonly MYSQL = new DatabaseInstanceEngine('mysql', secretsmanager.SecretRotationApplication.MYSQL_ROTATION_SINGLE_USER, secretsmanager.SecretRotationApplication.MYSQL_ROTATION_MULTI_USER); + public static readonly ORACLE_EE = new DatabaseInstanceEngine('oracle-ee', secretsmanager.SecretRotationApplication.ORACLE_ROTATION_SINGLE_USER, secretsmanager.SecretRotationApplication.ORACLE_ROTATION_MULTI_USER); + public static readonly ORACLE_SE2 = new DatabaseInstanceEngine('oracle-se2', secretsmanager.SecretRotationApplication.ORACLE_ROTATION_SINGLE_USER, secretsmanager.SecretRotationApplication.ORACLE_ROTATION_MULTI_USER); + public static readonly ORACLE_SE1 = new DatabaseInstanceEngine('oracle-se1', secretsmanager.SecretRotationApplication.ORACLE_ROTATION_SINGLE_USER, secretsmanager.SecretRotationApplication.ORACLE_ROTATION_MULTI_USER); + public static readonly ORACLE_SE = new DatabaseInstanceEngine('oracle-se', secretsmanager.SecretRotationApplication.ORACLE_ROTATION_SINGLE_USER, secretsmanager.SecretRotationApplication.ORACLE_ROTATION_MULTI_USER); + public static readonly POSTGRES = new DatabaseInstanceEngine('postgres', secretsmanager.SecretRotationApplication.POSTGRES_ROTATION_SINGLE_USER, secretsmanager.SecretRotationApplication.POSTGRES_ROTATION_MULTI_USER); + public static readonly SQL_SERVER_EE = new DatabaseInstanceEngine('sqlserver-ee', secretsmanager.SecretRotationApplication.SQLSERVER_ROTATION_SINGLE_USER, secretsmanager.SecretRotationApplication.SQLSERVER_ROTATION_MULTI_USER); + public static readonly SQL_SERVER_SE = new DatabaseInstanceEngine('sqlserver-se', secretsmanager.SecretRotationApplication.SQLSERVER_ROTATION_SINGLE_USER, secretsmanager.SecretRotationApplication.SQLSERVER_ROTATION_MULTI_USER); + public static readonly SQL_SERVER_EX = new DatabaseInstanceEngine('sqlserver-ex', secretsmanager.SecretRotationApplication.SQLSERVER_ROTATION_SINGLE_USER, secretsmanager.SecretRotationApplication.SQLSERVER_ROTATION_MULTI_USER); + public static readonly SQL_SERVER_WEB = new DatabaseInstanceEngine('sqlserver-web', secretsmanager.SecretRotationApplication.SQLSERVER_ROTATION_SINGLE_USER, secretsmanager.SecretRotationApplication.SQLSERVER_ROTATION_MULTI_USER); + /* tslint:enable max-line-length */ } /** @@ -665,12 +666,14 @@ abstract class DatabaseInstanceSource extends DatabaseInstanceNew implements IDa protected readonly sourceCfnProps: CfnDBInstanceProps; - private readonly secretRotationApplication: SecretRotationApplication; + private readonly singleUserRotationApplication: secretsmanager.SecretRotationApplication; + private readonly multiUserRotationApplication: secretsmanager.SecretRotationApplication; constructor(scope: Construct, id: string, props: DatabaseInstanceSourceProps) { super(scope, id, props); - this.secretRotationApplication = props.engine.secretRotationApplication; + this.singleUserRotationApplication = props.engine.singleUserRotationApplication; + this.multiUserRotationApplication = props.engine.multiUserRotationApplication; this.sourceCfnProps = { ...this.newCfnProps, @@ -687,18 +690,46 @@ abstract class DatabaseInstanceSource extends DatabaseInstanceNew implements IDa /** * Adds the single user rotation of the master password to this instance. + * + * @param [automaticallyAfter=Duration.days(30)] Specifies the number of days after the previous rotation + * before Secrets Manager triggers the next automatic rotation. */ - public addRotationSingleUser(id: string, options: SecretRotationOptions = {}): SecretRotation { + public addRotationSingleUser(automaticallyAfter?: Duration): secretsmanager.SecretRotation { if (!this.secret) { throw new Error('Cannot add single user rotation for an instance without secret.'); } - return new SecretRotation(this, id, { + + const id = 'RotationSingleUser'; + const existing = this.node.tryFindChild(id); + if (existing) { + throw new Error('A single user rotation was already added to this instance.'); + } + + return new secretsmanager.SecretRotation(this, id, { secret: this.secret, - application: this.secretRotationApplication, + automaticallyAfter, + application: this.singleUserRotationApplication, + vpc: this.vpc, + vpcSubnets: this.vpcPlacement, + target: this, + }); + } + + /** + * Adds the multi user rotation to this instance. + */ + public addRotationMultiUser(id: string, options: RotationMultiUserOptions): secretsmanager.SecretRotation { + if (!this.secret) { + throw new Error('Cannot add multi user rotation for an instance without secret.'); + } + return new secretsmanager.SecretRotation(this, id, { + secret: options.secret, + masterSecret: this.secret, + automaticallyAfter: options.automaticallyAfter, + application: this.multiUserRotationApplication, vpc: this.vpc, vpcSubnets: this.vpcPlacement, target: this, - ...options }); } } @@ -750,7 +781,7 @@ export class DatabaseInstance extends DatabaseInstanceSource implements IDatabas constructor(scope: Construct, id: string, props: DatabaseInstanceProps) { super(scope, id, props); - let secret; + let secret: DatabaseSecret | undefined; if (!props.masterUserPassword) { secret = new DatabaseSecret(this, 'Secret', { username: props.masterUsername, @@ -782,9 +813,7 @@ export class DatabaseInstance extends DatabaseInstanceSource implements IDatabas }); if (secret) { - this.secret = secret.addTargetAttachment('AttachedSecret', { - target: this - }); + this.secret = secret.attach(this); } this.setLogRetention(); @@ -882,9 +911,7 @@ export class DatabaseInstanceFromSnapshot extends DatabaseInstanceSource impleme }); if (secret) { - this.secret = secret.addTargetAttachment('AttachedSecret', { - target: this - }); + this.secret = secret.attach(this); } this.setLogRetention(); diff --git a/packages/@aws-cdk/aws-rds/lib/props.ts b/packages/@aws-cdk/aws-rds/lib/props.ts index add2912ba6cb3..c0971bdef82ca 100644 --- a/packages/@aws-cdk/aws-rds/lib/props.ts +++ b/packages/@aws-cdk/aws-rds/lib/props.ts @@ -1,17 +1,19 @@ import * as ec2 from '@aws-cdk/aws-ec2'; import * as kms from '@aws-cdk/aws-kms'; +import * as secretsmanager from '@aws-cdk/aws-secretsmanager'; import { Duration, SecretValue } from '@aws-cdk/core'; import { IParameterGroup } from './parameter-group'; -import { SecretRotationApplication } from './secret-rotation'; /** * A database cluster engine. Provides mapping to the serverless application * used for secret rotation. */ export class DatabaseClusterEngine { - public static readonly AURORA = new DatabaseClusterEngine('aurora', SecretRotationApplication.MYSQL_ROTATION_SINGLE_USER); - public static readonly AURORA_MYSQL = new DatabaseClusterEngine('aurora-mysql', SecretRotationApplication.MYSQL_ROTATION_SINGLE_USER); - public static readonly AURORA_POSTGRESQL = new DatabaseClusterEngine('aurora-postgresql', SecretRotationApplication.POSTGRES_ROTATION_SINGLE_USER); + /* tslint:disable max-line-length */ + public static readonly AURORA = new DatabaseClusterEngine('aurora', secretsmanager.SecretRotationApplication.MYSQL_ROTATION_SINGLE_USER, secretsmanager.SecretRotationApplication.MYSQL_ROTATION_MULTI_USER); + public static readonly AURORA_MYSQL = new DatabaseClusterEngine('aurora-mysql', secretsmanager.SecretRotationApplication.MYSQL_ROTATION_SINGLE_USER, secretsmanager.SecretRotationApplication.MYSQL_ROTATION_MULTI_USER); + public static readonly AURORA_POSTGRESQL = new DatabaseClusterEngine('aurora-postgresql', secretsmanager.SecretRotationApplication.POSTGRES_ROTATION_SINGLE_USER, secretsmanager.SecretRotationApplication.POSTGRES_ROTATION_MULTI_USER); + /* tslint:enable max-line-length */ /** * The engine. @@ -19,13 +21,20 @@ export class DatabaseClusterEngine { public readonly name: string; /** - * The secret rotation application. + * The single user secret rotation application. */ - public readonly secretRotationApplication: SecretRotationApplication; + public readonly singleUserRotationApplication: secretsmanager.SecretRotationApplication; - constructor(name: string, secretRotationApplication: SecretRotationApplication) { + /** + * The multi user secret rotation application. + */ + public readonly multiUserRotationApplication: secretsmanager.SecretRotationApplication; + + // tslint:disable-next-line max-line-length + constructor(name: string, singleUserRotationApplication: secretsmanager.SecretRotationApplication, multiUserRotationApplication: secretsmanager.SecretRotationApplication) { this.name = name; - this.secretRotationApplication = secretRotationApplication; + this.singleUserRotationApplication = singleUserRotationApplication; + this.multiUserRotationApplication = multiUserRotationApplication; } } @@ -121,3 +130,32 @@ export interface Login { */ readonly kmsKey?: kms.IKey; } + +/** + * Options to add the multi user rotation + */ +export interface RotationMultiUserOptions { + /** + * The secret to rotate. It must be a JSON string with the following format: + * ``` + * { + * "engine": , + * "host": , + * "username": , + * "password": , + * "dbname": , + * "port": , + * "masterarn": + * } + * ``` + */ + readonly secret: secretsmanager.ISecret; + + /** + * Specifies the number of days after the previous rotation before + * Secrets Manager triggers the next automatic rotation. + * + * @default Duration.days(30) + */ + readonly automaticallyAfter?: Duration; +} diff --git a/packages/@aws-cdk/aws-rds/lib/secret-rotation.ts b/packages/@aws-cdk/aws-rds/lib/secret-rotation.ts deleted file mode 100644 index 78954397e1920..0000000000000 --- a/packages/@aws-cdk/aws-rds/lib/secret-rotation.ts +++ /dev/null @@ -1,146 +0,0 @@ -import * as ec2 from '@aws-cdk/aws-ec2'; -import * as lambda from '@aws-cdk/aws-lambda'; -import * as serverless from '@aws-cdk/aws-sam'; -import * as secretsmanager from '@aws-cdk/aws-secretsmanager'; -import { Construct, Duration, Stack } from '@aws-cdk/core'; - -/** - * A secret rotation serverless application. - */ -export class SecretRotationApplication { - public static readonly MARIADB_ROTATION_SINGLE_USER = new SecretRotationApplication('SecretsManagerRDSMariaDBRotationSingleUser', '1.0.57'); - public static readonly MARIADB_ROTATION_MULTI_USER = new SecretRotationApplication('SecretsManagerRDSMariaDBRotationMultiUser', '1.0.57'); - - public static readonly MYSQL_ROTATION_SINGLE_USER = new SecretRotationApplication('SecretsManagerRDSMySQLRotationSingleUser', '1.0.85'); - public static readonly MYSQL_ROTATION_MULTI_USER = new SecretRotationApplication('SecretsManagerRDSMySQLRotationMultiUser', '1.0.85'); - - public static readonly ORACLE_ROTATION_SINGLE_USER = new SecretRotationApplication('SecretsManagerRDSOracleRotationSingleUser', '1.0.56'); - public static readonly ORACLE_ROTATION_MULTI_USER = new SecretRotationApplication('SecretsManagerRDSOracleRotationMultiUser', '1.0.56'); - - public static readonly POSTGRES_ROTATION_SINGLE_USER = new SecretRotationApplication('SecretsManagerRDSPostgreSQLRotationSingleUser', '1.0.86'); - public static readonly POSTGRES_ROTATION_MULTI_USER = new SecretRotationApplication('SecretsManagerRDSPostgreSQLRotationMultiUser ', '1.0.86'); - - public static readonly SQLSERVER_ROTATION_SINGLE_USER = new SecretRotationApplication('SecretsManagerRDSSQLServerRotationSingleUser', '1.0.57'); - public static readonly SQLSERVER_ROTATION_MULTI_USER = new SecretRotationApplication('SecretsManagerRDSSQLServerRotationMultiUser', '1.0.57'); - - public readonly applicationId: string; - public readonly semanticVersion: string; - - constructor(applicationId: string, semanticVersion: string) { - this.applicationId = `arn:aws:serverlessrepo:us-east-1:297356227824:applications/${applicationId}`; - this.semanticVersion = semanticVersion; - } -} - -/** - * Options to add secret rotation to a database instance or cluster. - */ -export interface SecretRotationOptions { - /** - * Specifies the number of days after the previous rotation before - * Secrets Manager triggers the next automatic rotation. - * - * @default Duration.days(30) - */ - readonly automaticallyAfter?: Duration; -} - -/** - * Construction properties for a SecretRotation. - */ -export interface SecretRotationProps extends SecretRotationOptions { - /** - * The secret to rotate. It must be a JSON string with the following format: - * { - * 'engine': , - * 'host': , - * 'username': , - * 'password': , - * 'dbname': , - * 'port': , - * 'masterarn': - * } - * - * This is typically the case for a secret referenced from an AWS::SecretsManager::SecretTargetAttachment - * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-secretsmanager-secrettargetattachment.html - */ - readonly secret: secretsmanager.ISecret; - - /** - * The serverless application for the rotation. - */ - readonly application: SecretRotationApplication; - - /** - * The VPC where the Lambda rotation function will run. - */ - readonly vpc: ec2.IVpc; - - /** - * The type of subnets in the VPC where the Lambda rotation function will run. - * - * @default - Private subnets. - */ - readonly vpcSubnets?: ec2.SubnetSelection; - - /** - * The target database cluster or instance - */ - readonly target: ec2.IConnectable; -} - -/** - * Secret rotation for a database instance or cluster. - */ -export class SecretRotation extends Construct { - constructor(scope: Construct, id: string, props: SecretRotationProps) { - super(scope, id); - - if (!props.target.connections.defaultPort) { - throw new Error('The `target` connections must have a default port range.'); - } - - const rotationFunctionName = this.node.uniqueId; - - const securityGroup = new ec2.SecurityGroup(this, 'SecurityGroup', { - vpc: props.vpc - }); - - const { subnetIds } = props.vpc.selectSubnets(props.vpcSubnets); - - props.target.connections.allowDefaultPortFrom(securityGroup); - - const application = new serverless.CfnApplication(this, 'Resource', { - location: props.application, - parameters: { - endpoint: `https://secretsmanager.${Stack.of(this).region}.${Stack.of(this).urlSuffix}`, - functionName: rotationFunctionName, - vpcSecurityGroupIds: securityGroup.securityGroupId, - vpcSubnetIds: subnetIds.join(',') - } - }); - - // Dummy import to reference this function in the rotation schedule - const rotationLambda = lambda.Function.fromFunctionArn(this, 'RotationLambda', Stack.of(this).formatArn({ - service: 'lambda', - resource: 'function', - sep: ':', - resourceName: rotationFunctionName - })); - - // Cannot use rotationLambda.addPermission because it's a no-op on imported - // functions. - const permission = new lambda.CfnPermission(this, 'Permission', { - action: 'lambda:InvokeFunction', - functionName: rotationFunctionName, - principal: `secretsmanager.${Stack.of(this).urlSuffix}` - }); - permission.node.addDependency(application); // Add permission after application is deployed - - const rotationSchedule = props.secret.addRotationSchedule('RotationSchedule', { - rotationLambda, - automaticallyAfter: props.automaticallyAfter - }); - rotationSchedule.node.addDependency(permission); // Cannot rotate without permission - } -} diff --git a/packages/@aws-cdk/aws-rds/package.json b/packages/@aws-cdk/aws-rds/package.json index 505e8f0c25e62..eeeab965a30ba 100644 --- a/packages/@aws-cdk/aws-rds/package.json +++ b/packages/@aws-cdk/aws-rds/package.json @@ -79,7 +79,6 @@ "@aws-cdk/aws-kms": "1.19.0", "@aws-cdk/aws-lambda": "1.19.0", "@aws-cdk/aws-logs": "1.19.0", - "@aws-cdk/aws-sam": "1.19.0", "@aws-cdk/aws-secretsmanager": "1.19.0", "@aws-cdk/core": "1.19.0" }, @@ -92,7 +91,6 @@ "@aws-cdk/aws-kms": "1.19.0", "@aws-cdk/aws-lambda": "1.19.0", "@aws-cdk/aws-logs": "1.19.0", - "@aws-cdk/aws-sam": "1.19.0", "@aws-cdk/aws-secretsmanager": "1.19.0", "@aws-cdk/core": "1.19.0" }, diff --git a/packages/@aws-cdk/aws-rds/test/integ.cluster-rotation.lit.expected.json b/packages/@aws-cdk/aws-rds/test/integ.cluster-rotation.lit.expected.json index 7c42502250dbd..e600b21e4b377 100644 --- a/packages/@aws-cdk/aws-rds/test/integ.cluster-rotation.lit.expected.json +++ b/packages/@aws-cdk/aws-rds/test/integ.cluster-rotation.lit.expected.json @@ -548,11 +548,11 @@ } } }, - "DatabaseSecurityGroupfromawscdkrdsclusterrotationDatabaseRotationSecurityGroup35913E19IndirectPort12DA2942": { + "DatabaseSecurityGroupfromawscdkrdsclusterrotationDatabaseRotationSingleUserSecurityGroup0FFF34B1IndirectPortE6A88723": { "Type": "AWS::EC2::SecurityGroupIngress", "Properties": { "IpProtocol": "tcp", - "Description": "from awscdkrdsclusterrotationDatabaseRotationSecurityGroup35913E19:{IndirectPort}", + "Description": "from awscdkrdsclusterrotationDatabaseRotationSingleUserSecurityGroup0FFF34B1:{IndirectPort}", "FromPort": { "Fn::GetAtt": [ "DatabaseB269D8BB", @@ -567,7 +567,7 @@ }, "SourceSecurityGroupId": { "Fn::GetAtt": [ - "DatabaseRotationSecurityGroup17736B63", + "DatabaseRotationSingleUserSecurityGroupAC6E0E73", "GroupId" ] }, @@ -590,7 +590,7 @@ } } }, - "DatabaseSecretAttachedSecretE6CAC445": { + "DatabaseSecretAttachmentE5D1B020": { "Type": "AWS::SecretsManager::SecretTargetAttachment", "Properties": { "SecretId": { @@ -602,39 +602,58 @@ "TargetType": "AWS::RDS::DBCluster" } }, - "DatabaseSecretAttachedSecretRotationSchedule93D67FF7": { + "DatabaseSecretAttachmentRotationScheduleA4E9F034": { "Type": "AWS::SecretsManager::RotationSchedule", "Properties": { "SecretId": { - "Ref": "DatabaseSecretAttachedSecretE6CAC445" + "Ref": "DatabaseSecretAttachmentE5D1B020" }, "RotationLambdaARN": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":lambda:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":function:awscdkrdsclusterrotationDatabaseRotation30042AAE" - ] + "Fn::GetAtt": [ + "DatabaseRotationSingleUser65F55654", + "Outputs.RotationLambdaARN" ] }, "RotationRules": { "AutomaticallyAfterDays": 30 } - }, - "DependsOn": [ - "DatabaseRotationPermission64416CB0" - ] + } + }, + "DatabaseSecretAttachmentPolicy5ACFE6CA": { + "Type": "AWS::SecretsManager::ResourcePolicy", + "Properties": { + "ResourcePolicy": { + "Statement": [ + { + "Action": "secretsmanager:DeleteSecret", + "Effect": "Deny", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "SecretId": { + "Ref": "DatabaseSecretAttachmentE5D1B020" + } + } }, "DatabaseB269D8BB": { "Type": "AWS::RDS::DBCluster", @@ -682,7 +701,7 @@ "DatabaseInstance1844F58FD": { "Type": "AWS::RDS::DBInstance", "Properties": { - "DBInstanceClass": "db.t2.small", + "DBInstanceClass": "db.t3.small", "DBClusterIdentifier": { "Ref": "DatabaseB269D8BB" }, @@ -702,7 +721,7 @@ "DatabaseInstance2AA380DEE": { "Type": "AWS::RDS::DBInstance", "Properties": { - "DBInstanceClass": "db.t2.small", + "DBInstanceClass": "db.t3.small", "DBClusterIdentifier": { "Ref": "DatabaseB269D8BB" }, @@ -719,10 +738,10 @@ "UpdateReplacePolicy": "Retain", "DeletionPolicy": "Retain" }, - "DatabaseRotationSecurityGroup17736B63": { + "DatabaseRotationSingleUserSecurityGroupAC6E0E73": { "Type": "AWS::EC2::SecurityGroup", "Properties": { - "GroupDescription": "aws-cdk-rds-cluster-rotation/Database/Rotation/SecurityGroup", + "GroupDescription": "aws-cdk-rds-cluster-rotation/Database/RotationSingleUser/SecurityGroup", "SecurityGroupEgress": [ { "CidrIp": "0.0.0.0/0", @@ -735,12 +754,12 @@ } } }, - "DatabaseRotation6B6E1D86": { + "DatabaseRotationSingleUser65F55654": { "Type": "AWS::Serverless::Application", "Properties": { "Location": { "ApplicationId": "arn:aws:serverlessrepo:us-east-1:297356227824:applications/SecretsManagerRDSMySQLRotationSingleUser", - "SemanticVersion": "1.0.85" + "SemanticVersion": "1.1.3" }, "Parameters": { "endpoint": { @@ -758,13 +777,7 @@ ] ] }, - "functionName": "awscdkrdsclusterrotationDatabaseRotation30042AAE", - "vpcSecurityGroupIds": { - "Fn::GetAtt": [ - "DatabaseRotationSecurityGroup17736B63", - "GroupId" - ] - }, + "functionName": "awscdkrdsclusterrotationDatabaseRotationSingleUser171A8E3A", "vpcSubnetIds": { "Fn::Join": [ "", @@ -782,30 +795,15 @@ } ] ] + }, + "vpcSecurityGroupIds": { + "Fn::GetAtt": [ + "DatabaseRotationSingleUserSecurityGroupAC6E0E73", + "GroupId" + ] } } } - }, - "DatabaseRotationPermission64416CB0": { - "Type": "AWS::Lambda::Permission", - "Properties": { - "Action": "lambda:InvokeFunction", - "FunctionName": "awscdkrdsclusterrotationDatabaseRotation30042AAE", - "Principal": { - "Fn::Join": [ - "", - [ - "secretsmanager.", - { - "Ref": "AWS::URLSuffix" - } - ] - ] - } - }, - "DependsOn": [ - "DatabaseRotation6B6E1D86" - ] } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-rds/test/integ.cluster-rotation.lit.ts b/packages/@aws-cdk/aws-rds/test/integ.cluster-rotation.lit.ts index 95de32892b561..d98567d9c5090 100644 --- a/packages/@aws-cdk/aws-rds/test/integ.cluster-rotation.lit.ts +++ b/packages/@aws-cdk/aws-rds/test/integ.cluster-rotation.lit.ts @@ -14,12 +14,12 @@ const cluster = new rds.DatabaseCluster(stack, 'Database', { username: 'admin' }, instanceProps: { - instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL), + instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE3, ec2.InstanceSize.SMALL), vpc } }); -cluster.addRotationSingleUser('Rotation'); +cluster.addRotationSingleUser(); /// !hide app.synth(); 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 dd65c8979ae98..20efb10fbaa2b 100644 --- a/packages/@aws-cdk/aws-rds/test/integ.cluster.expected.json +++ b/packages/@aws-cdk/aws-rds/test/integ.cluster.expected.json @@ -504,7 +504,7 @@ "DatabaseInstance1844F58FD": { "Type": "AWS::RDS::DBInstance", "Properties": { - "DBInstanceClass": "db.t2.small", + "DBInstanceClass": "db.t3.small", "DBClusterIdentifier": { "Ref": "DatabaseB269D8BB" }, @@ -524,7 +524,7 @@ "DatabaseInstance2AA380DEE": { "Type": "AWS::RDS::DBInstance", "Properties": { - "DBInstanceClass": "db.t2.small", + "DBInstanceClass": "db.t3.small", "DBClusterIdentifier": { "Ref": "DatabaseB269D8BB" }, diff --git a/packages/@aws-cdk/aws-rds/test/integ.cluster.ts b/packages/@aws-cdk/aws-rds/test/integ.cluster.ts index be19a24df4bf5..9e733ade94239 100644 --- a/packages/@aws-cdk/aws-rds/test/integ.cluster.ts +++ b/packages/@aws-cdk/aws-rds/test/integ.cluster.ts @@ -25,7 +25,7 @@ const cluster = new DatabaseCluster(stack, 'Database', { password: cdk.SecretValue.plainText('7959866cacc02c2d243ecfe177464fe6'), }, instanceProps: { - instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL), + instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE3, ec2.InstanceSize.SMALL), vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC }, vpc }, diff --git a/packages/@aws-cdk/aws-rds/test/integ.instance.lit.expected.json b/packages/@aws-cdk/aws-rds/test/integ.instance.lit.expected.json index c3ef78e333069..7845f40ddb0e4 100644 --- a/packages/@aws-cdk/aws-rds/test/integ.instance.lit.expected.json +++ b/packages/@aws-cdk/aws-rds/test/integ.instance.lit.expected.json @@ -472,11 +472,11 @@ } } }, - "InstanceSecurityGroupfromawscdkrdsinstanceInstanceRotationSecurityGroupBB71D98EIndirectPort60E4E51A": { + "InstanceSecurityGroupfromawscdkrdsinstanceInstanceRotationSingleUserSecurityGroupE959D912IndirectPortFF39F421": { "Type": "AWS::EC2::SecurityGroupIngress", "Properties": { "IpProtocol": "tcp", - "Description": "from awscdkrdsinstanceInstanceRotationSecurityGroupBB71D98E:{IndirectPort}", + "Description": "from awscdkrdsinstanceInstanceRotationSingleUserSecurityGroupE959D912:{IndirectPort}", "FromPort": { "Fn::GetAtt": [ "InstanceC1063A87", @@ -491,7 +491,7 @@ }, "SourceSecurityGroupId": { "Fn::GetAtt": [ - "InstanceRotationSecurityGroupEF8D211E", + "InstanceRotationSingleUserSecurityGroupF3FB5C25", "GroupId" ] }, @@ -545,7 +545,7 @@ } } }, - "InstanceSecretAttachedSecretBACA1D43": { + "InstanceSecretAttachment83BEE581": { "Type": "AWS::SecretsManager::SecretTargetAttachment", "Properties": { "SecretId": { @@ -557,44 +557,63 @@ "TargetType": "AWS::RDS::DBInstance" } }, - "InstanceSecretAttachedSecretRotationSchedule275109B7": { + "InstanceSecretAttachmentRotationScheduleCC555119": { "Type": "AWS::SecretsManager::RotationSchedule", "Properties": { "SecretId": { - "Ref": "InstanceSecretAttachedSecretBACA1D43" + "Ref": "InstanceSecretAttachment83BEE581" }, "RotationLambdaARN": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":lambda:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":function:awscdkrdsinstanceInstanceRotation0925DC60" - ] + "Fn::GetAtt": [ + "InstanceRotationSingleUser90E8AB49", + "Outputs.RotationLambdaARN" ] }, "RotationRules": { "AutomaticallyAfterDays": 30 } - }, - "DependsOn": [ - "InstanceRotationPermission63844D0A" - ] + } + }, + "InstanceSecretAttachmentPolicy60A8B8DE": { + "Type": "AWS::SecretsManager::ResourcePolicy", + "Properties": { + "ResourcePolicy": { + "Statement": [ + { + "Action": "secretsmanager:DeleteSecret", + "Effect": "Deny", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "SecretId": { + "Ref": "InstanceSecretAttachment83BEE581" + } + } }, "InstanceC1063A87": { "Type": "AWS::RDS::DBInstance", "Properties": { - "DBInstanceClass": "db.t2.medium", + "DBInstanceClass": "db.t3.medium", "AllocatedStorage": "100", "AutoMinorVersionUpgrade": false, "BackupRetentionPeriod": 7, @@ -763,10 +782,10 @@ "RetentionInDays": 30 } }, - "InstanceRotationSecurityGroupEF8D211E": { + "InstanceRotationSingleUserSecurityGroupF3FB5C25": { "Type": "AWS::EC2::SecurityGroup", "Properties": { - "GroupDescription": "aws-cdk-rds-instance/Instance/Rotation/SecurityGroup", + "GroupDescription": "aws-cdk-rds-instance/Instance/RotationSingleUser/SecurityGroup", "SecurityGroupEgress": [ { "CidrIp": "0.0.0.0/0", @@ -779,12 +798,12 @@ } } }, - "InstanceRotationAA37A997": { + "InstanceRotationSingleUser90E8AB49": { "Type": "AWS::Serverless::Application", "Properties": { "Location": { "ApplicationId": "arn:aws:serverlessrepo:us-east-1:297356227824:applications/SecretsManagerRDSOracleRotationSingleUser", - "SemanticVersion": "1.0.56" + "SemanticVersion": "1.1.3" }, "Parameters": { "endpoint": { @@ -802,13 +821,7 @@ ] ] }, - "functionName": "awscdkrdsinstanceInstanceRotation0925DC60", - "vpcSecurityGroupIds": { - "Fn::GetAtt": [ - "InstanceRotationSecurityGroupEF8D211E", - "GroupId" - ] - }, + "functionName": "awscdkrdsinstanceInstanceRotationSingleUserAFE3C214", "vpcSubnetIds": { "Fn::Join": [ "", @@ -822,31 +835,16 @@ } ] ] + }, + "vpcSecurityGroupIds": { + "Fn::GetAtt": [ + "InstanceRotationSingleUserSecurityGroupF3FB5C25", + "GroupId" + ] } } } }, - "InstanceRotationPermission63844D0A": { - "Type": "AWS::Lambda::Permission", - "Properties": { - "Action": "lambda:InvokeFunction", - "FunctionName": "awscdkrdsinstanceInstanceRotation0925DC60", - "Principal": { - "Fn::Join": [ - "", - [ - "secretsmanager.", - { - "Ref": "AWS::URLSuffix" - } - ] - ] - } - }, - "DependsOn": [ - "InstanceRotationAA37A997" - ] - }, "InstanceAvailabilityAD5D452C": { "Type": "AWS::Events::Rule", "Properties": { @@ -1113,4 +1111,4 @@ "Description": "Artifact hash for asset \"66b36b09a60a9569c7e68ece07c423f900fda09bfebf230559a4dc2b24562cfb\"" } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-rds/test/integ.instance.lit.ts b/packages/@aws-cdk/aws-rds/test/integ.instance.lit.ts index 634c67119084f..ff78056759656 100644 --- a/packages/@aws-cdk/aws-rds/test/integ.instance.lit.ts +++ b/packages/@aws-cdk/aws-rds/test/integ.instance.lit.ts @@ -46,7 +46,7 @@ class DatabaseInstanceStack extends cdk.Stack { const instance = new rds.DatabaseInstance(this, 'Instance', { engine: rds.DatabaseInstanceEngine.ORACLE_SE1, licenseModel: rds.LicenseModel.BRING_YOUR_OWN_LICENSE, - instanceClass: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.MEDIUM), + instanceClass: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE3, ec2.InstanceSize.MEDIUM), multiAz: true, storageType: rds.StorageType.IO1, masterUsername: 'syscdk', @@ -72,7 +72,7 @@ class DatabaseInstanceStack extends cdk.Stack { instance.connections.allowDefaultPortFromAnyIpv4(); // Rotate the master user password every 30 days - instance.addRotationSingleUser('Rotation'); + instance.addRotationSingleUser(); // Add alarm for high CPU new cloudwatch.Alarm(this, 'HighCPU', { diff --git a/packages/@aws-cdk/aws-rds/test/test.cluster.ts b/packages/@aws-cdk/aws-rds/test/test.cluster.ts index e61200ed3177c..95fad9cf384ed 100644 --- a/packages/@aws-cdk/aws-rds/test/test.cluster.ts +++ b/packages/@aws-cdk/aws-rds/test/test.cluster.ts @@ -469,7 +469,53 @@ export = { }, ResourcePart.Properties)); test.done(); - } + }, + + 'throws when trying to add rotation to a cluster without secret'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + + // WHEN + const cluster = new DatabaseCluster(stack, 'Database', { + engine: DatabaseClusterEngine.AURORA_MYSQL, + masterUser: { + username: 'admin', + password: cdk.SecretValue.plainText('tooshort') + }, + instanceProps: { + instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL), + vpc + } + }); + + // THEN + test.throws(() => cluster.addRotationSingleUser(), /without secret/); + + test.done(); + }, + + 'throws when trying to add single user rotation multiple timet'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + const cluster = new DatabaseCluster(stack, 'Database', { + engine: DatabaseClusterEngine.AURORA_MYSQL, + masterUser: { username: 'admin' }, + instanceProps: { + instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL), + vpc + } + }); + + // WHEN + cluster.addRotationSingleUser(); + + // THEN + test.throws(() => cluster.addRotationSingleUser(), /A single user rotation was already added to this cluster/); + + test.done(); + }, }; function testStack() { diff --git a/packages/@aws-cdk/aws-rds/test/test.instance.ts b/packages/@aws-cdk/aws-rds/test/test.instance.ts index e76e3a1b66d4b..30b519d4432f7 100644 --- a/packages/@aws-cdk/aws-rds/test/test.instance.ts +++ b/packages/@aws-cdk/aws-rds/test/test.instance.ts @@ -623,5 +623,43 @@ export = { })); test.done(); - } + }, + + 'throws when trying to add rotation to an instance without secret'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + const instance = new rds.DatabaseInstance(stack, 'Database', { + engine: rds.DatabaseInstanceEngine.SQL_SERVER_EE, + instanceClass: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL), + masterUsername: 'syscdk', + masterUserPassword: cdk.SecretValue.plainText('tooshort'), + vpc + }); + + // THEN + test.throws(() => instance.addRotationSingleUser(), /without secret/); + + test.done(); + }, + + 'throws when trying to add single user rotation multiple times'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + const instance = new rds.DatabaseInstance(stack, 'Database', { + engine: rds.DatabaseInstanceEngine.SQL_SERVER_EE, + instanceClass: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL), + masterUsername: 'syscdk', + vpc + }); + + // WHEN + instance.addRotationSingleUser(); + + // THEN + test.throws(() => instance.addRotationSingleUser(), /A single user rotation was already added to this instance/); + + test.done(); + }, }; diff --git a/packages/@aws-cdk/aws-rds/test/test.secret-rotation.ts b/packages/@aws-cdk/aws-rds/test/test.secret-rotation.ts deleted file mode 100644 index af79111b86024..0000000000000 --- a/packages/@aws-cdk/aws-rds/test/test.secret-rotation.ts +++ /dev/null @@ -1,365 +0,0 @@ -import { expect, haveResource } from '@aws-cdk/assert'; -import * as ec2 from '@aws-cdk/aws-ec2'; -import * as secretsmanager from '@aws-cdk/aws-secretsmanager'; -import * as cdk from '@aws-cdk/core'; -import { Test } from 'nodeunit'; -import * as rds from '../lib'; - -// tslint:disable:object-literal-key-quotes - -export = { - 'add a rds rotation single user to a cluster'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'VPC'); - const cluster = new rds.DatabaseCluster(stack, 'Database', { - engine: rds.DatabaseClusterEngine.AURORA_MYSQL, - masterUser: { - username: 'admin' - }, - instanceProps: { - instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL), - vpc - } - }); - - // WHEN - cluster.addRotationSingleUser('Rotation'); - - // THEN - expect(stack).to(haveResource('AWS::EC2::SecurityGroupIngress', { - "IpProtocol": "tcp", - "Description": "from DatabaseRotationSecurityGroup1C5A8031:{IndirectPort}", - "FromPort": { - "Fn::GetAtt": [ - "DatabaseB269D8BB", - "Endpoint.Port" - ] - }, - "GroupId": { - "Fn::GetAtt": [ - "DatabaseSecurityGroup5C91FDCB", - "GroupId" - ] - }, - "SourceSecurityGroupId": { - "Fn::GetAtt": [ - "DatabaseRotationSecurityGroup17736B63", - "GroupId" - ] - }, - "ToPort": { - "Fn::GetAtt": [ - "DatabaseB269D8BB", - "Endpoint.Port" - ] - } - })); - - expect(stack).to(haveResource('AWS::SecretsManager::RotationSchedule', { - "SecretId": { - "Ref": "DatabaseSecretAttachedSecretE6CAC445" - }, - "RotationLambdaARN": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":lambda:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":function:DatabaseRotation0D47EBD2" - ] - ] - }, - "RotationRules": { - "AutomaticallyAfterDays": 30 - } - })); - - expect(stack).to(haveResource('AWS::EC2::SecurityGroup', { - "GroupDescription": "Database/Rotation/SecurityGroup" - })); - - expect(stack).to(haveResource('AWS::Serverless::Application', { - "Location": { - "ApplicationId": "arn:aws:serverlessrepo:us-east-1:297356227824:applications/SecretsManagerRDSMySQLRotationSingleUser", - "SemanticVersion": "1.0.85" - }, - "Parameters": { - "endpoint": { - "Fn::Join": [ - "", - [ - "https://secretsmanager.", - { - "Ref": "AWS::Region" - }, - ".", - { - "Ref": "AWS::URLSuffix" - } - ] - ] - }, - "functionName": "DatabaseRotation0D47EBD2", - "vpcSecurityGroupIds": { - "Fn::GetAtt": [ - "DatabaseRotationSecurityGroup17736B63", - "GroupId" - ] - }, - "vpcSubnetIds": { - "Fn::Join": [ - "", - [ - { - "Ref": "VPCPrivateSubnet1Subnet8BCA10E0" - }, - ",", - { - "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A" - }, - ] - ] - } - } - })); - - expect(stack).to(haveResource('AWS::Lambda::Permission', { - "Action": "lambda:InvokeFunction", - "FunctionName": "DatabaseRotation0D47EBD2", - "Principal": { - "Fn::Join": [ - "", - [ - "secretsmanager.", - { - "Ref": "AWS::URLSuffix" - } - ] - ] - } - })); - - test.done(); - }, - - 'throws when trying to add rotation to a cluster without secret'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'VPC'); - - // WHEN - const cluster = new rds.DatabaseCluster(stack, 'Database', { - engine: rds.DatabaseClusterEngine.AURORA_MYSQL, - masterUser: { - username: 'admin', - password: cdk.SecretValue.plainText('tooshort') - }, - instanceProps: { - instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL), - vpc - } - }); - - // THEN - test.throws(() => cluster.addRotationSingleUser('Rotation'), /without secret/); - - test.done(); - }, - - 'throws when connections object has no default port range'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'VPC'); - const secret = new secretsmanager.Secret(stack, 'Secret'); - const securityGroup = new ec2.SecurityGroup(stack, 'SecurityGroup', { - vpc, - }); - - // WHEN - const target = new ec2.Connections({ - securityGroups: [securityGroup] - }); - - // THEN - test.throws(() => new rds.SecretRotation(stack, 'Rotation', { - secret, - application: rds.SecretRotationApplication.MYSQL_ROTATION_SINGLE_USER, - vpc, - target - }), /`target`.+default port range/); - - test.done(); - }, - - 'add a rds rotation single user to an instance'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'VPC'); - const instance = new rds.DatabaseInstance(stack, 'Database', { - engine: rds.DatabaseInstanceEngine.MARIADB, - instanceClass: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL), - masterUsername: 'syscdk', - vpc - }); - - // WHEN - instance.addRotationSingleUser('Rotation'); - - // THEN - expect(stack).to(haveResource('AWS::EC2::SecurityGroupIngress', { - "IpProtocol": "tcp", - "Description": "from DatabaseRotationSecurityGroup1C5A8031:{IndirectPort}", - "FromPort": { - "Fn::GetAtt": [ - "DatabaseB269D8BB", - "Endpoint.Port" - ] - }, - "GroupId": { - "Fn::GetAtt": [ - "DatabaseSecurityGroup5C91FDCB", - "GroupId" - ] - }, - "SourceSecurityGroupId": { - "Fn::GetAtt": [ - "DatabaseRotationSecurityGroup17736B63", - "GroupId" - ] - }, - "ToPort": { - "Fn::GetAtt": [ - "DatabaseB269D8BB", - "Endpoint.Port" - ] - } - })); - - expect(stack).to(haveResource('AWS::SecretsManager::RotationSchedule', { - "SecretId": { - "Ref": "DatabaseSecretAttachedSecretE6CAC445" - }, - "RotationLambdaARN": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":lambda:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":function:DatabaseRotation0D47EBD2" - ] - ] - }, - "RotationRules": { - "AutomaticallyAfterDays": 30 - } - })); - - expect(stack).to(haveResource('AWS::EC2::SecurityGroup', { - "GroupDescription": "Database/Rotation/SecurityGroup" - })); - - expect(stack).to(haveResource('AWS::Serverless::Application', { - "Location": { - "ApplicationId": "arn:aws:serverlessrepo:us-east-1:297356227824:applications/SecretsManagerRDSMariaDBRotationSingleUser", - "SemanticVersion": "1.0.57" - }, - "Parameters": { - "endpoint": { - "Fn::Join": [ - "", - [ - "https://secretsmanager.", - { - "Ref": "AWS::Region" - }, - ".", - { - "Ref": "AWS::URLSuffix" - } - ] - ] - }, - "functionName": "DatabaseRotation0D47EBD2", - "vpcSecurityGroupIds": { - "Fn::GetAtt": [ - "DatabaseRotationSecurityGroup17736B63", - "GroupId" - ] - }, - "vpcSubnetIds": { - "Fn::Join": [ - "", - [ - { - "Ref": "VPCPrivateSubnet1Subnet8BCA10E0" - }, - ",", - { - "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A" - }, - ] - ] - } - } - })); - - expect(stack).to(haveResource('AWS::Lambda::Permission', { - "Action": "lambda:InvokeFunction", - "FunctionName": "DatabaseRotation0D47EBD2", - "Principal": { - "Fn::Join": [ - "", - [ - "secretsmanager.", - { - "Ref": "AWS::URLSuffix" - } - ] - ] - } - })); - - test.done(); - }, - - 'throws when trying to add rotation to an instance without secret'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'VPC'); - - // WHEN - const instance = new rds.DatabaseInstance(stack, 'Database', { - engine: rds.DatabaseInstanceEngine.SQL_SERVER_EE, - instanceClass: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL), - masterUsername: 'syscdk', - masterUserPassword: cdk.SecretValue.plainText('tooshort'), - vpc - }); - - // THEN - test.throws(() => instance.addRotationSingleUser('Rotation'), /without secret/); - - test.done(); - }, -}; diff --git a/packages/@aws-cdk/aws-secretsmanager/README.md b/packages/@aws-cdk/aws-secretsmanager/README.md index b36a9d7478089..b48b11c08a2ea 100644 --- a/packages/@aws-cdk/aws-secretsmanager/README.md +++ b/packages/@aws-cdk/aws-secretsmanager/README.md @@ -39,8 +39,8 @@ const secret = secretsmanager.Secret.fromSecretAttributes(scope, 'ImportedSecret SecretsManager secret values can only be used in select set of properties. For the list of properties, see [the CloudFormation Dynamic References documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.htm). -### Rotating a Secret -A rotation schedule can be added to a Secret: +### Rotating a Secret with a custom Lambda function +A rotation schedule can be added to a Secret using a custom Lambda function: ```ts const fn = new lambda.Function(...); const secret = new secretsmanager.Secret(this, 'Secret'); @@ -52,4 +52,40 @@ secret.addRotationSchedule('RotationSchedule', { ``` See [Overview of the Lambda Rotation Function](https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html) on how to implement a Lambda Rotation Function. -For RDS credentials rotation, see [aws-rds](https://github.com/aws/aws-cdk/blob/master/packages/%40aws-cdk/aws-rds/README.md). +### Rotating database credentials +Define a `SecretRotation` to rotate database credentials: +```ts +new SecretRotation(this, 'SecretRotation', { + application: SecretRotationApplication.MYSQL_ROTATION_SINGLE_USER, // MySQL single user scheme + secret: mySecret, + target: myDatabase, // a Connectable + vpc: myVpc, // The VPC where the secret rotation application will be deployed +}); +``` + +The secret must be a JSON string with the following format: +```json +{ + "engine": "", + "host": "", + "username": "", + "password": "", + "dbname": "", + "port": "", + "masterarn": "" +} +``` + +For the multi user scheme, a `masterSecret` must be specified: +```ts +new SecretRotation(stack, 'SecretRotation', { + application: SecretRotationApplication.MYSQL_ROTATION_MULTI_USER, + secret: myUserSecret, // The secret that will be rotated + masterSecret: myMasterSecret, // The secret used for the rotation + target: myDatabase, + vpc: myVpc, +}); +``` + +See also [aws-rds](https://github.com/aws/aws-cdk/blob/master/packages/%40aws-cdk/aws-rds/README.md) where +credentials generation and rotation is integrated. diff --git a/packages/@aws-cdk/aws-secretsmanager/lib/index.ts b/packages/@aws-cdk/aws-secretsmanager/lib/index.ts index 79568e657c24c..7a001d748decf 100644 --- a/packages/@aws-cdk/aws-secretsmanager/lib/index.ts +++ b/packages/@aws-cdk/aws-secretsmanager/lib/index.ts @@ -1,5 +1,7 @@ export * from './secret'; export * from './rotation-schedule'; +export * from './policy'; +export * from './secret-rotation'; // AWS::SecretsManager CloudFormation Resources: export * from './secretsmanager.generated'; diff --git a/packages/@aws-cdk/aws-secretsmanager/lib/policy.ts b/packages/@aws-cdk/aws-secretsmanager/lib/policy.ts new file mode 100644 index 0000000000000..54d6b2af5bb03 --- /dev/null +++ b/packages/@aws-cdk/aws-secretsmanager/lib/policy.ts @@ -0,0 +1,33 @@ +import * as iam from '@aws-cdk/aws-iam'; +import { Construct, Resource } from '@aws-cdk/core'; +import { ISecret } from './secret'; +import { CfnResourcePolicy } from './secretsmanager.generated'; + +/** + * Construction properties for a ResourcePolicy + */ +export interface ResourcePolicyProps { + /** + * The secret to attach a resource-based permissions policy + */ + readonly secret: ISecret; +} + +/** + * Secret Resource Policy + */ +export class ResourcePolicy extends Resource { + /** + * The IAM policy document for this policy. + */ + public readonly document = new iam.PolicyDocument(); + + constructor(scope: Construct, id: string, props: ResourcePolicyProps) { + super(scope, id); + + new CfnResourcePolicy(this, 'Resource', { + resourcePolicy: this.document, + secretId: props.secret.secretArn, + }); + } +} diff --git a/packages/@aws-cdk/aws-secretsmanager/lib/secret-rotation.ts b/packages/@aws-cdk/aws-secretsmanager/lib/secret-rotation.ts new file mode 100644 index 0000000000000..c3a6e58593ecf --- /dev/null +++ b/packages/@aws-cdk/aws-secretsmanager/lib/secret-rotation.ts @@ -0,0 +1,261 @@ +import * as ec2 from '@aws-cdk/aws-ec2'; +import * as lambda from '@aws-cdk/aws-lambda'; +import * as serverless from '@aws-cdk/aws-sam'; +import { Construct, Duration, Stack, Token } from '@aws-cdk/core'; +import { ISecret } from './secret'; + +/** + * Options for a SecretRotationApplication + */ +export interface SecretRotationApplicationOptions { + /** + * Whether the rotation application uses the mutli user scheme + * + * @default false + */ + readonly isMultiUser?: boolean; +} +/** + * A secret rotation serverless application. + */ +export class SecretRotationApplication { + /** + * Conducts an AWS SecretsManager secret rotation for RDS MariaDB using the single user rotation scheme + */ + public static readonly MARIADB_ROTATION_SINGLE_USER = new SecretRotationApplication('SecretsManagerRDSMariaDBRotationSingleUser', '1.1.3'); + + /** + * Conducts an AWS SecretsManager secret rotation for RDS MariaDB using the multi user rotation scheme + */ + public static readonly MARIADB_ROTATION_MULTI_USER = new SecretRotationApplication('SecretsManagerRDSMariaDBRotationMultiUser', '1.1.3', { + isMultiUser: true + }); + + /** + * Conducts an AWS SecretsManager secret rotation for RDS MySQL using the single user rotation scheme + */ + public static readonly MYSQL_ROTATION_SINGLE_USER = new SecretRotationApplication('SecretsManagerRDSMySQLRotationSingleUser', '1.1.3'); + + /** + * Conducts an AWS SecretsManager secret rotation for RDS MySQL using the multi user rotation scheme + */ + public static readonly MYSQL_ROTATION_MULTI_USER = new SecretRotationApplication('SecretsManagerRDSMySQLRotationMultiUser', '1.1.3', { + isMultiUser: true + }); + + /** + * Conducts an AWS SecretsManager secret rotation for RDS Oracle using the single user rotation scheme + */ + public static readonly ORACLE_ROTATION_SINGLE_USER = new SecretRotationApplication('SecretsManagerRDSOracleRotationSingleUser', '1.1.3'); + + /** + * Conducts an AWS SecretsManager secret rotation for RDS Oracle using the multi user rotation scheme + */ + public static readonly ORACLE_ROTATION_MULTI_USER = new SecretRotationApplication('SecretsManagerRDSOracleRotationMultiUser', '1.1.3', { + isMultiUser: true + }); + + /** + * Conducts an AWS SecretsManager secret rotation for RDS PostgreSQL using the single user rotation scheme + */ + public static readonly POSTGRES_ROTATION_SINGLE_USER = new SecretRotationApplication('SecretsManagerRDSPostgreSQLRotationSingleUser', '1.1.3'); + + /** + * Conducts an AWS SecretsManager secret rotation for RDS PostgreSQL using the multi user rotation scheme + */ + public static readonly POSTGRES_ROTATION_MULTI_USER = new SecretRotationApplication('SecretsManagerRDSPostgreSQLRotationMultiUser ', '1.1.3', { + isMultiUser: true + }); + + /** + * Conducts an AWS SecretsManager secret rotation for RDS SQL Server using the single user rotation scheme + */ + public static readonly SQLSERVER_ROTATION_SINGLE_USER = new SecretRotationApplication('SecretsManagerRDSSQLServerRotationSingleUser', '1.1.3'); + + /** + * Conducts an AWS SecretsManager secret rotation for RDS SQL Server using the multi user rotation scheme + */ + public static readonly SQLSERVER_ROTATION_MULTI_USER = new SecretRotationApplication('SecretsManagerRDSSQLServerRotationMultiUser', '1.1.3', { + isMultiUser: true + }); + + /** + * Conducts an AWS SecretsManager secret rotation for Amazon Redshift using the single user rotation scheme + */ + public static readonly REDSHIFT_ROTATION_SINGLE_USER = new SecretRotationApplication('SecretsManagerRedshiftRotationSingleUser', '1.1.3'); + + /** + * Conducts an AWS SecretsManager secret rotation for Amazon Redshift using the multi user rotation scheme + */ + public static readonly REDSHIFT_ROTATION_MULTI_USER = new SecretRotationApplication('SecretsManagerRedshiftRotationMultiUser', '1.1.3', { + isMultiUser: true + }); + + /** + * Conducts an AWS SecretsManager secret rotation for MongoDB using the single user rotation scheme + */ + public static readonly MONGODB_ROTATION_SINGLE_USER = new SecretRotationApplication('SecretsManagerMongoDBRotationSingleUser', '1.1.3'); + + /** + * Conducts an AWS SecretsManager secret rotation for MongoDB using the multi user rotation scheme + */ + public static readonly MONGODB_ROTATION_MULTI_USER = new SecretRotationApplication('SecretsManagerMongoDBRotationMultiUser', '1.1.3', { + isMultiUser: true + }); + + /** + * The application identifier of the rotation application + */ + public readonly applicationId: string; + + /** + * The semantic version of the rotation application + */ + public readonly semanticVersion: string; + + /** + * Whether the rotation application uses the mutli user scheme + */ + public readonly isMultiUser?: boolean; + + constructor(applicationId: string, semanticVersion: string, options?: SecretRotationApplicationOptions) { + this.applicationId = `arn:aws:serverlessrepo:us-east-1:297356227824:applications/${applicationId}`; + this.semanticVersion = semanticVersion; + this.isMultiUser = options && options.isMultiUser; + } +} + +/** + * Construction properties for a SecretRotation. + */ +export interface SecretRotationProps { + /** + * The secret to rotate. It must be a JSON string with the following format: + * ``` + * { + * "engine": , + * "host": , + * "username": , + * "password": , + * "dbname": , + * "port": , + * "masterarn": + * } + * ``` + * + * This is typically the case for a secret referenced from an + * AWS::SecretsManager::SecretTargetAttachment or an `ISecret` returned by the `attach()` method of `Secret`. + * + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-secretsmanager-secrettargetattachment.html + */ + readonly secret: ISecret; + + /** + * The master secret for a multi user rotation scheme + * + * @default - single user rotation scheme + */ + readonly masterSecret?: ISecret; + + /** + * Specifies the number of days after the previous rotation before + * Secrets Manager triggers the next automatic rotation. + * + * @default Duration.days(30) + */ + readonly automaticallyAfter?: Duration; + + /** + * The serverless application for the rotation. + */ + readonly application: SecretRotationApplication; + + /** + * The VPC where the Lambda rotation function will run. + */ + readonly vpc: ec2.IVpc; + + /** + * The type of subnets in the VPC where the Lambda rotation function will run. + * + * @default - Private subnets. + */ + readonly vpcSubnets?: ec2.SubnetSelection; + + /** + * The target service or database + */ + readonly target: ec2.IConnectable; + + /** + * The security group for the Lambda rotation function + * + * @default - a new security group is created + */ + readonly securityGroup?: ec2.ISecurityGroup; +} + +/** + * Secret rotation for a service or database + */ +export class SecretRotation extends Construct { + constructor(scope: Construct, id: string, props: SecretRotationProps) { + super(scope, id); + + if (!props.target.connections.defaultPort) { + throw new Error('The `target` connections must have a default port range.'); + } + + if (props.application.isMultiUser && !props.masterSecret) { + throw new Error('The `masterSecret` must be specified for application using the multi user scheme.'); + } + + const rotationFunctionName = this.node.uniqueId; + + const securityGroup = props.securityGroup || new ec2.SecurityGroup(this, 'SecurityGroup', { + vpc: props.vpc + }); + props.target.connections.allowDefaultPortFrom(securityGroup); + + const parameters: { [key: string]: string } = { + endpoint: `https://secretsmanager.${Stack.of(this).region}.${Stack.of(this).urlSuffix}`, + functionName: rotationFunctionName, + vpcSubnetIds: props.vpc.selectSubnets(props.vpcSubnets).subnetIds.join(','), + vpcSecurityGroupIds: securityGroup.securityGroupId, + }; + + if (props.secret.encryptionKey) { + parameters.kmsKeyArn = props.secret.encryptionKey.keyArn; + } + + if (props.masterSecret) { + parameters.masterSecretArn = props.masterSecret.secretArn; + + if (props.masterSecret.encryptionKey) { + parameters.masterSecretKmsKeyArn = props.masterSecret.encryptionKey.keyArn; + } + } + + const application = new serverless.CfnApplication(this, 'Resource', { + location: props.application, + parameters, + }); + + // This creates a CF a dependency between the rotation schedule and the + // serverless application. This is needed because it's the application + // that creates the Lambda permission to invoke the function. + // See https://docs.aws.amazon.com/secretsmanager/latest/userguide/integrating_cloudformation.html + const rotationLambda = lambda.Function.fromFunctionArn(this, 'RotationLambda', Token.asString(application.getAtt('Outputs.RotationLambdaARN'))); + + props.secret.addRotationSchedule('RotationSchedule', { + rotationLambda, + automaticallyAfter: props.automaticallyAfter + }); + + // Prevent secrets deletions when rotation is in place + props.secret.denyAccountRootDelete(); + if (props.masterSecret) { + props.masterSecret.denyAccountRootDelete(); + } + } +} diff --git a/packages/@aws-cdk/aws-secretsmanager/lib/secret.ts b/packages/@aws-cdk/aws-secretsmanager/lib/secret.ts index f83275d6e4cb3..2ad76994d3090 100644 --- a/packages/@aws-cdk/aws-secretsmanager/lib/secret.ts +++ b/packages/@aws-cdk/aws-secretsmanager/lib/secret.ts @@ -1,6 +1,7 @@ import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; import { Construct, IResource, Resource, SecretValue, Stack } from '@aws-cdk/core'; +import { ResourcePolicy } from './policy'; import { RotationSchedule, RotationScheduleOptions } from './rotation-schedule'; import * as secretsmanager from './secretsmanager.generated'; @@ -44,6 +45,21 @@ export interface ISecret extends IResource { * Adds a rotation schedule to the secret. */ addRotationSchedule(id: string, options: RotationScheduleOptions): RotationSchedule; + + /** + * Adds a statement to the IAM resource policy associated with this secret. + * + * If this secret was created in this stack, a resource policy will be + * automatically created upon the first call to `addToResourcePolicy`. If + * the secret is imported, then this is a no-op. + */ + addToResourcePolicy(statement: iam.PolicyStatement): void; + + /** + * Denies the `DeleteSecret` action to all principals within the current + * account. + */ + denyAccountRootDelete(): void; } /** @@ -103,6 +119,10 @@ abstract class SecretBase extends Resource implements ISecret { public abstract readonly encryptionKey?: kms.IKey; public abstract readonly secretArn: string; + protected abstract readonly autoCreatePolicy: boolean; + + private policy?: ResourcePolicy; + public grantRead(grantee: iam.IGrantable, versionStages?: string[]): iam.Grant { // @see https://docs.aws.amazon.com/fr_fr/secretsmanager/latest/userguide/auth-and-access_identity-based-policies.html @@ -142,6 +162,25 @@ abstract class SecretBase extends Resource implements ISecret { ...options }); } + + public addToResourcePolicy(statement: iam.PolicyStatement) { + if (!this.policy && this.autoCreatePolicy) { + this.policy = new ResourcePolicy(this, 'Policy', { secret: this }); + } + + if (this.policy) { + this.policy.document.addStatements(statement); + } + } + + public denyAccountRootDelete() { + this.addToResourcePolicy(new iam.PolicyStatement({ + actions: ['secretsmanager:DeleteSecret'], + effect: iam.Effect.DENY, + resources: ['*'], + principals: [new iam.AccountRootPrincipal()] + })); + } } /** @@ -164,6 +203,7 @@ export class Secret extends SecretBase { class Import extends SecretBase { public readonly encryptionKey = attrs.encryptionKey; public readonly secretArn = attrs.secretArn; + protected readonly autoCreatePolicy = false; } return new Import(scope, id); @@ -172,6 +212,8 @@ export class Secret extends SecretBase { public readonly encryptionKey?: kms.IKey; public readonly secretArn: string; + protected readonly autoCreatePolicy = true; + constructor(scope: Construct, id: string, props: SecretProps = {}) { super(scope, id, { physicalName: props.secretName, @@ -204,6 +246,8 @@ export class Secret extends SecretBase { * Adds a target attachment to the secret. * * @returns an AttachedSecret + * + * @deprecated use `attach()` instead */ public addTargetAttachment(id: string, options: AttachedSecretOptions): SecretTargetAttachment { return new SecretTargetAttachment(this, id, { @@ -211,6 +255,26 @@ export class Secret extends SecretBase { ...options }); } + + /** + * Attach a target to this secret + * + * @param target The target to attach + * @returns An attached secret + */ + public attach(target: ISecretAttachmentTarget): ISecret { + const id = 'Attachment'; + const existing = this.node.tryFindChild(id); + + if (existing) { + throw new Error('Secret is already attached to a target.'); + } + + return new SecretTargetAttachment(this, id, { + secret: this, + target, + }); + } } /** @@ -225,17 +289,48 @@ export interface ISecretAttachmentTarget { /** * The type of service or database that's being associated with the secret. + * + * @deprecated use `SecretTargetType` instead */ export enum AttachmentTargetType { /** * A database instance + * + * @deprecated use RDS_DB_INSTANCE instead */ INSTANCE = 'AWS::RDS::DBInstance', /** * A database cluster + * + * @deprecated use RDS_DB_CLUSTER instead + */ + CLUSTER = 'AWS::RDS::DBCluster', + + /** + * AWS::RDS::DBInstance */ - CLUSTER = 'AWS::RDS::DBCluster' + RDS_DB_INSTANCE = 'AWS::RDS::DBInstance', + + /** + * AWS::RDS::DBCluster + */ + RDS_DB_CLUSTER = 'AWS::RDS::DBCluster', + + /** + * AWS::Redshift::Cluster + */ + REDSHIFT_CLUSTER = 'AWS::Redshift::Cluster', + + /** + * AWS::DocDB::DBInstance + */ + DOCDB_DB_INSTANCE = 'AWS::DocDB::DBInstance', + + /** + * AWS::DocDB::DBCluster + */ + DOCDB_DB_CLUSTER = 'AWS::DocDB::DBCluster' } /** @@ -255,6 +350,8 @@ export interface SecretAttachmentTargetProps { /** * Options to add a secret attachment to a secret. + * + * @deprecated use `secret.attach()` instead */ export interface AttachedSecretOptions { /** @@ -292,6 +389,7 @@ export class SecretTargetAttachment extends SecretBase implements ISecretTargetA public encryptionKey?: kms.IKey | undefined; public secretArn = secretTargetAttachmentSecretArn; public secretTargetAttachmentSecretArn = secretTargetAttachmentSecretArn; + protected readonly autoCreatePolicy = false; } return new Import(scope, id); @@ -305,6 +403,8 @@ export class SecretTargetAttachment extends SecretBase implements ISecretTargetA */ public readonly secretTargetAttachmentSecretArn: string; + protected readonly autoCreatePolicy = true; + constructor(scope: Construct, id: string, props: SecretTargetAttachmentProps) { super(scope, id); diff --git a/packages/@aws-cdk/aws-secretsmanager/package.json b/packages/@aws-cdk/aws-secretsmanager/package.json index b01da67d11f6e..5921ac870aaa8 100644 --- a/packages/@aws-cdk/aws-secretsmanager/package.json +++ b/packages/@aws-cdk/aws-secretsmanager/package.json @@ -76,6 +76,7 @@ "@aws-cdk/aws-iam": "1.19.0", "@aws-cdk/aws-kms": "1.19.0", "@aws-cdk/aws-lambda": "1.19.0", + "@aws-cdk/aws-sam": "1.19.0", "@aws-cdk/core": "1.19.0" }, "peerDependencies": { @@ -83,6 +84,7 @@ "@aws-cdk/aws-iam": "1.19.0", "@aws-cdk/aws-kms": "1.19.0", "@aws-cdk/aws-lambda": "1.19.0", + "@aws-cdk/aws-sam": "1.19.0", "@aws-cdk/core": "1.19.0" }, "engines": { @@ -99,7 +101,8 @@ "props-default-doc:@aws-cdk/aws-secretsmanager.SecretStringGenerator.generateStringKey", "props-default-doc:@aws-cdk/aws-secretsmanager.SecretAttributes.encryptionKey", "docs-public-apis:@aws-cdk/aws-secretsmanager.ISecretTargetAttachment", - "docs-public-apis:@aws-cdk/aws-secretsmanager.SecretTargetAttachment.fromSecretTargetAttachmentSecretArn" + "docs-public-apis:@aws-cdk/aws-secretsmanager.SecretTargetAttachment.fromSecretTargetAttachmentSecretArn", + "props-physical-name:@aws-cdk/aws-secretsmanager.ResourcePolicyProps" ] }, "stability": "stable" diff --git a/packages/@aws-cdk/aws-secretsmanager/test/test.secret-rotation.ts b/packages/@aws-cdk/aws-secretsmanager/test/test.secret-rotation.ts new file mode 100644 index 0000000000000..97675c03c712b --- /dev/null +++ b/packages/@aws-cdk/aws-secretsmanager/test/test.secret-rotation.ts @@ -0,0 +1,294 @@ +import { expect, haveResource } from '@aws-cdk/assert'; +import * as ec2 from '@aws-cdk/aws-ec2'; +import * as cdk from '@aws-cdk/core'; +import { Test } from 'nodeunit'; +import * as secretsmanager from '../lib'; + +export = { + 'secret rotation single user'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + const secret = new secretsmanager.Secret(stack, 'Secret'); + const target = new ec2.Connections({ + defaultPort: ec2.Port.tcp(3306), + securityGroups: [new ec2.SecurityGroup(stack, 'SecurityGroup', { vpc })] + }); + + // WHEN + new secretsmanager.SecretRotation(stack, 'SecretRotation', { + application: secretsmanager.SecretRotationApplication.MYSQL_ROTATION_SINGLE_USER, + secret, + target, + vpc, + }); + + // THEN + expect(stack).to(haveResource('AWS::EC2::SecurityGroupIngress', { + IpProtocol: 'tcp', + Description: 'from SecretRotationSecurityGroupAEC520AB:3306', + FromPort: 3306, + GroupId: { + 'Fn::GetAtt': [ + 'SecurityGroupDD263621', + 'GroupId' + ] + }, + SourceSecurityGroupId: { + 'Fn::GetAtt': [ + 'SecretRotationSecurityGroup9985012B', + 'GroupId' + ] + }, + ToPort: 3306 + })); + + expect(stack).to(haveResource('AWS::SecretsManager::RotationSchedule', { + SecretId: { + Ref: 'SecretA720EF05' + }, + RotationLambdaARN: { + 'Fn::GetAtt': [ + "SecretRotationA9FFCFA9", + "Outputs.RotationLambdaARN" + ] + }, + RotationRules: { + AutomaticallyAfterDays: 30 + } + })); + + expect(stack).to(haveResource('AWS::EC2::SecurityGroup', { + GroupDescription: 'SecretRotation/SecurityGroup' + })); + + expect(stack).to(haveResource('AWS::Serverless::Application', { + Location: { + ApplicationId: 'arn:aws:serverlessrepo:us-east-1:297356227824:applications/SecretsManagerRDSMySQLRotationSingleUser', + SemanticVersion: '1.1.3' + }, + Parameters: { + endpoint: { + 'Fn::Join': [ + '', + [ + 'https://secretsmanager.', + { + Ref: 'AWS::Region' + }, + '.', + { + Ref: 'AWS::URLSuffix' + } + ] + ] + }, + functionName: 'SecretRotation', + vpcSecurityGroupIds: { + 'Fn::GetAtt': [ + 'SecretRotationSecurityGroup9985012B', + 'GroupId' + ] + }, + vpcSubnetIds: { + 'Fn::Join': [ + '', + [ + { + Ref: 'VPCPrivateSubnet1Subnet8BCA10E0' + }, + ',', + { + Ref: 'VPCPrivateSubnet2SubnetCFCDAA7A' + }, + ] + ] + } + } + })); + + expect(stack).to(haveResource('AWS::SecretsManager::ResourcePolicy', { + ResourcePolicy: { + Statement: [ + { + Action: 'secretsmanager:DeleteSecret', + Effect: 'Deny', + Principal: { + AWS: { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition' + }, + ':iam::', + { + Ref: 'AWS::AccountId' + }, + ':root' + ] + ] + } + }, + Resource: '*' + } + ], + Version: '2012-10-17' + }, + SecretId: { + Ref: 'SecretA720EF05' + } + })); + + test.done(); + }, + + 'secret rotation multi user'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + const secret = new secretsmanager.Secret(stack, 'Secret'); + const masterSecret = new secretsmanager.Secret(stack, 'MasterSecret'); + const target = new ec2.Connections({ + defaultPort: ec2.Port.tcp(3306), + securityGroups: [new ec2.SecurityGroup(stack, 'SecurityGroup', { vpc })] + }); + + // WHEN + new secretsmanager.SecretRotation(stack, 'SecretRotation', { + application: secretsmanager.SecretRotationApplication.MYSQL_ROTATION_MULTI_USER, + secret, + masterSecret, + target, + vpc, + }); + + // THEN + expect(stack).to(haveResource('AWS::Serverless::Application', { + Parameters: { + endpoint: { + 'Fn::Join': [ + '', + [ + 'https://secretsmanager.', + { + Ref: 'AWS::Region' + }, + '.', + { + Ref: 'AWS::URLSuffix' + } + ] + ] + }, + functionName: 'SecretRotation', + vpcSecurityGroupIds: { + 'Fn::GetAtt': [ + 'SecretRotationSecurityGroup9985012B', + 'GroupId' + ] + }, + vpcSubnetIds: { + 'Fn::Join': [ + '', + [ + { + Ref: 'VPCPrivateSubnet1Subnet8BCA10E0' + }, + ',', + { + Ref: 'VPCPrivateSubnet2SubnetCFCDAA7A' + }, + ] + ] + }, + masterSecretArn: { + Ref: 'MasterSecretA11BF785' + } + } + })); + + expect(stack).to(haveResource('AWS::SecretsManager::ResourcePolicy', { + ResourcePolicy: { + Statement: [ + { + Action: 'secretsmanager:DeleteSecret', + Effect: 'Deny', + Principal: { + AWS: { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition' + }, + ':iam::', + { + Ref: 'AWS::AccountId' + }, + ':root' + ] + ] + } + }, + Resource: '*' + } + ], + Version: '2012-10-17' + }, + SecretId: { + Ref: 'MasterSecretA11BF785' + } + })); + + test.done(); + }, + + 'throws when connections object has no default port range'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + const secret = new secretsmanager.Secret(stack, 'Secret'); + const securityGroup = new ec2.SecurityGroup(stack, 'SecurityGroup', { + vpc, + }); + + // WHEN + const target = new ec2.Connections({ + securityGroups: [securityGroup] + }); + + // THEN + test.throws(() => new secretsmanager.SecretRotation(stack, 'Rotation', { + secret, + application: secretsmanager.SecretRotationApplication.MYSQL_ROTATION_SINGLE_USER, + vpc, + target + }), /`target`.+default port range/); + + test.done(); + }, + + 'throws when master secret is missing for a multi user application'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + const secret = new secretsmanager.Secret(stack, 'Secret'); + const target = new ec2.Connections({ + defaultPort: ec2.Port.tcp(3306), + securityGroups: [new ec2.SecurityGroup(stack, 'SecurityGroup', { vpc })] + }); + + // THEN + test.throws(() => new secretsmanager.SecretRotation(stack, 'Rotation', { + secret, + application: secretsmanager.SecretRotationApplication.MYSQL_ROTATION_MULTI_USER, + vpc, + target + }), /The `masterSecret` must be specified for application using the multi user scheme/); + + test.done(); + }, +}; diff --git a/packages/@aws-cdk/aws-secretsmanager/test/test.secret.ts b/packages/@aws-cdk/aws-secretsmanager/test/test.secret.ts index a40f74cc3663a..39b44c6fea44d 100644 --- a/packages/@aws-cdk/aws-secretsmanager/test/test.secret.ts +++ b/packages/@aws-cdk/aws-secretsmanager/test/test.secret.ts @@ -309,43 +309,59 @@ export = { test.done(); }, - 'attached secret'(test: Test) { + 'can attach a secret with attach()'(test: Test) { // GIVEN const stack = new cdk.Stack(); const secret = new secretsmanager.Secret(stack, 'Secret'); - const target: secretsmanager.ISecretAttachmentTarget = { - asSecretAttachmentTarget: () => ({ - targetId: 'instance', - targetType: secretsmanager.AttachmentTargetType.INSTANCE - }) - }; // WHEN - secret.addTargetAttachment('AttachedSecret', { target }); + secret.attach({ + asSecretAttachmentTarget: () => ({ + targetId: 'target-id', + targetType: 'target-type' as secretsmanager.AttachmentTargetType + }) + }); // THEN expect(stack).to(haveResource('AWS::SecretsManager::SecretTargetAttachment', { SecretId: { Ref: 'SecretA720EF05' }, - TargetId: 'instance', - TargetType: 'AWS::RDS::DBInstance' + TargetId: 'target-id', + TargetType: 'target-type' })); test.done(); }, - 'add a rotation schedule to an attached secret'(test: Test) { + 'throws when trying to attach a target multiple times to a secret'(test: Test) { // GIVEN const stack = new cdk.Stack(); const secret = new secretsmanager.Secret(stack, 'Secret'); - const target: secretsmanager.ISecretAttachmentTarget = { + const target = { asSecretAttachmentTarget: () => ({ - targetId: 'cluster', - targetType: secretsmanager.AttachmentTargetType.CLUSTER + targetId: 'target-id', + targetType: 'target-type' as secretsmanager.AttachmentTargetType }) }; - const attachedSecret = secret.addTargetAttachment('AttachedSecret', { target }); + secret.attach(target); + + // THEN + test.throws(() => secret.attach(target), /Secret is already attached to a target/); + + test.done(); + }, + + 'add a rotation schedule to an attached secret'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const secret = new secretsmanager.Secret(stack, 'Secret'); + const attachedSecret = secret.attach({ + asSecretAttachmentTarget: () => ({ + targetId: 'target-id', + targetType: 'target-type' as secretsmanager.AttachmentTargetType + }) + }); const rotationLambda = new lambda.Function(stack, 'Lambda', { runtime: lambda.Runtime.NODEJS_10_X, code: lambda.Code.fromInline('export.handler = event => event;'), @@ -360,7 +376,7 @@ export = { // THEN expect(stack).to(haveResource('AWS::SecretsManager::RotationSchedule', { SecretId: { - Ref: 'SecretAttachedSecret94145316' // The secret returned by the attachment, not the secret itself. + Ref: 'SecretAttachment2E1B7C3B' // The secret returned by the attachment, not the secret itself. } })); @@ -405,6 +421,41 @@ export = { // THEN test.deepEqual(stack.resolve(imported), stack.resolve(value)); + test.done(); + }, + + 'can add to the resource policy of a secret'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const secret = new secretsmanager.Secret(stack, 'Secret'); + + // WHEN + secret.addToResourcePolicy(new iam.PolicyStatement({ + actions: ['secretsmanager:GetSecretValue'], + resources: ['*'], + principals: [new iam.ArnPrincipal('arn:aws:iam::123456789012:user/cool-user')] + })); + + // THEN + expect(stack).to(haveResource('AWS::SecretsManager::ResourcePolicy', { + ResourcePolicy: { + Statement: [ + { + Action: 'secretsmanager:GetSecretValue', + Effect: 'Allow', + Principal: { + AWS: 'arn:aws:iam::123456789012:user/cool-user' + }, + Resource: '*' + } + ], + Version: '2012-10-17' + }, + SecretId: { + Ref: 'SecretA720EF05' + } + })); + test.done(); } }; From 5a4874b1c4dd975e86013f70d45218c019d03377 Mon Sep 17 00:00:00 2001 From: Jerry Kindall <52084730+Jerry-AWS@users.noreply.github.com> Date: Fri, 20 Dec 2019 03:44:54 -0800 Subject: [PATCH 09/80] docs(alb): ALB requires region to use logging (#5085) * Add comment about requiring region to use logging * Update application-load-balancer.ts Co-authored-by: Rico Huijbers --- .../lib/alb/application-load-balancer.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-load-balancer.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-load-balancer.ts index f161a6bd816ca..33d8cf8557895 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-load-balancer.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-load-balancer.ts @@ -79,7 +79,10 @@ export class ApplicationLoadBalancer extends BaseLoadBalancer implements IApplic } /** - * Enable access logging for this load balancer + * Enable access logging for this load balancer. + * + * A region must be specified on the stack containing the load balancer; you cannot enable logging on + * environment-agnostic stacks. See https://docs.aws.amazon.com/cdk/latest/guide/environments.html */ public logAccessLogs(bucket: s3.IBucket, prefix?: string) { this.setAttribute('access_logs.s3.enabled', 'true'); From f10b3e64feab9c9ccc78c9820dc99a8b6b0971ec Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Sat, 21 Dec 2019 02:51:09 +0100 Subject: [PATCH 10/80] fix: increase IAM wait timeout in integ test (#5504) Survive occasional latency spikes that exceed 1 minute. --- .../aws-cdk/test/integ/cli/test-cdk-deploy-with-role.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/aws-cdk/test/integ/cli/test-cdk-deploy-with-role.sh b/packages/aws-cdk/test/integ/cli/test-cdk-deploy-with-role.sh index 5e068f4eb4d81..892a4239aaf1a 100755 --- a/packages/aws-cdk/test/integ/cli/test-cdk-deploy-with-role.sh +++ b/packages/aws-cdk/test/integ/cli/test-cdk-deploy-with-role.sh @@ -42,15 +42,17 @@ aws iam put-role-policy \ }] }') -for i in $(seq 1 10); do +# 5 minutes +attempts=60 +for i in $(seq 1 $attempts); do if aws sts assume-role --role-arn ${role_arn} --role-session-name testing >/dev/null; then echo "Successfully assumed newly created role" break; - elif [ $i -lt 10 ]; then + elif [ $i -lt $attempts ]; then echo "Sleeping 5 seconds to improve chances of the role having propagated" sleep 5 else - echo "Failed to assume role after 10 attempts, exiting." + echo "Failed to assume role after $attempts attempts, exiting." exit 1 fi done From bc34d0a96b3380a28e80d110ff7ff4c6a8f5f1fa Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Mon, 23 Dec 2019 15:40:36 +0200 Subject: [PATCH 11/80] chore(build): resolve eslint plugins relative to cdk-build-tools (#5528) plugins are installed centrally under cdk-build-tools and therefore resolution should happen against that module instead of the current module. otherwise, we get an error `ESLint couldn't find the plugin "eslint-plugin-node".` Furthermore, we also ignore any local .eslintrc files --- tools/cdk-build-tools/lib/compile.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/cdk-build-tools/lib/compile.ts b/tools/cdk-build-tools/lib/compile.ts index 0fa9bc9cd4edc..6b73301eb65cb 100644 --- a/tools/cdk-build-tools/lib/compile.ts +++ b/tools/cdk-build-tools/lib/compile.ts @@ -22,6 +22,8 @@ export async function compileCurrentPackage(timers: Timers, options: CDKBuildOpt '.', '--ext=.js,.ts', '--ignore-path=.gitignore', + '--no-eslintrc', // ignore local .eslintrc files + `--resolve-plugins-relative-to=${__dirname}`, ...options.eslint?.["ignore-pattern"]?.map(pattern => `--ignore-pattern=${pattern}`) || [] ], { timers }); await shell([compilers.tslint || require.resolve('tslint/bin/tslint'), '--project', '.'], { timers }); From acb396548f282de7fc8759b10109e1fb8c577574 Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Mon, 23 Dec 2019 16:16:11 +0200 Subject: [PATCH 12/80] chore: fix lambda destination api break error (#5529) since lambda destinations was a new module, it was published to npm before the actual release of the aws-lambda module. this causes the breaking change checker to fail because it could not find the required type in lambda --- allowed-breaking-changes.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/allowed-breaking-changes.txt b/allowed-breaking-changes.txt index 402968212c43d..794a8ed3e5b98 100644 --- a/allowed-breaking-changes.txt +++ b/allowed-breaking-changes.txt @@ -27,3 +27,8 @@ incompatible-argument:@aws-cdk/aws-servicediscovery.Service.fromServiceAttribute removed:@aws-cdk/core.ConstructNode.addReference removed:@aws-cdk/core.ConstructNode.references removed:@aws-cdk/core.OutgoingReference +change-return-type:@aws-cdk/aws-lambda-destinations.EventBridgeDestination.bind +change-return-type:@aws-cdk/aws-lambda-destinations.LambdaDestination.bind +change-return-type:@aws-cdk/aws-lambda-destinations.SnsDestination.bind +change-return-type:@aws-cdk/aws-lambda-destinations.SqsDestination.bind + From 52fd497cd0c2f06b9094a9e4c05ee05bdec43dd1 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2019 14:47:00 +0000 Subject: [PATCH 13/80] chore(deps): bump typescript from 3.7.3 to 3.7.4 (#5513) --- package.json | 2 +- tools/awslint/package.json | 2 +- tools/cdk-build-tools/package.json | 2 +- yarn.lock | 228 +++-------------------------- 4 files changed, 26 insertions(+), 208 deletions(-) diff --git a/package.json b/package.json index 6705c99ead087..4180762ac99cf 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "jsii-diff": "^0.20.11", "jsii-pacmak": "^0.20.11", "lerna": "^3.18.4", - "typescript": "~3.7.3" + "typescript": "~3.7.4" }, "repository": { "type": "git", diff --git a/tools/awslint/package.json b/tools/awslint/package.json index 61a911a28558b..541f821fb9bb9 100644 --- a/tools/awslint/package.json +++ b/tools/awslint/package.json @@ -27,7 +27,7 @@ "@types/fs-extra": "^8.0.1", "@types/yargs": "^13.0.3", "tslint": "^5.20.1", - "typescript": "~3.7.3" + "typescript": "~3.7.4" }, "repository": { "type": "git", diff --git a/tools/cdk-build-tools/package.json b/tools/cdk-build-tools/package.json index cd33418c1dfa4..f938ca08778c9 100644 --- a/tools/cdk-build-tools/package.json +++ b/tools/cdk-build-tools/package.json @@ -53,7 +53,7 @@ "nyc": "^14.1.1", "ts-jest": "^24.1.0", "tslint": "^5.20.1", - "typescript": "~3.7.3", + "typescript": "~3.7.4", "yargs": "^15.0.1" }, "keywords": [ diff --git a/yarn.lock b/yarn.lock index 0b3c3efc0291e..065da79e0e276 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1498,7 +1498,7 @@ add-stream@^1.0.0: resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" integrity sha1-anmQQ3ynNtXhKI25K9MmbV9csqo= -agent-base@4, agent-base@^4.2.0, agent-base@^4.3.0: +agent-base@4, agent-base@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== @@ -1765,11 +1765,6 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= -ast-types@0.x.x: - version "0.13.2" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.2.tgz#df39b677a911a83f3a049644fb74fdded23cea48" - integrity sha512-uWMHxJxtfj/1oZClOxDEV1sQ1HCDkA4MG8Gr69KKeBjEVH0R84WlejZ0y2DcwyBlpAEMltmVYkVgqfLFb2oyiA== - astral-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" @@ -2038,11 +2033,6 @@ byte-size@^5.0.1: resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-5.0.1.tgz#4b651039a5ecd96767e71a3d7ed380e48bed4191" integrity sha512-/XuKeqWocKsYa/cBY1YbSJSWWqTi4cFgr9S6OyM7PBaPbr9zvNGwWP33vt0uqGhwDdN+y3yhbXVILEUpnwEWGw== -bytes@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" - integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== - cacache@^12.0.0, cacache@^12.0.3: version "12.0.3" resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.3.tgz#be99abba4e1bf5df461cd5a2c1071fc432573390" @@ -2805,11 +2795,6 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -data-uri-to-buffer@1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz#77163ea9c20d8641b4707e8f18abdf9a78f34835" - integrity sha512-vKQ9DTQPN1FLYiiEEOQ6IBGFqvjCa5rSK3cWMy/Nespm5d/x3dGFT9UBZnkLxCwua/IXBi2TYnwTEpsOvhC4UQ== - data-urls@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe" @@ -2834,13 +2819,6 @@ dateformat@^3.0.0: resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== -debug@2, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - debug@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" @@ -2848,12 +2826,12 @@ debug@3.1.0: dependencies: ms "2.0.0" -debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" - integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== +debug@^2.1.3, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: - ms "^2.1.1" + ms "2.0.0" debug@^3.1.0, debug@^3.2.6: version "3.2.6" @@ -2862,6 +2840,13 @@ debug@^3.1.0, debug@^3.2.6: dependencies: ms "^2.1.1" +debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" @@ -2969,15 +2954,6 @@ define-property@^2.0.2: is-descriptor "^1.0.2" isobject "^3.0.1" -degenerator@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-1.0.4.tgz#fcf490a37ece266464d9cc431ab98c5819ced095" - integrity sha1-/PSQo37OJmRk2cxDGrmMWBnO0JU= - dependencies: - ast-types "0.x.x" - escodegen "1.x.x" - esprima "3.x.x" - delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -2988,11 +2964,6 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= - deprecation@^2.0.0: version "2.3.1" resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" @@ -3251,7 +3222,7 @@ escape-string-regexp@^2.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== -escodegen@1.x.x, escodegen@^1.9.1: +escodegen@^1.9.1: version "1.12.0" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.12.0.tgz#f763daf840af172bb3a2b6dd7219c0e17f7ff541" integrity sha512-TuA+EhsanGcme5T3R0L80u4t8CpbXQjegRmf7+FPTJrtCTErXFeelblRgHQa1FofEzqYYJmJ/OqjTwREp9qgmg== @@ -3385,7 +3356,7 @@ espree@^6.1.2: acorn-jsx "^5.1.0" eslint-visitor-keys "^1.1.0" -esprima@3.x.x, esprima@^3.1.3: +esprima@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= @@ -3620,11 +3591,6 @@ file-entry-cache@^5.0.1: dependencies: flat-cache "^2.0.1" -file-uri-to-path@1: - version "1.0.0" - resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" - integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== - fill-keys@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/fill-keys/-/fill-keys-1.0.2.tgz#9a8fa36f4e8ad634e3bf6b4f3c8882551452eb20" @@ -3804,14 +3770,6 @@ fsevents@^1.2.7: nan "^2.12.1" node-pre-gyp "^0.12.0" -ftp@~0.3.10: - version "0.3.10" - resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" - integrity sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0= - dependencies: - readable-stream "1.1.x" - xregexp "2.0.0" - function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -3884,18 +3842,6 @@ get-stream@^4.0.0, get-stream@^4.1.0: dependencies: pump "^3.0.0" -get-uri@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-2.0.4.tgz#d4937ab819e218d4cb5ae18e4f5962bef169cc6a" - integrity sha512-v7LT/s8kVjs+Tx0ykk1I+H/rbpzkHvuIq87LmeXptcf5sNWm9uQiwjNAt94SJPA1zOlCntmnOlJvVWKmzsxG8Q== - dependencies: - data-uri-to-buffer "1" - debug "2" - extend "~3.0.2" - file-uri-to-path "1" - ftp "~0.3.10" - readable-stream "2" - get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -4176,17 +4122,6 @@ http-cache-semantics@^3.8.1: resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== -http-errors@1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" - integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== - dependencies: - depd "~1.1.2" - inherits "2.0.4" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - http-proxy-agent@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" @@ -4212,14 +4147,6 @@ https-proxy-agent@^2.2.3: agent-base "^4.3.0" debug "^3.1.0" -https-proxy-agent@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-3.0.1.tgz#b8c286433e87602311b01c8ea34413d856a4af81" - integrity sha512-+ML2Rbh6DAuee7d07tYGEKOEi2voWPUGan+ExdPbPW6Z3svq+JCqr0v8WmKPOkz1vOVykPCBSuobe7G8GJUtVg== - dependencies: - agent-base "^4.3.0" - debug "^3.1.0" - humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" @@ -4315,7 +4242,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -4384,7 +4311,7 @@ invariant@^2.2.4: dependencies: loose-envify "^1.0.0" -ip@1.1.5, ip@^1.1.5: +ip@1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= @@ -5992,11 +5919,6 @@ nested-error-stacks@^2.0.0: resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz#0fbdcf3e13fe4994781280524f8b96b0cdff9c61" integrity sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug== -netmask@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35" - integrity sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU= - nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -6510,31 +6432,6 @@ p-waterfall@^1.0.0: dependencies: p-reduce "^1.0.0" -pac-proxy-agent@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-3.0.1.tgz#115b1e58f92576cac2eba718593ca7b0e37de2ad" - integrity sha512-44DUg21G/liUZ48dJpUSjZnFfZro/0K5JTyFYLBcmh9+T6Ooi4/i4efwUiEy0+4oQusCBqWdhv16XohIj1GqnQ== - dependencies: - agent-base "^4.2.0" - debug "^4.1.1" - get-uri "^2.0.0" - http-proxy-agent "^2.1.0" - https-proxy-agent "^3.0.0" - pac-resolver "^3.0.0" - raw-body "^2.2.0" - socks-proxy-agent "^4.0.1" - -pac-resolver@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-3.0.0.tgz#6aea30787db0a891704deb7800a722a7615a6f26" - integrity sha512-tcc38bsjuE3XZ5+4vP96OfhOugrX+JcnpUbhfuc4LuXBLQhoTthOstZeoQJBDnQUDYzYmdImKsbz0xSl1/9qeA== - dependencies: - co "^4.6.0" - degenerator "^1.0.4" - ip "^1.1.5" - netmask "^1.0.6" - thunkify "^2.1.2" - package-hash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/package-hash/-/package-hash-3.0.0.tgz#50183f2d36c9e3e528ea0a8605dff57ce976f88e" @@ -6832,25 +6729,6 @@ protoduck@^5.0.1: dependencies: genfun "^5.0.0" -proxy-agent@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-3.1.1.tgz#7e04e06bf36afa624a1540be247b47c970bd3014" - integrity sha512-WudaR0eTsDx33O3EJE16PjBRZWcX8GqCEeERw1W3hZJgH/F2a46g7jty6UGty6NeJ4CKQy8ds2CJPMiyeqaTvw== - dependencies: - agent-base "^4.2.0" - debug "4" - http-proxy-agent "^2.1.0" - https-proxy-agent "^3.0.0" - lru-cache "^5.1.1" - pac-proxy-agent "^3.0.1" - proxy-from-env "^1.0.0" - socks-proxy-agent "^4.0.1" - -proxy-from-env@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" - integrity sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4= - proxyquire@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/proxyquire/-/proxyquire-2.1.3.tgz#2049a7eefa10a9a953346a18e54aab2b4268df39" @@ -6935,16 +6813,6 @@ quick-lru@^1.0.0: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" integrity sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g= -raw-body@^2.2.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" - integrity sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA== - dependencies: - bytes "3.1.0" - http-errors "1.7.3" - iconv-lite "0.4.24" - unpipe "1.0.0" - rc@^1.2.7: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" @@ -7054,7 +6922,7 @@ read@1, read@^1.0.4, read@~1.0.1: dependencies: mute-stream "~0.0.4" -"readable-stream@1 || 2", readable-stream@2, readable-stream@^2, readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.6, readable-stream@~2.3.6: +"readable-stream@1 || 2", readable-stream@^2, readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== @@ -7067,16 +6935,6 @@ read@1, read@^1.0.4, read@~1.0.1: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@1.1.x: - version "1.1.14" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" - integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - "readable-stream@2 || 3", readable-stream@^3.0.1, readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc" @@ -7429,11 +7287,6 @@ set-value@^2.0.0, set-value@^2.0.1: is-plain-object "^2.0.3" split-string "^3.0.1" -setprototypeof@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" - integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== - shallow-clone@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" @@ -7540,7 +7393,7 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" -socks-proxy-agent@^4.0.0, socks-proxy-agent@^4.0.1: +socks-proxy-agent@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz#3c8991f3145b2799e70e11bd5fbc8b1963116386" integrity sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg== @@ -7710,11 +7563,6 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" -"statuses@>= 1.5.0 < 2": - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= - stealthy-require@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" @@ -7813,11 +7661,6 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= - string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -8125,11 +7968,6 @@ through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= -thunkify@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/thunkify/-/thunkify-2.1.2.tgz#faa0e9d230c51acc95ca13a361ac05ca7e04553d" - integrity sha1-+qDp0jDFGsyVyhOjYawFyn4EVT0= - tiny-glob@^0.2.6: version "0.2.6" resolved "https://registry.yarnpkg.com/tiny-glob/-/tiny-glob-0.2.6.tgz#9e056e169d9788fe8a734dfa1ff02e9b92ed7eda" @@ -8185,11 +8023,6 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" -toidentifier@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" - integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== - tough-cookie@^2.3.3, tough-cookie@^2.3.4: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" @@ -8362,15 +8195,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@^3.3.3: - version "3.7.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.2.tgz#27e489b95fa5909445e9fef5ee48d81697ad18fb" - integrity sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ== - -typescript@~3.7.3: - version "3.7.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.3.tgz#b36840668a16458a7025b9eabfad11b66ab85c69" - integrity sha512-Mcr/Qk7hXqFBXMN7p7Lusj1ktCBydylfQM/FZCk5glCNQJrCUKPkMHdo9R0MTFWsC/4kPFvDS0fDPvukfCkFsw== +typescript@^3.3.3, typescript@~3.7.3, typescript@~3.7.4: + version "3.7.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.4.tgz#1743a5ec5fef6a1fa9f3e4708e33c81c73876c19" + integrity sha512-A25xv5XCtarLwXpcDNZzCGvW2D1S3/bACratYBx2sax8PefsFhlYmkQicKHvpYflFS8if4zne5zT5kpJ7pzuvw== uglify-js@^3.1.4: version "3.6.9" @@ -8434,11 +8262,6 @@ universalify@^0.1.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== -unpipe@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= - unset-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" @@ -8738,11 +8561,6 @@ xmlbuilder@~9.0.1: resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= -xregexp@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" - integrity sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM= - xregexp@^4.2.4: version "4.2.4" resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.2.4.tgz#02a4aea056d65a42632c02f0233eab8e4d7e57ed" From fcf3612aec7d6f7cc24ee22f0785d34f438726aa Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2019 15:15:03 +0000 Subject: [PATCH 14/80] chore(deps): bump eslint from 6.7.2 to 6.8.0 (#5510) Bumps [eslint](https://github.com/eslint/eslint) from 6.7.2 to 6.8.0. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v6.7.2...v6.8.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- tools/cdk-build-tools/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/cdk-build-tools/package.json b/tools/cdk-build-tools/package.json index f938ca08778c9..98fcc2e3e56ca 100644 --- a/tools/cdk-build-tools/package.json +++ b/tools/cdk-build-tools/package.json @@ -41,7 +41,7 @@ "@typescript-eslint/parser": "^2.12.0", "awslint": "1.19.0", "colors": "^1.4.0", - "eslint": "^6.7.2", + "eslint": "^6.8.0", "eslint-import-resolver-node": "^0.3.2", "eslint-import-resolver-typescript": "^2.0.0", "eslint-plugin-import": "^2.19.1", diff --git a/yarn.lock b/yarn.lock index 065da79e0e276..53f54bd12ca3e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3299,10 +3299,10 @@ eslint-visitor-keys@^1.1.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== -eslint@^6.7.2: - version "6.7.2" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.7.2.tgz#c17707ca4ad7b2d8af986a33feba71e18a9fecd1" - integrity sha512-qMlSWJaCSxDFr8fBPvJM9kJwbazrhNcBU3+DszDW1OlEwKBBRWsJc7NJFelvwQpanHCR14cOLD41x8Eqvo3Nng== +eslint@^6.8.0: + version "6.8.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" + integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig== dependencies: "@babel/code-frame" "^7.0.0" ajv "^6.10.0" From c388ad6a8192ccddac6248c79b79444bb7b573da Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2019 15:43:20 +0000 Subject: [PATCH 15/80] chore(deps): bump aws-sdk from 2.590.0 to 2.595.0 (#5508) Bumps [aws-sdk](https://github.com/aws/aws-sdk-js) from 2.590.0 to 2.595.0. - [Release notes](https://github.com/aws/aws-sdk-js/releases) - [Changelog](https://github.com/aws/aws-sdk-js/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js/compare/v2.590.0...v2.595.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- packages/@aws-cdk/aws-cloudfront/package.json | 2 +- packages/@aws-cdk/aws-cloudtrail/package.json | 2 +- packages/@aws-cdk/aws-codebuild/package.json | 2 +- packages/@aws-cdk/aws-codecommit/package.json | 2 +- packages/@aws-cdk/aws-events-targets/package.json | 2 +- packages/@aws-cdk/aws-lambda/package.json | 2 +- packages/@aws-cdk/aws-route53/package.json | 2 +- packages/@aws-cdk/aws-sqs/package.json | 2 +- packages/@aws-cdk/custom-resources/package.json | 2 +- packages/aws-cdk/package.json | 2 +- yarn.lock | 8 ++++---- 11 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/@aws-cdk/aws-cloudfront/package.json b/packages/@aws-cdk/aws-cloudfront/package.json index d2691cd73e568..8e71520f60141 100644 --- a/packages/@aws-cdk/aws-cloudfront/package.json +++ b/packages/@aws-cdk/aws-cloudfront/package.json @@ -64,7 +64,7 @@ "devDependencies": { "@aws-cdk/assert": "1.19.0", "@types/nodeunit": "^0.0.30", - "aws-sdk": "^2.590.0", + "aws-sdk": "^2.595.0", "cdk-build-tools": "1.19.0", "cdk-integ-tools": "1.19.0", "cfn2ts": "1.19.0", diff --git a/packages/@aws-cdk/aws-cloudtrail/package.json b/packages/@aws-cdk/aws-cloudtrail/package.json index f9a811b18bff2..bfc5aaf42183f 100644 --- a/packages/@aws-cdk/aws-cloudtrail/package.json +++ b/packages/@aws-cdk/aws-cloudtrail/package.json @@ -64,7 +64,7 @@ "devDependencies": { "@aws-cdk/assert": "1.19.0", "@types/nodeunit": "^0.0.30", - "aws-sdk": "^2.590.0", + "aws-sdk": "^2.595.0", "cdk-build-tools": "1.19.0", "cdk-integ-tools": "1.19.0", "cfn2ts": "1.19.0", diff --git a/packages/@aws-cdk/aws-codebuild/package.json b/packages/@aws-cdk/aws-codebuild/package.json index fa8060a173f68..2fc6027da6666 100644 --- a/packages/@aws-cdk/aws-codebuild/package.json +++ b/packages/@aws-cdk/aws-codebuild/package.json @@ -70,7 +70,7 @@ "@aws-cdk/aws-sns": "1.19.0", "@aws-cdk/aws-sqs": "1.19.0", "@types/nodeunit": "^0.0.30", - "aws-sdk": "^2.590.0", + "aws-sdk": "^2.595.0", "cdk-build-tools": "1.19.0", "cdk-integ-tools": "1.19.0", "cfn2ts": "1.19.0", diff --git a/packages/@aws-cdk/aws-codecommit/package.json b/packages/@aws-cdk/aws-codecommit/package.json index 54ce83c676a0d..ae630c40e9186 100644 --- a/packages/@aws-cdk/aws-codecommit/package.json +++ b/packages/@aws-cdk/aws-codecommit/package.json @@ -70,7 +70,7 @@ "@aws-cdk/assert": "1.19.0", "@aws-cdk/aws-sns": "1.19.0", "@types/nodeunit": "^0.0.30", - "aws-sdk": "^2.590.0", + "aws-sdk": "^2.595.0", "cdk-build-tools": "1.19.0", "cdk-integ-tools": "1.19.0", "cfn2ts": "1.19.0", diff --git a/packages/@aws-cdk/aws-events-targets/package.json b/packages/@aws-cdk/aws-events-targets/package.json index 59db51944c9c0..b9dfd6e42b509 100644 --- a/packages/@aws-cdk/aws-events-targets/package.json +++ b/packages/@aws-cdk/aws-events-targets/package.json @@ -80,7 +80,7 @@ "devDependencies": { "@aws-cdk/assert": "1.19.0", "@aws-cdk/aws-codecommit": "1.19.0", - "aws-sdk": "^2.590.0", + "aws-sdk": "^2.595.0", "aws-sdk-mock": "^4.5.0", "cdk-build-tools": "1.19.0", "cdk-integ-tools": "1.19.0", diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json index 8f7c8d6507a53..6db50a8984b0a 100644 --- a/packages/@aws-cdk/aws-lambda/package.json +++ b/packages/@aws-cdk/aws-lambda/package.json @@ -71,7 +71,7 @@ "@types/lodash": "^4.14.149", "@types/nodeunit": "^0.0.30", "@types/sinon": "^7.5.0", - "aws-sdk": "^2.590.0", + "aws-sdk": "^2.595.0", "aws-sdk-mock": "^4.5.0", "cdk-build-tools": "1.19.0", "cdk-integ-tools": "1.19.0", diff --git a/packages/@aws-cdk/aws-route53/package.json b/packages/@aws-cdk/aws-route53/package.json index afdc3ff8421e6..754f2dd2fa794 100644 --- a/packages/@aws-cdk/aws-route53/package.json +++ b/packages/@aws-cdk/aws-route53/package.json @@ -64,7 +64,7 @@ "devDependencies": { "@aws-cdk/assert": "1.19.0", "@types/nodeunit": "^0.0.30", - "aws-sdk": "^2.590.0", + "aws-sdk": "^2.595.0", "cdk-build-tools": "1.19.0", "cdk-integ-tools": "1.19.0", "cfn2ts": "1.19.0", diff --git a/packages/@aws-cdk/aws-sqs/package.json b/packages/@aws-cdk/aws-sqs/package.json index d135f73cd12b0..9dbe3d3cc5340 100644 --- a/packages/@aws-cdk/aws-sqs/package.json +++ b/packages/@aws-cdk/aws-sqs/package.json @@ -65,7 +65,7 @@ "@aws-cdk/assert": "1.19.0", "@aws-cdk/aws-s3": "1.19.0", "@types/nodeunit": "^0.0.30", - "aws-sdk": "^2.590.0", + "aws-sdk": "^2.595.0", "cdk-build-tools": "1.19.0", "cdk-integ-tools": "1.19.0", "cfn2ts": "1.19.0", diff --git a/packages/@aws-cdk/custom-resources/package.json b/packages/@aws-cdk/custom-resources/package.json index 087cfed1a97af..764ca8eb0994c 100644 --- a/packages/@aws-cdk/custom-resources/package.json +++ b/packages/@aws-cdk/custom-resources/package.json @@ -73,7 +73,7 @@ "@types/aws-lambda": "^8.10.39", "@types/fs-extra": "^8.0.1", "@types/sinon": "^7.5.0", - "aws-sdk": "^2.590.0", + "aws-sdk": "^2.595.0", "aws-sdk-mock": "^4.5.0", "cdk-build-tools": "1.19.0", "cdk-integ-tools": "1.19.0", diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index de81ee942db80..531491a8bdf6f 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -73,7 +73,7 @@ "@aws-cdk/cx-api": "1.19.0", "@aws-cdk/region-info": "1.19.0", "archiver": "^3.1.1", - "aws-sdk": "^2.590.0", + "aws-sdk": "^2.595.0", "camelcase": "^5.3.1", "colors": "^1.4.0", "decamelize": "^3.2.0", diff --git a/yarn.lock b/yarn.lock index 53f54bd12ca3e..68564994e28b0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1806,10 +1806,10 @@ aws-sdk-mock@^4.5.0: sinon "^7.3.2" traverse "^0.6.6" -aws-sdk@^2.483.0, aws-sdk@^2.590.0: - version "2.590.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.590.0.tgz#1c2496a3f1e7ea57ada696989bee60b04f0453d7" - integrity sha512-cdH3B/IuEuds84zRvi52WEW0UXOjsyl9hEkdbS2Uo8P7pWepaKTOZ3BKIUsy/lXen0nHcRWDCHeUkBwNW1Q2bg== +aws-sdk@^2.483.0, aws-sdk@^2.595.0: + version "2.595.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.595.0.tgz#eb9f198716547d34a615e4bc32ce21056ee40959" + integrity sha512-bE/XzwlvEv3YPGfU7EfvAOi1IaEzmM+9VWP6xD9xN1lLhdBgCIiQIvSnr52LDR4J7ohqVP+oYpuBZcXrqZaP2Q== dependencies: buffer "4.9.1" events "1.1.1" From 0adf6c75c1f0aa4acc131915970a496326dc559f Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Mon, 23 Dec 2019 17:26:43 +0100 Subject: [PATCH 16/80] fix(autoscaling): every deployment resets capacity (#5507) * fix(autoscaling): every deployment resets capacity If `DesiredCapacity` is specified in the CloudFormation template, on every deployment the capacity of the AutoScalingGroup is reset to that number, even if the group had been scaled out at that point. The solution is to leave DesiredCapacity empty, in which case it will remain untouched during a deployment. Previously, CDK would use some logic to always calculate a DesiredCapacity for you, even if you left the `desiredCapacity` property unset, leading to the undesirable behavior--which frankly represents an availability risk. Now, if you don't specify `desiredCapacity`, we won't set `DesiredCapacity` either, avoiding the availability risk that we introduced beforehand. In fact, if you *do* set `desiredCapacity`, we will warn you that you probably shouldn't using a construct warning. Fixes #5215, closes #5208. BREAKING CHANGE: AutoScalingGroups without `desiredCapacity` are now initially scaled to their minimum capacity (instead of their maximum capaciety). * Add links Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .../aws-autoscaling/lib/auto-scaling-group.ts | 28 +++-- .../test/integ.amazonlinux2.expected.json | 1 - ...g.asg-w-classic-loadbalancer.expected.json | 1 - .../test/integ.asg-w-elbv2.expected.json | 1 - .../test/integ.custom-scaling.expected.json | 1 - .../test/integ.external-role.expected.json | 1 - .../test/integ.spot-instances.expected.json | 1 - .../test/test.auto-scaling-group.ts | 5 +- .../test/test.scheduled-action.ts | 1 - .../integ.deployment-group.expected.json | 3 +- ...on-load-balanced-ecs-service.expected.json | 3 +- ...integ.scheduled-ecs-task.lit.expected.json | 3 +- .../integ.app-mesh-proxy-config.expected.json | 3 +- .../test/ec2/integ.clb-host-nw.expected.json | 3 +- .../test/ec2/integ.lb-awsvpc-nw.expected.json | 3 +- .../test/ec2/integ.lb-bridge-nw.expected.json | 3 +- .../test/ec2/integ.sd-awsvpc-nw.expected.json | 3 +- .../test/ec2/integ.sd-bridge-nw.expected.json | 3 +- .../test/ec2/integ.spot-drain.expected.json | 2 +- .../@aws-cdk/aws-ecs/test/test.ecs-cluster.ts | 4 - .../aws-eks/test/integ.eks-spot.expected.json | 3 +- .../integ.event-ec2-task.lit.expected.json | 3 +- .../test/integ.ec2-task.expected.json | 1 - yarn.lock | 108 +++++++++++++++++- 24 files changed, 138 insertions(+), 50 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 5f56ef0a291e6..10649303ccb50 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts @@ -44,7 +44,11 @@ export interface CommonAutoScalingGroupProps { /** * Initial amount of instances in the fleet * - * @default 1 + * If this is set to a number, every deployment will reset the amount of + * instances to this number. It is recommended to leave this value blank. + * + * @default minCapacity, and leave unchanged during deployment + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-as-group.html#cfn-as-group-desiredcapacity */ readonly desiredCapacity?: number; @@ -475,30 +479,40 @@ export class AutoScalingGroup extends AutoScalingGroupBase implements launchConfig.node.addDependency(this.role); - const desiredCapacity = - (props.desiredCapacity !== undefined ? props.desiredCapacity : - (props.minCapacity !== undefined ? props.minCapacity : - (props.maxCapacity !== undefined ? props.maxCapacity : 1))); + // desiredCapacity just reflects what the user has supplied. + const desiredCapacity = props.desiredCapacity; const minCapacity = props.minCapacity !== undefined ? props.minCapacity : 1; - const maxCapacity = props.maxCapacity !== undefined ? props.maxCapacity : desiredCapacity; + const maxCapacity = props.maxCapacity !== undefined ? props.maxCapacity : + desiredCapacity !== undefined ? desiredCapacity : Math.max(minCapacity, 1); + withResolved(minCapacity, maxCapacity, (min, max) => { + if (min > max) { + throw new Error(`minCapacity (${min}) should be <= maxCapacity (${max})`); + } + }); withResolved(desiredCapacity, minCapacity, (desired, min) => { + if (desired === undefined) { return; } if (desired < min) { throw new Error(`Should have minCapacity (${min}) <= desiredCapacity (${desired})`); } }); withResolved(desiredCapacity, maxCapacity, (desired, max) => { + if (desired === undefined) { return; } if (max < desired) { throw new Error(`Should have desiredCapacity (${desired}) <= maxCapacity (${max})`); } }); + if (desiredCapacity !== undefined) { + this.node.addWarning(`desiredCapacity has been configured. Be aware this will reset the size of your AutoScalingGroup on every deployment. See https://github.com/aws/aws-cdk/issues/5215`); + } + const { subnetIds, hasPublic } = props.vpc.selectSubnets(props.vpcSubnets); const asgProps: CfnAutoScalingGroupProps = { cooldown: props.cooldown !== undefined ? props.cooldown.toSeconds().toString() : undefined, minSize: Tokenization.stringifyNumber(minCapacity), maxSize: Tokenization.stringifyNumber(maxCapacity), - desiredCapacity: Tokenization.stringifyNumber(desiredCapacity), + desiredCapacity: desiredCapacity !== undefined ? Tokenization.stringifyNumber(desiredCapacity) : undefined, launchConfigurationName: launchConfig.ref, loadBalancerNames: Lazy.listValue({ produce: () => this.loadBalancerNames }, { omitEmpty: true }), targetGroupArns: Lazy.listValue({ produce: () => this.targetGroupArns }, { omitEmpty: true }), diff --git a/packages/@aws-cdk/aws-autoscaling/test/integ.amazonlinux2.expected.json b/packages/@aws-cdk/aws-autoscaling/test/integ.amazonlinux2.expected.json index 4db28ea65b2c3..39c5bc0ffebd6 100644 --- a/packages/@aws-cdk/aws-autoscaling/test/integ.amazonlinux2.expected.json +++ b/packages/@aws-cdk/aws-autoscaling/test/integ.amazonlinux2.expected.json @@ -451,7 +451,6 @@ "Properties": { "MaxSize": "1", "MinSize": "1", - "DesiredCapacity": "1", "LaunchConfigurationName": { "Ref": "FleetLaunchConfig59F79D36" }, 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 7588b85893296..7b9cd21bff4e7 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 @@ -631,7 +631,6 @@ "Properties": { "MaxSize": "1", "MinSize": "1", - "DesiredCapacity": "1", "LaunchConfigurationName": { "Ref": "FleetLaunchConfig59F79D36" }, 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 dcebbf42ea301..66f8ea0e2bdfe 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 @@ -472,7 +472,6 @@ "Properties": { "MaxSize": "1", "MinSize": "1", - "DesiredCapacity": "1", "LaunchConfigurationName": { "Ref": "FleetLaunchConfig59F79D36" }, diff --git a/packages/@aws-cdk/aws-autoscaling/test/integ.custom-scaling.expected.json b/packages/@aws-cdk/aws-autoscaling/test/integ.custom-scaling.expected.json index 75cc2daae6a52..ce82a1f7be835 100644 --- a/packages/@aws-cdk/aws-autoscaling/test/integ.custom-scaling.expected.json +++ b/packages/@aws-cdk/aws-autoscaling/test/integ.custom-scaling.expected.json @@ -451,7 +451,6 @@ "Properties": { "MaxSize": "1", "MinSize": "1", - "DesiredCapacity": "1", "LaunchConfigurationName": { "Ref": "FleetLaunchConfig59F79D36" }, diff --git a/packages/@aws-cdk/aws-autoscaling/test/integ.external-role.expected.json b/packages/@aws-cdk/aws-autoscaling/test/integ.external-role.expected.json index 23c4234bb30f5..fc4a1ed60045b 100644 --- a/packages/@aws-cdk/aws-autoscaling/test/integ.external-role.expected.json +++ b/packages/@aws-cdk/aws-autoscaling/test/integ.external-role.expected.json @@ -604,7 +604,6 @@ "Properties": { "MaxSize": "1", "MinSize": "1", - "DesiredCapacity": "1", "LaunchConfigurationName": { "Ref": "ASGLaunchConfigC00AF12B" }, diff --git a/packages/@aws-cdk/aws-autoscaling/test/integ.spot-instances.expected.json b/packages/@aws-cdk/aws-autoscaling/test/integ.spot-instances.expected.json index 9ef273dc8b119..5a38a514b146c 100644 --- a/packages/@aws-cdk/aws-autoscaling/test/integ.spot-instances.expected.json +++ b/packages/@aws-cdk/aws-autoscaling/test/integ.spot-instances.expected.json @@ -452,7 +452,6 @@ "Properties": { "MaxSize": "1", "MinSize": "1", - "DesiredCapacity": "1", "LaunchConfigurationName": { "Ref": "FleetLaunchConfig59F79D36" }, 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 31e97d967e858..2e9def25d478a 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 @@ -113,7 +113,6 @@ export = { } }, "Properties": { - "DesiredCapacity": "1", "LaunchConfigurationName": { "Ref": "MyFleetLaunchConfig5D7F9801" }, @@ -252,7 +251,6 @@ export = { expect(stack).to(haveResource("AWS::AutoScaling::AutoScalingGroup", { MinSize: "10", MaxSize: "10", - DesiredCapacity: "10", } )); @@ -275,8 +273,7 @@ export = { // THEN expect(stack).to(haveResource("AWS::AutoScaling::AutoScalingGroup", { MinSize: "1", - MaxSize: "10", - DesiredCapacity: "10", + MaxSize: "10" } )); 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 ba6e85ea0ffd0..bd42078a57560 100644 --- a/packages/@aws-cdk/aws-autoscaling/test/test.scheduled-action.ts +++ b/packages/@aws-cdk/aws-autoscaling/test/test.scheduled-action.ts @@ -64,7 +64,6 @@ export = { Properties: { MaxSize: "1", MinSize: "1", - DesiredCapacity: "1", LaunchConfigurationName: { Ref: "ASGLaunchConfigC00AF12B" }, Tags: [ { diff --git a/packages/@aws-cdk/aws-codedeploy/test/server/integ.deployment-group.expected.json b/packages/@aws-cdk/aws-codedeploy/test/server/integ.deployment-group.expected.json index 7ac5e5e279b5e..b5981044d5296 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/server/integ.deployment-group.expected.json +++ b/packages/@aws-cdk/aws-codedeploy/test/server/integ.deployment-group.expected.json @@ -683,7 +683,6 @@ "Properties": { "MaxSize": "1", "MinSize": "1", - "DesiredCapacity": "1", "LaunchConfigurationName": { "Ref": "ASGLaunchConfigC00AF12B" }, @@ -885,4 +884,4 @@ "Default": "/aws/service/ami-amazon-linux-latest/amzn-ami-hvm-x86_64-gp2" } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.multiple-application-load-balanced-ecs-service.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.multiple-application-load-balanced-ecs-service.expected.json index 3141e11ec5b49..38a36766c5532 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.multiple-application-load-balanced-ecs-service.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.multiple-application-load-balanced-ecs-service.expected.json @@ -519,7 +519,6 @@ "Properties": { "MaxSize": "1", "MinSize": "1", - "DesiredCapacity": "1", "LaunchConfigurationName": { "Ref": "ClusterDefaultAutoScalingGroupLaunchConfig81EA5466" }, @@ -1152,4 +1151,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.scheduled-ecs-task.lit.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.scheduled-ecs-task.lit.expected.json index 59b5b74b73952..8fed931e5dab3 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.scheduled-ecs-task.lit.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.scheduled-ecs-task.lit.expected.json @@ -339,7 +339,6 @@ "Properties": { "MaxSize": "1", "MinSize": "1", - "DesiredCapacity": "1", "LaunchConfigurationName": { "Ref": "EcsClusterDefaultAutoScalingGroupLaunchConfigB7E376C1" }, @@ -856,4 +855,4 @@ "Default": "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id" } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.app-mesh-proxy-config.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.app-mesh-proxy-config.expected.json index a43e384553864..c7a842ce03a5e 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.app-mesh-proxy-config.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.app-mesh-proxy-config.expected.json @@ -498,7 +498,6 @@ "Properties": { "MaxSize": "1", "MinSize": "1", - "DesiredCapacity": "1", "LaunchConfigurationName": { "Ref": "EcsClusterDefaultAutoScalingGroupLaunchConfigB7E376C1" }, @@ -939,4 +938,4 @@ "Default": "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id" } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.clb-host-nw.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.clb-host-nw.expected.json index d92d3ea2cb7bc..ff68024d095e1 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.clb-host-nw.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.clb-host-nw.expected.json @@ -519,7 +519,6 @@ "Properties": { "MaxSize": "1", "MinSize": "1", - "DesiredCapacity": "1", "LaunchConfigurationName": { "Ref": "EcsClusterDefaultAutoScalingGroupLaunchConfigB7E376C1" }, @@ -987,4 +986,4 @@ } } } -} \ No newline at end of file +} 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 a154d7821ab63..bcee0666ca066 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 @@ -498,7 +498,6 @@ "Properties": { "MaxSize": "1", "MinSize": "1", - "DesiredCapacity": "1", "LaunchConfigurationName": { "Ref": "EcsClusterDefaultAutoScalingGroupLaunchConfigB7E376C1" }, @@ -1053,4 +1052,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 802d283801f14..fdc2aad51c16a 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 @@ -519,7 +519,6 @@ "Properties": { "MaxSize": "1", "MinSize": "1", - "DesiredCapacity": "1", "LaunchConfigurationName": { "Ref": "EcsClusterDefaultAutoScalingGroupLaunchConfigB7E376C1" }, @@ -1017,4 +1016,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-awsvpc-nw.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-awsvpc-nw.expected.json index d1bd4d0a8bf29..5e4dd2710205a 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-awsvpc-nw.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-awsvpc-nw.expected.json @@ -498,7 +498,6 @@ "Properties": { "MaxSize": "1", "MinSize": "1", - "DesiredCapacity": "1", "LaunchConfigurationName": { "Ref": "EcsClusterDefaultAutoScalingGroupLaunchConfigB7E376C1" }, @@ -963,4 +962,4 @@ "Default": "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id" } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-bridge-nw.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-bridge-nw.expected.json index 416a5e613a269..a7477132828e1 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-bridge-nw.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-bridge-nw.expected.json @@ -498,7 +498,6 @@ "Properties": { "MaxSize": "1", "MinSize": "1", - "DesiredCapacity": "1", "LaunchConfigurationName": { "Ref": "EcsClusterDefaultAutoScalingGroupLaunchConfigB7E376C1" }, @@ -928,4 +927,4 @@ "Default": "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id" } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.spot-drain.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.spot-drain.expected.json index c6070c2783db6..e0dd1e5bb18f3 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.spot-drain.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.spot-drain.expected.json @@ -1323,4 +1323,4 @@ "Default": "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id" } } -} \ No newline at end of file +} 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 c935db093f0c6..09576362eb60d 100644 --- a/packages/@aws-cdk/aws-ecs/test/test.ecs-cluster.ts +++ b/packages/@aws-cdk/aws-ecs/test/test.ecs-cluster.ts @@ -67,7 +67,6 @@ export = { expect(stack).to(haveResource("AWS::AutoScaling::AutoScalingGroup", { MaxSize: "1", MinSize: "1", - DesiredCapacity: "1", LaunchConfigurationName: { Ref: "EcsClusterDefaultAutoScalingGroupLaunchConfigB7E376C1" }, @@ -213,7 +212,6 @@ export = { expect(stack).to(haveResource("AWS::AutoScaling::AutoScalingGroup", { MaxSize: "1", MinSize: "1", - DesiredCapacity: "1", LaunchConfigurationName: { Ref: "EcsClusterDefaultAutoScalingGroupLaunchConfigB7E376C1" }, @@ -514,7 +512,6 @@ export = { expect(stack).to(haveResource("AWS::AutoScaling::AutoScalingGroup", { MaxSize: "1", MinSize: "1", - DesiredCapacity: "1", LaunchConfigurationName: { Ref: "EcsClusterDefaultAutoScalingGroupLaunchConfigB7E376C1" }, @@ -1273,7 +1270,6 @@ export = { expect(stack).to(haveResource("AWS::AutoScaling::AutoScalingGroup", { MaxSize: "1", MinSize: "1", - DesiredCapacity: "1", LaunchConfigurationName: { Ref: "EcsClusterDefaultAutoScalingGroupLaunchConfigB7E376C1" }, diff --git a/packages/@aws-cdk/aws-eks/test/integ.eks-spot.expected.json b/packages/@aws-cdk/aws-eks/test/integ.eks-spot.expected.json index a993c2604f9f6..982e65e5d7d63 100644 --- a/packages/@aws-cdk/aws-eks/test/integ.eks-spot.expected.json +++ b/packages/@aws-cdk/aws-eks/test/integ.eks-spot.expected.json @@ -1310,7 +1310,6 @@ "Properties": { "MaxSize": "10", "MinSize": "1", - "DesiredCapacity": "10", "LaunchConfigurationName": { "Ref": "myClusterspotLaunchConfig6681F311" }, @@ -1449,4 +1448,4 @@ "Default": "/aws/service/eks/optimized-ami/1.14/amazon-linux-2/recommended/image_id" } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.expected.json b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.expected.json index d2c89fde89b4c..88cdef93cf952 100644 --- a/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.expected.json +++ b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.expected.json @@ -339,7 +339,6 @@ "Properties": { "MaxSize": "1", "MinSize": "1", - "DesiredCapacity": "1", "LaunchConfigurationName": { "Ref": "EcsClusterDefaultAutoScalingGroupLaunchConfigB7E376C1" }, @@ -1125,4 +1124,4 @@ "Description": "Artifact hash for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.ec2-task.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.ec2-task.expected.json index 9e589c8acc84c..1cc438369d59b 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.ec2-task.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.ec2-task.expected.json @@ -141,7 +141,6 @@ "Properties": { "MaxSize": "1", "MinSize": "1", - "DesiredCapacity": "1", "LaunchConfigurationName": { "Ref": "FargateClusterDefaultAutoScalingGroupLaunchConfig57306899" }, diff --git a/yarn.lock b/yarn.lock index 68564994e28b0..e52f1bac1fd96 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1589,6 +1589,11 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" +app-root-path@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.2.1.tgz#d0df4a682ee408273583d43f6f79e9892624bc9a" + integrity sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA== + append-transform@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-1.0.0.tgz#046a52ae582a228bd72f58acfbe2967c678759ab" @@ -1821,6 +1826,21 @@ aws-sdk@^2.483.0, aws-sdk@^2.595.0: uuid "3.3.2" xml2js "0.4.19" +aws-sdk@^2.540.0: + version "2.594.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.594.0.tgz#0aa86a7d7e5ff7e74e4ebff1bb6c55edab7c38b0" + integrity sha512-z6PTM6Tz+UHB5o/5cG63E5san9788mSRH/7A7Qz0hNC4nVe9GlKrPcgf3+bceaP8YB0hf1TJ51Ki3BhkM9nfKg== + dependencies: + buffer "4.9.1" + events "1.1.1" + ieee754 "1.1.13" + jmespath "0.15.0" + querystring "0.2.0" + sax "1.2.1" + url "0.10.3" + uuid "3.3.2" + xml2js "0.4.19" + aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" @@ -3067,6 +3087,16 @@ dot-prop@^4.2.0: dependencies: is-obj "^1.0.0" +dotenv-json@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dotenv-json/-/dotenv-json-1.0.0.tgz#fc7f672aafea04bed33818733b9f94662332815c" + integrity sha512-jAssr+6r4nKhKRudQ0HOzMskOFFi9+ubXWwmrSGJFgTvpjyPXCXsCsYbjif6mXp7uxA7xY3/LGaiTQukZzSbOQ== + +dotenv@^8.0.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" + integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== + dreamopt@~0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/dreamopt/-/dreamopt-0.6.0.tgz#d813ccdac8d39d8ad526775514a13dda664d6b4b" @@ -3234,6 +3264,11 @@ escodegen@^1.9.1: optionalDependencies: source-map "~0.6.1" +eslint-config-standard@^14.1.0: + version "14.1.0" + resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-14.1.0.tgz#b23da2b76fe5a2eba668374f246454e7058f15d4" + integrity sha512-EF6XkrrGVbvv8hL/kYa/m6vnvmUT+K82pJJc4JJVMM6+Qgqh0pnwprSxdduDLB9p/7bIxD+YV5O0wfb8lmcPbA== + eslint-import-resolver-node@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a" @@ -3261,7 +3296,15 @@ eslint-module-utils@^2.4.1: debug "^2.6.9" pkg-dir "^2.0.0" -eslint-plugin-import@^2.19.1: +eslint-plugin-es@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-2.0.0.tgz#0f5f5da5f18aa21989feebe8a73eadefb3432976" + integrity sha512-f6fceVtg27BR02EYnBhgWLFQfK6bN4Ll0nQFrBHOlCsAyxeZkn0NHns5O0YZOPrV1B3ramd6cgFwaoFLcSkwEQ== + dependencies: + eslint-utils "^1.4.2" + regexpp "^3.0.0" + +eslint-plugin-import@^2.18.2, eslint-plugin-import@^2.19.1: version "2.19.1" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.19.1.tgz#5654e10b7839d064dd0d46cd1b88ec2133a11448" integrity sha512-x68131aKoCZlCae7rDXKSAQmbT5DQuManyXo2sK6fJJ0aK5CWAkv6A6HJZGgqC8IhjQxYPgo6/IY4Oz8AFsbBw== @@ -3279,6 +3322,28 @@ eslint-plugin-import@^2.19.1: read-pkg-up "^2.0.0" resolve "^1.12.0" +eslint-plugin-node@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-10.0.0.tgz#fd1adbc7a300cf7eb6ac55cf4b0b6fc6e577f5a6" + integrity sha512-1CSyM/QCjs6PXaT18+zuAXsjXGIGo5Rw630rSKwokSs2jrYURQc4R5JZpoanNCqwNmepg+0eZ9L7YiRUJb8jiQ== + dependencies: + eslint-plugin-es "^2.0.0" + eslint-utils "^1.4.2" + ignore "^5.1.1" + minimatch "^3.0.4" + resolve "^1.10.1" + semver "^6.1.0" + +eslint-plugin-promise@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz#845fd8b2260ad8f82564c1222fce44ad71d9418a" + integrity sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw== + +eslint-plugin-standard@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz#ff0519f7ffaff114f76d1bd7c3996eef0f6e20b4" + integrity sha512-v/KBnfyaOMPmZc/dmc6ozOdWqekGp7bBGq4jLAecEfPGmfKiWS4sA8sC0LqiV9w5qmXAtXVn4M3p1jSyhY85SQ== + eslint-scope@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" @@ -3287,7 +3352,7 @@ eslint-scope@^5.0.0: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-utils@^1.4.3: +eslint-utils@^1.4.2, eslint-utils@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== @@ -4183,6 +4248,11 @@ ignore@^4.0.3, ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== +ignore@^5.1.1: + version "5.1.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.4.tgz#84b7b3dbe64552b6ef0eca99f6743dbec6d97adf" + integrity sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A== + immediate@~3.0.5: version "3.0.6" resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" @@ -5243,6 +5313,24 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +lambda-leak@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lambda-leak/-/lambda-leak-2.0.0.tgz#771985d3628487f6e885afae2b54510dcfb2cd7e" + integrity sha1-dxmF02KEh/boha+uK1RRDc+yzX4= + +lambda-tester@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/lambda-tester/-/lambda-tester-3.6.0.tgz#ceb7d4f4f0da768487a05cff37dcd088508b5247" + integrity sha512-F2ZTGWCLyIR95o/jWK46V/WnOCFAEUG/m/V7/CLhPJ7PCM+pror1rZ6ujP3TkItSGxUfpJi0kqwidw+M/nEqWw== + dependencies: + app-root-path "^2.2.1" + dotenv "^8.0.0" + dotenv-json "^1.0.0" + lambda-leak "^2.0.0" + semver "^6.1.1" + uuid "^3.3.2" + vandium-utils "^1.1.1" + lazystream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" @@ -5935,7 +6023,7 @@ nise@^1.5.2: lolex "^4.1.0" path-to-regexp "^1.7.0" -nock@^11.7.0: +nock@^11.3.5, nock@^11.7.0: version "11.7.0" resolved "https://registry.yarnpkg.com/nock/-/nock-11.7.0.tgz#5eaae8b8a55c0dfc014d05692c8cf3d31d61a342" integrity sha512-7c1jhHew74C33OBeRYyQENT+YXQiejpwIrEjinh6dRurBae+Ei4QjeUaPlkptIF0ZacEiVCnw8dWaxqepkiihg== @@ -7122,6 +7210,13 @@ resolve@1.x, resolve@^1.10.0, resolve@^1.11.1, resolve@^1.3.2: dependencies: path-parse "^1.0.6" +resolve@^1.10.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.14.1.tgz#9e018c540fcf0c427d678b9931cbf45e984bcaff" + integrity sha512-fn5Wobh4cxbLzuHaE+nphztHy43/b++4M6SsGFC2gB8uYwf0C8LcarfCz1un7UTW8OFQg9iNjZ4xpcFVGebDPg== + dependencies: + path-parse "^1.0.6" + resolve@^1.12.0, resolve@^1.5.0: version "1.13.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.13.1.tgz#be0aa4c06acd53083505abb35f4d66932ab35d16" @@ -7252,7 +7347,7 @@ sax@>=0.6.0, sax@^1.2.4: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@^6.0.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: +semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -8345,6 +8440,11 @@ validate-npm-package-name@^3.0.0: dependencies: builtins "^1.0.3" +vandium-utils@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/vandium-utils/-/vandium-utils-1.2.0.tgz#44735de4b7641a05de59ebe945f174e582db4f59" + integrity sha1-RHNd5LdkGgXeWevpRfF05YLbT1k= + verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" From 394313ee931e38bc20fc3dcb1cd2fd1b6f66822e Mon Sep 17 00:00:00 2001 From: Vincent Lesierse Date: Mon, 23 Dec 2019 17:54:21 +0100 Subject: [PATCH 17/80] feat(eks): helm chart support (#5390) * Added HelmRelease construct * feat(eks): Add HelmRelease construct * Fix some linting problems * Remove trailing whitespace * Add the possibility to specify the chart version * Changes after code review * Add shell=True to command execution * Execute helm command in /tmp * Write a correct values.yaml * Add resources to integration tests * Change require to import * Lazy add HelmChartHandler * Add integration tests for Helm * Added convenience addChart to Cluster * Fix integration test. * Change addChart method to use options pattern * Added @default and truncate default chart name * Added the Helm entry to the README.md Co-authored-by: Elad Ben-Israel Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- packages/@aws-cdk/aws-eks/README.md | 39 +- packages/@aws-cdk/aws-eks/lib/cluster.ts | 17 +- packages/@aws-cdk/aws-eks/lib/helm-chart.ts | 123 ++ .../@aws-cdk/aws-eks/lib/helm-chart/index.py | 136 ++ packages/@aws-cdk/aws-eks/lib/index.ts | 1 + .../@aws-cdk/aws-eks/lib/kubectl-layer.ts | 2 +- .../test/integ.eks-helm.lit.expected.json | 1336 +++++++++++++++++ .../aws-eks/test/integ.eks-helm.lit.ts | 54 + .../@aws-cdk/aws-eks/test/test.cluster.ts | 1 + .../@aws-cdk/aws-eks/test/test.helm-chart.ts | 55 + packages/@aws-cdk/aws-eks/test/util.ts | 8 + 11 files changed, 1769 insertions(+), 3 deletions(-) create mode 100644 packages/@aws-cdk/aws-eks/lib/helm-chart.ts create mode 100644 packages/@aws-cdk/aws-eks/lib/helm-chart/index.py create mode 100644 packages/@aws-cdk/aws-eks/test/integ.eks-helm.lit.expected.json create mode 100644 packages/@aws-cdk/aws-eks/test/integ.eks-helm.lit.ts create mode 100644 packages/@aws-cdk/aws-eks/test/test.helm-chart.ts diff --git a/packages/@aws-cdk/aws-eks/README.md b/packages/@aws-cdk/aws-eks/README.md index 0321d68a8d057..c4c7d3caa1f97 100644 --- a/packages/@aws-cdk/aws-eks/README.md +++ b/packages/@aws-cdk/aws-eks/README.md @@ -381,7 +381,44 @@ When kubectl is disabled, you should be aware of the following: edit the [aws-auth ConfigMap](https://docs.aws.amazon.com/eks/latest/userguide/add-user-role.html) when you add capacity in order to map the IAM instance role to RBAC to allow nodes to join the cluster. 3. Any `eks.Cluster` APIs that depend on programmatic kubectl support will fail - with an error: `cluster.addResource`, `cluster.awsAuth`, `props.mastersRole`. + with an error: `cluster.addResource`, `cluster.addChart`, `cluster.awsAuth`, `props.mastersRole`. + +### Helm Charts + +The `HelmChart` construct or `cluster.addChart` method can be used +to add Kubernetes resources to this cluster using Helm. + +The following example will install the [NGINX Ingress Controller](https://kubernetes.github.io/ingress-nginx/) +to you cluster using Helm. + +```ts +// option 1: use a construct +new HelmChart(this, 'NginxIngress', { + cluster, + chart: 'nginx-ingress', + repository: 'https://helm.nginx.com/stable', + namespace: 'kube-system' +}); + +// or, option2: use `addChart` +cluster.addChart('NginxIngress', { + chart: 'nginx-ingress', + repository: 'https://helm.nginx.com/stable', + namespace: 'kube-system' +}); +``` + +Helm charts will be installed and updated using `helm upgrade --install`. +This means that if the chart is added to CDK with the same release name, it will try to update +the chart in the cluster. The chart will exists as CloudFormation resource. + +Helm charts are implemented as CloudFormation resources in CDK. +This means that if the chart is deleted from your code (or the stack is +deleted), the next `cdk deploy` will issue a `helm uninstall` command and the +Helm chart will be deleted. + +When there is no `release` defined, the chart will be installed using the `node.uniqueId`, +which will be lower cassed and truncated to the last 63 characters. ### Roadmap diff --git a/packages/@aws-cdk/aws-eks/lib/cluster.ts b/packages/@aws-cdk/aws-eks/lib/cluster.ts index 14753bd7fdc77..97d11787ad45c 100644 --- a/packages/@aws-cdk/aws-eks/lib/cluster.ts +++ b/packages/@aws-cdk/aws-eks/lib/cluster.ts @@ -8,6 +8,7 @@ import * as path from 'path'; import { AwsAuth } from './aws-auth'; import { ClusterResource } from './cluster-resource'; import { CfnCluster, CfnClusterProps } from './eks.generated'; +import { HelmChart, HelmChartOptions } from './helm-chart'; import { KubernetesResource } from './k8s-resource'; import { KubectlLayer } from './kubectl-layer'; import { spotInterruptHandler } from './spot-interrupt-handler'; @@ -309,8 +310,10 @@ export class Cluster extends Resource implements ICluster { * automatically added by Amazon EKS to the `system:masters` RBAC group of the * cluster. Use `addMastersRole` or `props.mastersRole` to define additional * IAM roles as administrators. + * + * @internal */ - private readonly _defaultMastersRole?: iam.IRole; + public readonly _defaultMastersRole?: iam.IRole; /** * Manages the aws-auth config map. @@ -579,6 +582,18 @@ export class Cluster extends Resource implements ICluster { return new KubernetesResource(this, `manifest-${id}`, { cluster: this, manifest }); } + /** + * Defines a Helm chart in this cluster. + * + * @param id logical id of this chart. + * @param options options of this chart. + * @returns a `HelmChart` object + * @throws If `kubectlEnabled` is `false` + */ + public addChart(id: string, options: HelmChartOptions) { + return new HelmChart(this, `chart-${id}`, { cluster: this, ...options }); + } + private createKubernetesResourceHandler() { if (!this.kubectlEnabled) { return undefined; diff --git a/packages/@aws-cdk/aws-eks/lib/helm-chart.ts b/packages/@aws-cdk/aws-eks/lib/helm-chart.ts new file mode 100644 index 0000000000000..051b13774a3cf --- /dev/null +++ b/packages/@aws-cdk/aws-eks/lib/helm-chart.ts @@ -0,0 +1,123 @@ +import { CustomResource, CustomResourceProvider } from '@aws-cdk/aws-cloudformation'; +import * as lambda from '@aws-cdk/aws-lambda'; +import { Construct, Duration, Stack } from '@aws-cdk/core'; +import * as path from 'path'; +import { Cluster } from './cluster'; +import { KubectlLayer } from './kubectl-layer'; + +/** + * Helm Chart options. + */ + +export interface HelmChartOptions { + /** + * The name of the chart. + */ + readonly chart: string; + + /** + * The name of the release. + * @default - If no release name is given, it will use the last 63 characters of the node's unique id. + */ + readonly release?: string; + + /** + * The chart version to install. + * @default - If this is not specified, the latest version is installed + */ + readonly version?: string; + + /** + * The repository which contains the chart. For example: https://kubernetes-charts.storage.googleapis.com/ + * @default - No repository will be used, which means that the chart needs to be an absolute URL. + */ + readonly repository?: string; + + /** + * The Kubernetes namespace scope of the requests. + * @default default + */ + readonly namespace?: string; + + /** + * The values to be used by the chart. + * @default - No values are provided to the chart. + */ + readonly values?: {[key: string]: any}; +} + +/** + * Helm Chart properties. + */ +export interface HelmChartProps extends HelmChartOptions { + /** + * The EKS cluster to apply this configuration to. + * + * [disable-awslint:ref-via-interface] + */ + readonly cluster: Cluster; +} + +/** + * Represents a helm chart within the Kubernetes system. + * + * Applies/deletes the resources using `kubectl` in sync with the resource. + */ +export class HelmChart extends Construct { + /** + * The CloudFormation reosurce type. + */ + public static readonly RESOURCE_TYPE = 'Custom::AWSCDK-EKS-HelmChart'; + + constructor(scope: Construct, id: string, props: HelmChartProps) { + super(scope, id); + + const stack = Stack.of(this); + + // we maintain a single manifest custom resource handler for each cluster + const handler = this.getOrCreateHelmChartHandler(props.cluster); + if (!handler) { + throw new Error(`Cannot define a Helm chart on a cluster with kubectl disabled`); + } + + new CustomResource(this, 'Resource', { + provider: CustomResourceProvider.lambda(handler), + resourceType: HelmChart.RESOURCE_TYPE, + properties: { + Release: props.release || this.node.uniqueId.slice(-63).toLowerCase(), // Helm has a 63 character limit for the name + Chart: props.chart, + Version: props.version, + Values: (props.values ? stack.toJsonString(props.values) : undefined), + Namespace: props.namespace || 'default', + Repository: props.repository + } + }); + } + + private getOrCreateHelmChartHandler(cluster: Cluster): lambda.IFunction | undefined { + if (!cluster.kubectlEnabled) { + return undefined; + } + + let handler = cluster.node.tryFindChild('HelmChartHandler') as lambda.IFunction; + if (!handler) { + handler = new lambda.Function(cluster, 'HelmChartHandler', { + code: lambda.Code.fromAsset(path.join(__dirname, 'helm-chart')), + runtime: lambda.Runtime.PYTHON_3_7, + handler: 'index.handler', + timeout: Duration.minutes(15), + layers: [ KubectlLayer.getOrCreate(this, { version: "2.0.0-beta1" }) ], + memorySize: 256, + environment: { + CLUSTER_NAME: cluster.clusterName, + }, + + // NOTE: we must use the default IAM role that's mapped to "system:masters" + // as the execution role of this custom resource handler. This is the only + // way to be able to interact with the cluster after it's been created. + role: cluster._defaultMastersRole, + }); + } + return handler; + } +} diff --git a/packages/@aws-cdk/aws-eks/lib/helm-chart/index.py b/packages/@aws-cdk/aws-eks/lib/helm-chart/index.py new file mode 100644 index 0000000000000..0b311f61e0fcd --- /dev/null +++ b/packages/@aws-cdk/aws-eks/lib/helm-chart/index.py @@ -0,0 +1,136 @@ +import subprocess +import os +import json +import logging +import boto3 +from uuid import uuid4 +from botocore.vendored import requests + +logger = logging.getLogger() +logger.setLevel(logging.INFO) + +# these are coming from the kubectl layer +os.environ['PATH'] = '/opt/helm:/opt/awscli:' + os.environ['PATH'] + +outdir = os.environ.get('TEST_OUTDIR', '/tmp') +kubeconfig = os.path.join(outdir, 'kubeconfig') + +CFN_SUCCESS = "SUCCESS" +CFN_FAILED = "FAILED" + +def handler(event, context): + + def cfn_error(message=None): + logger.error("| cfn_error: %s" % message) + cfn_send(event, context, CFN_FAILED, reason=message) + + try: + logger.info(json.dumps(event)) + + request_type = event['RequestType'] + props = event['ResourceProperties'] + physical_id = event.get('PhysicalResourceId', None) + release = props['Release'] + chart = props['Chart'] + version = props.get('Version', None) + namespace = props.get('Namespace', None) + repository = props.get('Repository', None) + values_text = props.get('Values', None) + + cluster_name = os.environ.get('CLUSTER_NAME', None) + if cluster_name is None: + cfn_error("CLUSTER_NAME is missing in environment") + return + + subprocess.check_call([ 'aws', 'eks', 'update-kubeconfig', + '--name', cluster_name, + '--kubeconfig', kubeconfig + ]) + + # Write out the values to a file and include them with the install and upgrade + values_file = None + if not request_type == "Delete" and not values_text is None: + values = json.loads(values_text) + values_file = os.path.join(outdir, 'values.yaml') + with open(values_file, "w") as f: + f.write(json.dumps(values, indent=2)) + + if request_type == 'Create' or request_type == 'Update': + helm('upgrade', release, chart, repository, values_file, namespace, version) + elif request_type == "Delete": + try: + helm('uninstall', release, namespace=namespace) + except Exception as e: + logger.info("delete error: %s" % e) + + # if we are creating a new resource, allocate a physical id for it + # otherwise, we expect physical id to be relayed by cloudformation + if request_type == 'Create': + physical_id = "%s/%s" % (cluster_name, str(uuid4())) + else: + if not physical_id: + cfn_error("invalid request: request type is '%s' but 'PhysicalResourceId' is not defined" % request_type) + return + + cfn_send(event, context, CFN_SUCCESS, physicalResourceId=physical_id) + return + + except KeyError as e: + cfn_error("invalid request. Missing '%s'" % str(e)) + except Exception as e: + logger.exception(e) + cfn_error(str(e)) + +def helm(verb, release, chart = None, repo = None, file = None, namespace = None, version = None): + import subprocess + try: + cmnd = ['helm', verb, release] + if not chart is None: + cmnd.append(chart) + if verb == 'upgrade': + cmnd.append('--install') + if not repo is None: + cmnd.extend(['--repo', repo]) + if not file is None: + cmnd.extend(['--values', file]) + if not version is None: + cmnd.extend(['--version', version]) + if not namespace is None: + cmnd.extend(['--namespace', namespace]) + cmnd.extend(['--kubeconfig', kubeconfig]) + output = subprocess.check_output(cmnd, stderr=subprocess.STDOUT, cwd=outdir) + logger.info(output) + except subprocess.CalledProcessError as exc: + raise Exception(exc.output) + +#--------------------------------------------------------------------------------------------------- +# sends a response to cloudformation +def cfn_send(event, context, responseStatus, responseData={}, physicalResourceId=None, noEcho=False, reason=None): + + responseUrl = event['ResponseURL'] + logger.info(responseUrl) + + responseBody = {} + responseBody['Status'] = responseStatus + responseBody['Reason'] = reason or ('See the details in CloudWatch Log Stream: ' + context.log_stream_name) + responseBody['PhysicalResourceId'] = physicalResourceId or context.log_stream_name + responseBody['StackId'] = event['StackId'] + responseBody['RequestId'] = event['RequestId'] + responseBody['LogicalResourceId'] = event['LogicalResourceId'] + responseBody['NoEcho'] = noEcho + responseBody['Data'] = responseData + + body = json.dumps(responseBody) + logger.info("| response body:\n" + body) + + headers = { + 'content-type' : '', + 'content-length' : str(len(body)) + } + + try: + response = requests.put(responseUrl, data=body, headers=headers) + logger.info("| status code: " + response.reason) + except Exception as e: + logger.error("| unable to send response to CloudFormation") + logger.exception(e) diff --git a/packages/@aws-cdk/aws-eks/lib/index.ts b/packages/@aws-cdk/aws-eks/lib/index.ts index f894490485013..166d5bd35e5fc 100644 --- a/packages/@aws-cdk/aws-eks/lib/index.ts +++ b/packages/@aws-cdk/aws-eks/lib/index.ts @@ -1,6 +1,7 @@ export * from './cluster'; export * from './aws-auth-mapping'; export * from './k8s-resource'; +export * from './helm-chart'; export * from './aws-auth'; // AWS::EKS CloudFormation Resources: diff --git a/packages/@aws-cdk/aws-eks/lib/kubectl-layer.ts b/packages/@aws-cdk/aws-eks/lib/kubectl-layer.ts index 7bbb379aed3bd..211f6d8b36abd 100644 --- a/packages/@aws-cdk/aws-eks/lib/kubectl-layer.ts +++ b/packages/@aws-cdk/aws-eks/lib/kubectl-layer.ts @@ -26,7 +26,7 @@ export class KubectlLayer extends Construct implements lambda.ILayerVersion { */ public static getOrCreate(scope: Construct, props: KubectlLayerProps = {}): KubectlLayer { const stack = Stack.of(scope); - const id = 'kubectl-layer-8C2542BC-BF2B-4DFE-B765-E181FD30A9A0'; + const id = 'kubectl-layer-' + (props.version ? props.version : "8C2542BC-BF2B-4DFE-B765-E181FD30A9A0"); const exists = stack.node.tryFindChild(id) as KubectlLayer; if (exists) { return exists; diff --git a/packages/@aws-cdk/aws-eks/test/integ.eks-helm.lit.expected.json b/packages/@aws-cdk/aws-eks/test/integ.eks-helm.lit.expected.json new file mode 100644 index 0000000000000..10172624f8f2e --- /dev/null +++ b/packages/@aws-cdk/aws-eks/test/integ.eks-helm.lit.expected.json @@ -0,0 +1,1336 @@ +[ + { + "Resources": { + "vpcA2121C38": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "k8s-vpc/vpc" + } + ] + } + }, + "vpcPublicSubnet1Subnet2E65531E": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.0.0/18", + "VpcId": { + "Ref": "vpcA2121C38" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "Name", + "Value": "k8s-vpc/vpc/PublicSubnet1" + }, + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "kubernetes.io/role/elb", + "Value": "1" + } + ] + } + }, + "vpcPublicSubnet1RouteTable48A2DF9B": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "vpcA2121C38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "k8s-vpc/vpc/PublicSubnet1" + }, + { + "Key": "kubernetes.io/role/elb", + "Value": "1" + } + ] + } + }, + "vpcPublicSubnet1RouteTableAssociation5D3F4579": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "vpcPublicSubnet1RouteTable48A2DF9B" + }, + "SubnetId": { + "Ref": "vpcPublicSubnet1Subnet2E65531E" + } + } + }, + "vpcPublicSubnet1DefaultRoute10708846": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "vpcPublicSubnet1RouteTable48A2DF9B" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "vpcIGWE57CBDCA" + } + }, + "DependsOn": [ + "vpcVPCGW7984C166" + ] + }, + "vpcPublicSubnet1EIPDA49DCBE": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "k8s-vpc/vpc/PublicSubnet1" + }, + { + "Key": "kubernetes.io/role/elb", + "Value": "1" + } + ] + } + }, + "vpcPublicSubnet1NATGateway9C16659E": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "vpcPublicSubnet1EIPDA49DCBE", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "vpcPublicSubnet1Subnet2E65531E" + }, + "Tags": [ + { + "Key": "Name", + "Value": "k8s-vpc/vpc/PublicSubnet1" + }, + { + "Key": "kubernetes.io/role/elb", + "Value": "1" + } + ] + } + }, + "vpcPublicSubnet2Subnet009B674F": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.64.0/18", + "VpcId": { + "Ref": "vpcA2121C38" + }, + "AvailabilityZone": "test-region-1b", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "Name", + "Value": "k8s-vpc/vpc/PublicSubnet2" + }, + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "kubernetes.io/role/elb", + "Value": "1" + } + ] + } + }, + "vpcPublicSubnet2RouteTableEB40D4CB": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "vpcA2121C38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "k8s-vpc/vpc/PublicSubnet2" + }, + { + "Key": "kubernetes.io/role/elb", + "Value": "1" + } + ] + } + }, + "vpcPublicSubnet2RouteTableAssociation21F81B59": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "vpcPublicSubnet2RouteTableEB40D4CB" + }, + "SubnetId": { + "Ref": "vpcPublicSubnet2Subnet009B674F" + } + } + }, + "vpcPublicSubnet2DefaultRouteA1EC0F60": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "vpcPublicSubnet2RouteTableEB40D4CB" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "vpcIGWE57CBDCA" + } + }, + "DependsOn": [ + "vpcVPCGW7984C166" + ] + }, + "vpcPublicSubnet2EIP9B3743B1": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "k8s-vpc/vpc/PublicSubnet2" + }, + { + "Key": "kubernetes.io/role/elb", + "Value": "1" + } + ] + } + }, + "vpcPublicSubnet2NATGateway9B8AE11A": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "vpcPublicSubnet2EIP9B3743B1", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "vpcPublicSubnet2Subnet009B674F" + }, + "Tags": [ + { + "Key": "Name", + "Value": "k8s-vpc/vpc/PublicSubnet2" + }, + { + "Key": "kubernetes.io/role/elb", + "Value": "1" + } + ] + } + }, + "vpcPrivateSubnet1Subnet934893E8": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.128.0/18", + "VpcId": { + "Ref": "vpcA2121C38" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "Name", + "Value": "k8s-vpc/vpc/PrivateSubnet1" + }, + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "kubernetes.io/role/internal-elb", + "Value": "1" + } + ] + } + }, + "vpcPrivateSubnet1RouteTableB41A48CC": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "vpcA2121C38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "k8s-vpc/vpc/PrivateSubnet1" + }, + { + "Key": "kubernetes.io/role/internal-elb", + "Value": "1" + } + ] + } + }, + "vpcPrivateSubnet1RouteTableAssociation67945127": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "vpcPrivateSubnet1RouteTableB41A48CC" + }, + "SubnetId": { + "Ref": "vpcPrivateSubnet1Subnet934893E8" + } + } + }, + "vpcPrivateSubnet1DefaultRoute1AA8E2E5": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "vpcPrivateSubnet1RouteTableB41A48CC" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "vpcPublicSubnet1NATGateway9C16659E" + } + } + }, + "vpcPrivateSubnet2Subnet7031C2BA": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.192.0/18", + "VpcId": { + "Ref": "vpcA2121C38" + }, + "AvailabilityZone": "test-region-1b", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "Name", + "Value": "k8s-vpc/vpc/PrivateSubnet2" + }, + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "kubernetes.io/role/internal-elb", + "Value": "1" + } + ] + } + }, + "vpcPrivateSubnet2RouteTable7280F23E": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "vpcA2121C38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "k8s-vpc/vpc/PrivateSubnet2" + }, + { + "Key": "kubernetes.io/role/internal-elb", + "Value": "1" + } + ] + } + }, + "vpcPrivateSubnet2RouteTableAssociation007E94D3": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "vpcPrivateSubnet2RouteTable7280F23E" + }, + "SubnetId": { + "Ref": "vpcPrivateSubnet2Subnet7031C2BA" + } + } + }, + "vpcPrivateSubnet2DefaultRouteB0E07F99": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "vpcPrivateSubnet2RouteTable7280F23E" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "vpcPublicSubnet2NATGateway9B8AE11A" + } + } + }, + "vpcIGWE57CBDCA": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "k8s-vpc/vpc" + } + ] + } + }, + "vpcVPCGW7984C166": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "VpcId": { + "Ref": "vpcA2121C38" + }, + "InternetGatewayId": { + "Ref": "vpcIGWE57CBDCA" + } + } + } + }, + "Outputs": { + "ExportsOutputRefvpcA2121C384D1B3CDE": { + "Value": { + "Ref": "vpcA2121C38" + }, + "Export": { + "Name": "k8s-vpc:ExportsOutputRefvpcA2121C384D1B3CDE" + } + }, + "ExportsOutputRefvpcPublicSubnet1Subnet2E65531ECCB85041": { + "Value": { + "Ref": "vpcPublicSubnet1Subnet2E65531E" + }, + "Export": { + "Name": "k8s-vpc:ExportsOutputRefvpcPublicSubnet1Subnet2E65531ECCB85041" + } + }, + "ExportsOutputRefvpcPublicSubnet2Subnet009B674FB900C242": { + "Value": { + "Ref": "vpcPublicSubnet2Subnet009B674F" + }, + "Export": { + "Name": "k8s-vpc:ExportsOutputRefvpcPublicSubnet2Subnet009B674FB900C242" + } + }, + "ExportsOutputRefvpcPrivateSubnet1Subnet934893E8236E2271": { + "Value": { + "Ref": "vpcPrivateSubnet1Subnet934893E8" + }, + "Export": { + "Name": "k8s-vpc:ExportsOutputRefvpcPrivateSubnet1Subnet934893E8236E2271" + } + }, + "ExportsOutputRefvpcPrivateSubnet2Subnet7031C2BA60DCB1EE": { + "Value": { + "Ref": "vpcPrivateSubnet2Subnet7031C2BA" + }, + "Export": { + "Name": "k8s-vpc:ExportsOutputRefvpcPrivateSubnet2Subnet7031C2BA60DCB1EE" + } + } + } + }, + { + "Transform": "AWS::Serverless-2016-10-31", + "Resources": { + "cluster22ClusterRole5FC933B4": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "eks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/AmazonEKSClusterPolicy" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/AmazonEKSServicePolicy" + ] + ] + } + ] + } + }, + "cluster22ControlPlaneSecurityGroup2648B9CD": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "EKS Control Plane Security Group", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Fn::ImportValue": "k8s-vpc:ExportsOutputRefvpcA2121C384D1B3CDE" + } + } + }, + "cluster22ControlPlaneSecurityGroupfromk8sclustercluster22NodesInstanceSecurityGroupF903AE86443C3EDA943": { + "Type": "AWS::EC2::SecurityGroupIngress", + "Properties": { + "IpProtocol": "tcp", + "Description": "from k8sclustercluster22NodesInstanceSecurityGroupF903AE86:443", + "FromPort": 443, + "GroupId": { + "Fn::GetAtt": [ + "cluster22ControlPlaneSecurityGroup2648B9CD", + "GroupId" + ] + }, + "SourceSecurityGroupId": { + "Fn::GetAtt": [ + "cluster22NodesInstanceSecurityGroup4A3CDC24", + "GroupId" + ] + }, + "ToPort": 443 + } + }, + "cluster22ResourceHandlerServiceRoleC2E4F327": { + "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" + ] + ] + } + ] + } + }, + "cluster22ResourceHandlerServiceRoleDefaultPolicy1D33C3AC": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "eks:CreateCluster", + "eks:DescribeCluster", + "eks:DeleteCluster", + "eks:UpdateClusterVersion" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": "iam:PassRole", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "cluster22ClusterRole5FC933B4", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "cluster22ResourceHandlerServiceRoleDefaultPolicy1D33C3AC", + "Roles": [ + { + "Ref": "cluster22ResourceHandlerServiceRoleC2E4F327" + } + ] + } + }, + "cluster22ResourceHandler6227579A": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "AssetParametersea4957b16062595851e7d293ee45835db05c5693669a729cc02944b6ad19a204S3Bucket371D99F8" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParametersea4957b16062595851e7d293ee45835db05c5693669a729cc02944b6ad19a204S3VersionKeyFDCB25DD" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParametersea4957b16062595851e7d293ee45835db05c5693669a729cc02944b6ad19a204S3VersionKeyFDCB25DD" + } + ] + } + ] + } + ] + ] + } + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "cluster22ResourceHandlerServiceRoleC2E4F327", + "Arn" + ] + }, + "Runtime": "python3.7", + "Layers": [ + { + "Fn::GetAtt": [ + "kubectllayer8C2542BCBF2B4DFEB765E181FD30A9A0617C4ADA", + "Outputs.LayerVersionArn" + ] + } + ], + "MemorySize": 512, + "Timeout": 900 + }, + "DependsOn": [ + "cluster22ResourceHandlerServiceRoleDefaultPolicy1D33C3AC", + "cluster22ResourceHandlerServiceRoleC2E4F327" + ] + }, + "cluster227BD1CB20": { + "Type": "Custom::AWSCDK-EKS-Cluster", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "cluster22ResourceHandler6227579A", + "Arn" + ] + }, + "Config": { + "roleArn": { + "Fn::GetAtt": [ + "cluster22ClusterRole5FC933B4", + "Arn" + ] + }, + "resourcesVpcConfig": { + "securityGroupIds": [ + { + "Fn::GetAtt": [ + "cluster22ControlPlaneSecurityGroup2648B9CD", + "GroupId" + ] + } + ], + "subnetIds": [ + { + "Fn::ImportValue": "k8s-vpc:ExportsOutputRefvpcPublicSubnet1Subnet2E65531ECCB85041" + }, + { + "Fn::ImportValue": "k8s-vpc:ExportsOutputRefvpcPublicSubnet2Subnet009B674FB900C242" + }, + { + "Fn::ImportValue": "k8s-vpc:ExportsOutputRefvpcPrivateSubnet1Subnet934893E8236E2271" + }, + { + "Fn::ImportValue": "k8s-vpc:ExportsOutputRefvpcPrivateSubnet2Subnet7031C2BA60DCB1EE" + } + ] + } + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "cluster22KubernetesResourceHandler599F07E6": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444S3Bucket919126CB" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444S3VersionKey529BEF54" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444S3VersionKey529BEF54" + } + ] + } + ] + } + ] + ] + } + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "cluster22ResourceHandlerServiceRoleC2E4F327", + "Arn" + ] + }, + "Runtime": "python3.7", + "Environment": { + "Variables": { + "CLUSTER_NAME": { + "Ref": "cluster227BD1CB20" + } + } + }, + "Layers": [ + { + "Fn::GetAtt": [ + "kubectllayer8C2542BCBF2B4DFEB765E181FD30A9A0617C4ADA", + "Outputs.LayerVersionArn" + ] + } + ], + "MemorySize": 256, + "Timeout": 900 + }, + "DependsOn": [ + "cluster22ResourceHandlerServiceRoleDefaultPolicy1D33C3AC", + "cluster22ResourceHandlerServiceRoleC2E4F327" + ] + }, + "cluster22AwsAuthmanifest4685C84D": { + "Type": "Custom::AWSCDK-EKS-KubernetesResource", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "cluster22KubernetesResourceHandler599F07E6", + "Arn" + ] + }, + "Manifest": { + "Fn::Join": [ + "", + [ + "[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"aws-auth\",\"namespace\":\"kube-system\"},\"data\":{\"mapRoles\":\"[{\\\"rolearn\\\":\\\"", + { + "Fn::GetAtt": [ + "AdminRole38563C57", + "Arn" + ] + }, + "\\\",\\\"groups\\\":[\\\"system:masters\\\"]},{\\\"rolearn\\\":\\\"", + { + "Fn::GetAtt": [ + "cluster22NodesInstanceRole51CD052F", + "Arn" + ] + }, + "\\\",\\\"username\\\":\\\"system:node:{{EC2PrivateDNSName}}\\\",\\\"groups\\\":[\\\"system:bootstrappers\\\",\\\"system:nodes\\\"]}]\",\"mapUsers\":\"[]\",\"mapAccounts\":\"[]\"}}]" + ] + ] + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "cluster22NodesInstanceSecurityGroup4A3CDC24": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "k8s-cluster/cluster22/Nodes/InstanceSecurityGroup", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "Tags": [ + { + "Key": "Name", + "Value": "k8s-cluster/cluster22/Nodes" + }, + { + "Key": { + "Fn::Join": [ + "", + [ + "kubernetes.io/cluster/", + { + "Ref": "cluster227BD1CB20" + } + ] + ] + }, + "Value": "owned" + } + ], + "VpcId": { + "Fn::ImportValue": "k8s-vpc:ExportsOutputRefvpcA2121C384D1B3CDE" + } + } + }, + "cluster22NodesInstanceSecurityGroupfromk8sclustercluster22NodesInstanceSecurityGroupF903AE86ALLTRAFFIC774C7781": { + "Type": "AWS::EC2::SecurityGroupIngress", + "Properties": { + "IpProtocol": "-1", + "Description": "from k8sclustercluster22NodesInstanceSecurityGroupF903AE86:ALL TRAFFIC", + "GroupId": { + "Fn::GetAtt": [ + "cluster22NodesInstanceSecurityGroup4A3CDC24", + "GroupId" + ] + }, + "SourceSecurityGroupId": { + "Fn::GetAtt": [ + "cluster22NodesInstanceSecurityGroup4A3CDC24", + "GroupId" + ] + } + } + }, + "cluster22NodesInstanceSecurityGroupfromk8sclustercluster22ControlPlaneSecurityGroup3B5F21B44434A6E344D": { + "Type": "AWS::EC2::SecurityGroupIngress", + "Properties": { + "IpProtocol": "tcp", + "Description": "from k8sclustercluster22ControlPlaneSecurityGroup3B5F21B4:443", + "FromPort": 443, + "GroupId": { + "Fn::GetAtt": [ + "cluster22NodesInstanceSecurityGroup4A3CDC24", + "GroupId" + ] + }, + "SourceSecurityGroupId": { + "Fn::GetAtt": [ + "cluster22ControlPlaneSecurityGroup2648B9CD", + "GroupId" + ] + }, + "ToPort": 443 + } + }, + "cluster22NodesInstanceSecurityGroupfromk8sclustercluster22ControlPlaneSecurityGroup3B5F21B41025655355658FCAA": { + "Type": "AWS::EC2::SecurityGroupIngress", + "Properties": { + "IpProtocol": "tcp", + "Description": "from k8sclustercluster22ControlPlaneSecurityGroup3B5F21B4:1025-65535", + "FromPort": 1025, + "GroupId": { + "Fn::GetAtt": [ + "cluster22NodesInstanceSecurityGroup4A3CDC24", + "GroupId" + ] + }, + "SourceSecurityGroupId": { + "Fn::GetAtt": [ + "cluster22ControlPlaneSecurityGroup2648B9CD", + "GroupId" + ] + }, + "ToPort": 65535 + } + }, + "cluster22NodesInstanceRole51CD052F": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": { + "Fn::Join": [ + "", + [ + "ec2.", + { + "Ref": "AWS::URLSuffix" + } + ] + ] + } + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/AmazonEKSWorkerNodePolicy" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/AmazonEKS_CNI_Policy" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/AmazonEC2ContainerRegistryReadOnly" + ] + ] + } + ], + "Tags": [ + { + "Key": "Name", + "Value": "k8s-cluster/cluster22/Nodes" + }, + { + "Key": { + "Fn::Join": [ + "", + [ + "kubernetes.io/cluster/", + { + "Ref": "cluster227BD1CB20" + } + ] + ] + }, + "Value": "owned" + } + ] + } + }, + "cluster22NodesInstanceProfile3D4963ED": { + "Type": "AWS::IAM::InstanceProfile", + "Properties": { + "Roles": [ + { + "Ref": "cluster22NodesInstanceRole51CD052F" + } + ] + } + }, + "cluster22NodesLaunchConfig184BF3BA": { + "Type": "AWS::AutoScaling::LaunchConfiguration", + "Properties": { + "ImageId": { + "Ref": "SsmParameterValueawsserviceeksoptimizedami114amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter" + }, + "InstanceType": "t2.medium", + "IamInstanceProfile": { + "Ref": "cluster22NodesInstanceProfile3D4963ED" + }, + "SecurityGroups": [ + { + "Fn::GetAtt": [ + "cluster22NodesInstanceSecurityGroup4A3CDC24", + "GroupId" + ] + } + ], + "UserData": { + "Fn::Base64": { + "Fn::Join": [ + "", + [ + "#!/bin/bash\nset -o xtrace\n/etc/eks/bootstrap.sh ", + { + "Ref": "cluster227BD1CB20" + }, + " --kubelet-extra-args \"--node-labels lifecycle=OnDemand\" --use-max-pods true\n/opt/aws/bin/cfn-signal --exit-code $? --stack k8s-cluster --resource cluster22NodesASGC0A97398 --region test-region" + ] + ] + } + } + }, + "DependsOn": [ + "cluster22NodesInstanceRole51CD052F" + ] + }, + "cluster22NodesASGC0A97398": { + "Type": "AWS::AutoScaling::AutoScalingGroup", + "Properties": { + "MaxSize": "3", + "MinSize": "1", + "DesiredCapacity": "3", + "LaunchConfigurationName": { + "Ref": "cluster22NodesLaunchConfig184BF3BA" + }, + "Tags": [ + { + "Key": "Name", + "PropagateAtLaunch": true, + "Value": "k8s-cluster/cluster22/Nodes" + }, + { + "Key": { + "Fn::Join": [ + "", + [ + "kubernetes.io/cluster/", + { + "Ref": "cluster227BD1CB20" + } + ] + ] + }, + "PropagateAtLaunch": true, + "Value": "owned" + } + ], + "VPCZoneIdentifier": [ + { + "Fn::ImportValue": "k8s-vpc:ExportsOutputRefvpcPrivateSubnet1Subnet934893E8236E2271" + }, + { + "Fn::ImportValue": "k8s-vpc:ExportsOutputRefvpcPrivateSubnet2Subnet7031C2BA60DCB1EE" + } + ] + }, + "UpdatePolicy": { + "AutoScalingRollingUpdate": { + "WaitOnResourceSignals": false, + "PauseTime": "PT0S", + "SuspendProcesses": [ + "HealthCheck", + "ReplaceUnhealthy", + "AZRebalance", + "AlarmNotification", + "ScheduledActions" + ] + }, + "AutoScalingScheduledAction": { + "IgnoreUnmodifiedGroupSizeProperties": true + } + } + }, + "cluster22chartdashboard616811AB": { + "Type": "Custom::AWSCDK-EKS-HelmChart", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "cluster22HelmChartHandler0BAF302E", + "Arn" + ] + }, + "Release": "k8sclustercluster22chartdashboard3844c297", + "Chart": "kubernetes-dashboard", + "Namespace": "default", + "Repository": "https://kubernetes-charts.storage.googleapis.com" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "cluster22HelmChartHandler0BAF302E": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "AssetParameters8e2989bd32b411eba804b201a0f3984c984893c7fe6daa0b572fdd59c63e3653S3BucketD01BFA78" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters8e2989bd32b411eba804b201a0f3984c984893c7fe6daa0b572fdd59c63e3653S3VersionKeyD67E9179" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters8e2989bd32b411eba804b201a0f3984c984893c7fe6daa0b572fdd59c63e3653S3VersionKeyD67E9179" + } + ] + } + ] + } + ] + ] + } + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "cluster22ResourceHandlerServiceRoleC2E4F327", + "Arn" + ] + }, + "Runtime": "python3.7", + "Environment": { + "Variables": { + "CLUSTER_NAME": { + "Ref": "cluster227BD1CB20" + } + } + }, + "Layers": [ + { + "Fn::GetAtt": [ + "kubectllayer200beta1B9303363", + "Outputs.LayerVersionArn" + ] + } + ], + "MemorySize": 256, + "Timeout": 900 + }, + "DependsOn": [ + "cluster22ResourceHandlerServiceRoleDefaultPolicy1D33C3AC", + "cluster22ResourceHandlerServiceRoleC2E4F327" + ] + }, + "cluster22chartnginxingress90C2D506": { + "Type": "Custom::AWSCDK-EKS-HelmChart", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "cluster22HelmChartHandler0BAF302E", + "Arn" + ] + }, + "Release": "k8sclustercluster22chartnginxingress8b03389e", + "Chart": "nginx-ingress", + "Namespace": "kube-system", + "Repository": "https://helm.nginx.com/stable" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "kubectllayer8C2542BCBF2B4DFEB765E181FD30A9A0617C4ADA": { + "Type": "AWS::Serverless::Application", + "Properties": { + "Location": { + "ApplicationId": "arn:aws:serverlessrepo:us-east-1:903779448426:applications/lambda-layer-kubectl", + "SemanticVersion": "1.13.7" + }, + "Parameters": { + "LayerName": "kubectl-bedb92f2e70f45155fba70d3425dd148" + } + } + }, + "AdminRole38563C57": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::12345678:root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "kubectllayer200beta1B9303363": { + "Type": "AWS::Serverless::Application", + "Properties": { + "Location": { + "ApplicationId": "arn:aws:serverlessrepo:us-east-1:903779448426:applications/lambda-layer-kubectl", + "SemanticVersion": "2.0.0-beta1" + }, + "Parameters": { + "LayerName": "kubectl-aa3d1881d348da39094e6b1ce165f580" + } + } + } + }, + "Outputs": { + "cluster22ConfigCommand96B20279": { + "Value": { + "Fn::Join": [ + "", + [ + "aws eks update-kubeconfig --name ", + { + "Ref": "cluster227BD1CB20" + }, + " --region test-region" + ] + ] + } + }, + "cluster22GetTokenCommand99DB9B02": { + "Value": { + "Fn::Join": [ + "", + [ + "aws eks get-token --cluster-name ", + { + "Ref": "cluster227BD1CB20" + }, + " --region test-region" + ] + ] + } + } + }, + "Parameters": { + "AssetParametersea4957b16062595851e7d293ee45835db05c5693669a729cc02944b6ad19a204S3Bucket371D99F8": { + "Type": "String", + "Description": "S3 bucket for asset \"ea4957b16062595851e7d293ee45835db05c5693669a729cc02944b6ad19a204\"" + }, + "AssetParametersea4957b16062595851e7d293ee45835db05c5693669a729cc02944b6ad19a204S3VersionKeyFDCB25DD": { + "Type": "String", + "Description": "S3 key for asset version \"ea4957b16062595851e7d293ee45835db05c5693669a729cc02944b6ad19a204\"" + }, + "AssetParametersea4957b16062595851e7d293ee45835db05c5693669a729cc02944b6ad19a204ArtifactHashB80B497F": { + "Type": "String", + "Description": "Artifact hash for asset \"ea4957b16062595851e7d293ee45835db05c5693669a729cc02944b6ad19a204\"" + }, + "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444S3Bucket919126CB": { + "Type": "String", + "Description": "S3 bucket for asset \"640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444\"" + }, + "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444S3VersionKey529BEF54": { + "Type": "String", + "Description": "S3 key for asset version \"640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444\"" + }, + "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444ArtifactHash606C8127": { + "Type": "String", + "Description": "Artifact hash for asset \"640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444\"" + }, + "AssetParameters8e2989bd32b411eba804b201a0f3984c984893c7fe6daa0b572fdd59c63e3653S3BucketD01BFA78": { + "Type": "String", + "Description": "S3 bucket for asset \"8e2989bd32b411eba804b201a0f3984c984893c7fe6daa0b572fdd59c63e3653\"" + }, + "AssetParameters8e2989bd32b411eba804b201a0f3984c984893c7fe6daa0b572fdd59c63e3653S3VersionKeyD67E9179": { + "Type": "String", + "Description": "S3 key for asset version \"8e2989bd32b411eba804b201a0f3984c984893c7fe6daa0b572fdd59c63e3653\"" + }, + "AssetParameters8e2989bd32b411eba804b201a0f3984c984893c7fe6daa0b572fdd59c63e3653ArtifactHash77099D9F": { + "Type": "String", + "Description": "Artifact hash for asset \"8e2989bd32b411eba804b201a0f3984c984893c7fe6daa0b572fdd59c63e3653\"" + }, + "SsmParameterValueawsserviceeksoptimizedami114amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/aws/service/eks/optimized-ami/1.14/amazon-linux-2/recommended/image_id" + } + } + } +] \ No newline at end of file diff --git a/packages/@aws-cdk/aws-eks/test/integ.eks-helm.lit.ts b/packages/@aws-cdk/aws-eks/test/integ.eks-helm.lit.ts new file mode 100644 index 0000000000000..35113435a740b --- /dev/null +++ b/packages/@aws-cdk/aws-eks/test/integ.eks-helm.lit.ts @@ -0,0 +1,54 @@ +/// !cdk-integ * + +import * as ec2 from '@aws-cdk/aws-ec2'; +import * as iam from '@aws-cdk/aws-iam'; +import { App, Construct } from '@aws-cdk/core'; +import { Cluster } from '../lib'; +import { TestStack } from './util'; + +class VpcStack extends TestStack { + public readonly vpc: ec2.Vpc; + + constructor(scope: Construct, id: string) { + super(scope, id); + this.vpc = new ec2.Vpc(this, 'vpc', { maxAzs: 2 }); + } +} + +class ClusterStack extends TestStack { + public readonly cluster: Cluster; + + constructor(scope: Construct, id: string, props: { vpc: ec2.Vpc }) { + super(scope, id); + + /// !show + // define the cluster. kubectl is enabled by default. + this.cluster = new Cluster(this, 'cluster22', { + vpc: props.vpc, + defaultCapacity: 0, + }); + + // define an IAM role assumable by anyone in the account and map it to the k8s + // `system:masters` group this is required if you want to be able to issue + // manual `kubectl` commands against the cluster. + const mastersRole = new iam.Role(this, 'AdminRole', { assumedBy: new iam.AccountRootPrincipal() }); + this.cluster.awsAuth.addMastersRole(mastersRole); + + // add some capacity to the cluster. The IAM instance role will + // automatically be mapped via aws-auth to allow nodes to join the cluster. + this.cluster.addCapacity('Nodes', { + instanceType: new ec2.InstanceType('t2.medium'), + desiredCapacity: 3, + }); + + // add two Helm charts to the cluster. This will be the Kubernetes dashboard and the Nginx Ingress Controller + this.cluster.addChart('dashboard', { chart: 'kubernetes-dashboard', repository: 'https://kubernetes-charts.storage.googleapis.com' }); + this.cluster.addChart('nginx-ingress', { chart: 'nginx-ingress', repository: 'https://helm.nginx.com/stable', namespace: 'kube-system' }); + /// !hide + } +} + +const app = new App(); +const vpcStack = new VpcStack(app, 'k8s-vpc'); +new ClusterStack(app, 'k8s-cluster', { vpc: vpcStack.vpc }); +app.synth(); diff --git a/packages/@aws-cdk/aws-eks/test/test.cluster.ts b/packages/@aws-cdk/aws-eks/test/test.cluster.ts index 305058068f7f3..63934c1319046 100644 --- a/packages/@aws-cdk/aws-eks/test/test.cluster.ts +++ b/packages/@aws-cdk/aws-eks/test/test.cluster.ts @@ -203,6 +203,7 @@ export = { test.throws(() => cluster.addResource('foo', {}), /Cannot define a KubernetesManifest resource on a cluster with kubectl disabled/); test.throws(() => cluster.addCapacity('boo', { instanceType: new ec2.InstanceType('r5d.24xlarge'), mapRole: true }), /Cannot map instance IAM role to RBAC if kubectl is disabled for the cluster/); + test.throws(() => new eks.HelmChart(stack, 'MyChart', { cluster, chart: 'chart' }), /Cannot define a Helm chart on a cluster with kubectl disabled/); test.done(); }, diff --git a/packages/@aws-cdk/aws-eks/test/test.helm-chart.ts b/packages/@aws-cdk/aws-eks/test/test.helm-chart.ts new file mode 100644 index 0000000000000..8d36452d2be07 --- /dev/null +++ b/packages/@aws-cdk/aws-eks/test/test.helm-chart.ts @@ -0,0 +1,55 @@ +import { expect, haveResource } from '@aws-cdk/assert'; +import { Test } from 'nodeunit'; +import * as eks from '../lib'; +import { testFixtureCluster } from './util'; + +// tslint:disable:max-line-length + +export = { + 'add Helm chart': { + 'should have default namespace'(test: Test) { + // GIVEN + const { stack, cluster } = testFixtureCluster(); + + // WHEN + new eks.HelmChart(stack, 'MyChart', { cluster, chart: 'chart' }); + + // THEN + expect(stack).to(haveResource(eks.HelmChart.RESOURCE_TYPE, { Namespace: 'default' })); + test.done(); + }, + 'should have a lowercase default release name'(test: Test) { + // GIVEN + const { stack, cluster } = testFixtureCluster(); + + // WHEN + new eks.HelmChart(stack, 'MyChart', { cluster, chart: 'chart' }); + + // THEN + expect(stack).to(haveResource(eks.HelmChart.RESOURCE_TYPE, { Release: 'stackmychartff398361' })); + test.done(); + }, + 'should trim the last 63 of the default release name'(test: Test) { + // GIVEN + const { stack, cluster } = testFixtureCluster(); + + // WHEN + new eks.HelmChart(stack, 'MyChartNameWhichISMostProbablyLongerThenSixtyThreeCharacters', { cluster, chart: 'chart' }); + + // THEN + expect(stack).to(haveResource(eks.HelmChart.RESOURCE_TYPE, { Release: 'rtnamewhichismostprobablylongerthensixtythreecharactersb800614d' })); + test.done(); + }, + 'with values'(test: Test) { + // GIVEN + const { stack, cluster } = testFixtureCluster(); + + // WHEN + new eks.HelmChart(stack, 'MyChart', { cluster, chart: 'chart', values: { foo: 123 } }); + + // THEN + expect(stack).to(haveResource(eks.HelmChart.RESOURCE_TYPE, { Values: '{\"foo\":123}' })); + test.done(); + } + } +}; diff --git a/packages/@aws-cdk/aws-eks/test/util.ts b/packages/@aws-cdk/aws-eks/test/util.ts index a96d1d720ea84..6406e746d0a3e 100644 --- a/packages/@aws-cdk/aws-eks/test/util.ts +++ b/packages/@aws-cdk/aws-eks/test/util.ts @@ -1,5 +1,6 @@ import * as ec2 from '@aws-cdk/aws-ec2'; import { App, Construct, Stack } from '@aws-cdk/core'; +import { Cluster } from '../lib'; export function testFixture() { const { stack, app } = testFixtureNoVpc(); @@ -14,6 +15,13 @@ export function testFixtureNoVpc() { return { stack, app }; } +export function testFixtureCluster() { + const { stack, app } = testFixtureNoVpc(); + const cluster = new Cluster(stack, 'Cluster'); + + return { stack, app, cluster }; +} + // we must specify an explicit environment because we have an AMI map that is // keyed from the target region. const env = { From 9ab989e4aba7b4fdeee062097dda01b25d41675d Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Mon, 23 Dec 2019 21:32:53 +0200 Subject: [PATCH 18/80] feat(custom-resources): ignore DELETE after failed CREATE (#5525) * feat(custom-resources): ignore DELETE after failed CREATE When a CREATE operation fails, CloudFormation will automatically issue a DELETE operation with the `PhysicalResourceId` submitted by the FAILED response. The provider framework currently does not support customizing the PhysicalResourceId of a failed response (as described in #5524), and therefore it makes more sense to have the framework simply ignore this DELETE operation. Otherwise, the user handler will need to special case this somehow, without proper signal. The solution is to use a special marker for the physical resource ID when a CREATE fails, and recognize this marker in the subsequent DELETE. * chore(build): resolve eslint plugins relative to cdk-build-tools plugins are installed centrally under cdk-build-tools and therefore resolution should happen against that module instead of the current module. otherwise, we get an error `ESLint couldn't find the plugin "eslint-plugin-node".` * moved MISSING_MARKER to the last minute * update expectations --- packages/@aws-cdk/custom-resources/README.md | 13 +++---- .../runtime/cfn-response.ts | 36 +++++++++++++++++- .../integ.provider.expected.json | 36 +++++++++--------- .../test/provider-framework/runtime.test.ts | 37 ++++++++++++++++++- 4 files changed, 93 insertions(+), 29 deletions(-) diff --git a/packages/@aws-cdk/custom-resources/README.md b/packages/@aws-cdk/custom-resources/README.md index 6c2cf40b3533b..37c15ce100677 100644 --- a/packages/@aws-cdk/custom-resources/README.md +++ b/packages/@aws-cdk/custom-resources/README.md @@ -59,7 +59,7 @@ At the minimum, users must define the `onEvent` handler, which is invoked by the framework for all resource lifecycle events (`Create`, `Update` and `Delete`) and returns a result which is then submitted to CloudFormation. -The following example is a skelaton for a Python implementation of `onEvent`: +The following example is a skeleton for a Python implementation of `onEvent`: ```py def on_event(event, context): @@ -96,7 +96,7 @@ where the lifecycle operation cannot be completed immediately. The `isComplete` handler will be retried asynchronously after `onEvent` until it returns `IsComplete: true`, or until the total provider timeout has expired. -The following example is a skelaton for a Python implementation of `isComplete`: +The following example is a skeleton for a Python implementation of `isComplete`: ```py def is_complete(event, context): @@ -219,11 +219,10 @@ When AWS CloudFormation receives a "FAILED" response, it will attempt to roll back the stack to it's last state. This has different meanings for different lifecycle events: -- If a `Create` event fails, CloudFormation will issue a `Delete` event to allow - the provider to clean up any unfinished work related to the creation of the - resource. The implication of this is that it is recommended to implement - `Delete` in an idempotent way, in order to make sure that the rollback - `Delete` operation won't fail if a resource creation has failed. +- If a `Create` event fails, the resource provider framework will automatically + ignore the subsequent `Delete` operation issued by AWS CloudFormation. The + framework currently does not support customizing this behavior (see + https://github.com/aws/aws-cdk/issues/5524). - If an `Update` event fails, CloudFormation will issue an additional `Update` with the previous properties. - If a `Delete` event fails, CloudFormation will abandon this resource. diff --git a/packages/@aws-cdk/custom-resources/lib/provider-framework/runtime/cfn-response.ts b/packages/@aws-cdk/custom-resources/lib/provider-framework/runtime/cfn-response.ts index bd5d2140b31bb..2a7b1b0daf011 100644 --- a/packages/@aws-cdk/custom-resources/lib/provider-framework/runtime/cfn-response.ts +++ b/packages/@aws-cdk/custom-resources/lib/provider-framework/runtime/cfn-response.ts @@ -4,6 +4,9 @@ import * as url from 'url'; import { httpRequest } from './outbound'; import { log } from './util'; +export const CREATE_FAILED_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::CREATE_FAILED'; +export const MISSING_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::MISSING_PHYSICAL_ID'; + export interface CloudFormationResponseOptions { readonly reason?: string; readonly noEcho?: boolean; @@ -24,7 +27,7 @@ export async function submitResponse(status: 'SUCCESS' | 'FAILED', event: CloudF Reason: options.reason || status, StackId: event.StackId, RequestId: event.RequestId, - PhysicalResourceId: event.PhysicalResourceId || event.RequestId, + PhysicalResourceId: event.PhysicalResourceId || MISSING_PHYSICAL_ID_MARKER, LogicalResourceId: event.LogicalResourceId, NoEcho: options.noEcho, Data: event.Data @@ -50,11 +53,40 @@ export let includeStackTraces = true; // for unit tests export function safeHandler(block: (event: any) => Promise) { return async (event: any) => { + + // ignore DELETE event when the physical resource ID is the marker that + // indicates that this DELETE is a subsequent DELETE to a failed CREATE + // operation. + if (event.RequestType === 'Delete' && event.PhysicalResourceId === CREATE_FAILED_PHYSICAL_ID_MARKER) { + log(`ignoring DELETE event caused by a failed CREATE event`); + await submitResponse('SUCCESS', event); + return; + } + try { await block(event); } catch (e) { // tell waiter state machine to retry - if (e instanceof Retry) { throw e; } + if (e instanceof Retry) { + log(`retry requested by handler`); + throw e; + } + + if (!event.PhysicalResourceId) { + // special case: if CREATE fails, which usually implies, we usually don't + // have a physical resource id. in this case, the subsequent DELETE + // operation does not have any meaning, and will likely fail as well. to + // address this, we use a marker so the provider framework can simply + // ignore the subsequent DELETE. + if (event.RequestType === 'Create') { + log(`CREATE failed, responding with a marker physical resource id so that the subsequent DELETE will be ignored`); + event.PhysicalResourceId = CREATE_FAILED_PHYSICAL_ID_MARKER; + } else { + // otherwise, if PhysicalResourceId is not specified, something is + // terribly wrong because all other events should have an ID. + log(`ERROR: Malformed event. "PhysicalResourceId" is required: ${JSON.stringify(event)}`); + } + } // this is an actual error, fail the activity altogether and exist. await submitResponse('FAILED', event, { diff --git a/packages/@aws-cdk/custom-resources/test/provider-framework/integ.provider.expected.json b/packages/@aws-cdk/custom-resources/test/provider-framework/integ.provider.expected.json index 61673e72b6dbf..561e2e3b18680 100644 --- a/packages/@aws-cdk/custom-resources/test/provider-framework/integ.provider.expected.json +++ b/packages/@aws-cdk/custom-resources/test/provider-framework/integ.provider.expected.json @@ -200,7 +200,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters75d866254bf5ba7ec72a947ae03a1304d0b5dbe42d254b461b842ddf64abe63fS3BucketBF5EAA30" + "Ref": "AssetParameters3e728f777afd6d4a580bc77d99f86194358dca730432b3f4583e544f1e85d2a0S3BucketE8BB46CE" }, "S3Key": { "Fn::Join": [ @@ -213,7 +213,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters75d866254bf5ba7ec72a947ae03a1304d0b5dbe42d254b461b842ddf64abe63fS3VersionKeyFEA51101" + "Ref": "AssetParameters3e728f777afd6d4a580bc77d99f86194358dca730432b3f4583e544f1e85d2a0S3VersionKeyDFF4B5D8" } ] } @@ -226,7 +226,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters75d866254bf5ba7ec72a947ae03a1304d0b5dbe42d254b461b842ddf64abe63fS3VersionKeyFEA51101" + "Ref": "AssetParameters3e728f777afd6d4a580bc77d99f86194358dca730432b3f4583e544f1e85d2a0S3VersionKeyDFF4B5D8" } ] } @@ -579,7 +579,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters75d866254bf5ba7ec72a947ae03a1304d0b5dbe42d254b461b842ddf64abe63fS3BucketBF5EAA30" + "Ref": "AssetParameters3e728f777afd6d4a580bc77d99f86194358dca730432b3f4583e544f1e85d2a0S3BucketE8BB46CE" }, "S3Key": { "Fn::Join": [ @@ -592,7 +592,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters75d866254bf5ba7ec72a947ae03a1304d0b5dbe42d254b461b842ddf64abe63fS3VersionKeyFEA51101" + "Ref": "AssetParameters3e728f777afd6d4a580bc77d99f86194358dca730432b3f4583e544f1e85d2a0S3VersionKeyDFF4B5D8" } ] } @@ -605,7 +605,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters75d866254bf5ba7ec72a947ae03a1304d0b5dbe42d254b461b842ddf64abe63fS3VersionKeyFEA51101" + "Ref": "AssetParameters3e728f777afd6d4a580bc77d99f86194358dca730432b3f4583e544f1e85d2a0S3VersionKeyDFF4B5D8" } ] } @@ -721,7 +721,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters75d866254bf5ba7ec72a947ae03a1304d0b5dbe42d254b461b842ddf64abe63fS3BucketBF5EAA30" + "Ref": "AssetParameters3e728f777afd6d4a580bc77d99f86194358dca730432b3f4583e544f1e85d2a0S3BucketE8BB46CE" }, "S3Key": { "Fn::Join": [ @@ -734,7 +734,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters75d866254bf5ba7ec72a947ae03a1304d0b5dbe42d254b461b842ddf64abe63fS3VersionKeyFEA51101" + "Ref": "AssetParameters3e728f777afd6d4a580bc77d99f86194358dca730432b3f4583e544f1e85d2a0S3VersionKeyDFF4B5D8" } ] } @@ -747,7 +747,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters75d866254bf5ba7ec72a947ae03a1304d0b5dbe42d254b461b842ddf64abe63fS3VersionKeyFEA51101" + "Ref": "AssetParameters3e728f777afd6d4a580bc77d99f86194358dca730432b3f4583e544f1e85d2a0S3VersionKeyDFF4B5D8" } ] } @@ -860,7 +860,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters75d866254bf5ba7ec72a947ae03a1304d0b5dbe42d254b461b842ddf64abe63fS3BucketBF5EAA30" + "Ref": "AssetParameters3e728f777afd6d4a580bc77d99f86194358dca730432b3f4583e544f1e85d2a0S3BucketE8BB46CE" }, "S3Key": { "Fn::Join": [ @@ -873,7 +873,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters75d866254bf5ba7ec72a947ae03a1304d0b5dbe42d254b461b842ddf64abe63fS3VersionKeyFEA51101" + "Ref": "AssetParameters3e728f777afd6d4a580bc77d99f86194358dca730432b3f4583e544f1e85d2a0S3VersionKeyDFF4B5D8" } ] } @@ -886,7 +886,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters75d866254bf5ba7ec72a947ae03a1304d0b5dbe42d254b461b842ddf64abe63fS3VersionKeyFEA51101" + "Ref": "AssetParameters3e728f777afd6d4a580bc77d99f86194358dca730432b3f4583e544f1e85d2a0S3VersionKeyDFF4B5D8" } ] } @@ -1038,17 +1038,17 @@ "Type": "String", "Description": "Artifact hash for asset \"865603dd9d562e52d496bad5ef42cafdeb7c05931986d8ad11deb93dc0e436e6\"" }, - "AssetParameters75d866254bf5ba7ec72a947ae03a1304d0b5dbe42d254b461b842ddf64abe63fS3BucketBF5EAA30": { + "AssetParameters3e728f777afd6d4a580bc77d99f86194358dca730432b3f4583e544f1e85d2a0S3BucketE8BB46CE": { "Type": "String", - "Description": "S3 bucket for asset \"75d866254bf5ba7ec72a947ae03a1304d0b5dbe42d254b461b842ddf64abe63f\"" + "Description": "S3 bucket for asset \"3e728f777afd6d4a580bc77d99f86194358dca730432b3f4583e544f1e85d2a0\"" }, - "AssetParameters75d866254bf5ba7ec72a947ae03a1304d0b5dbe42d254b461b842ddf64abe63fS3VersionKeyFEA51101": { + "AssetParameters3e728f777afd6d4a580bc77d99f86194358dca730432b3f4583e544f1e85d2a0S3VersionKeyDFF4B5D8": { "Type": "String", - "Description": "S3 key for asset version \"75d866254bf5ba7ec72a947ae03a1304d0b5dbe42d254b461b842ddf64abe63f\"" + "Description": "S3 key for asset version \"3e728f777afd6d4a580bc77d99f86194358dca730432b3f4583e544f1e85d2a0\"" }, - "AssetParameters75d866254bf5ba7ec72a947ae03a1304d0b5dbe42d254b461b842ddf64abe63fArtifactHash921C6847": { + "AssetParameters3e728f777afd6d4a580bc77d99f86194358dca730432b3f4583e544f1e85d2a0ArtifactHashDD0FF81A": { "Type": "String", - "Description": "Artifact hash for asset \"75d866254bf5ba7ec72a947ae03a1304d0b5dbe42d254b461b842ddf64abe63f\"" + "Description": "Artifact hash for asset \"3e728f777afd6d4a580bc77d99f86194358dca730432b3f4583e544f1e85d2a0\"" }, "AssetParametersdb961fc9d087616ad76339bd5135f518cea24001f866a17067a1024235128511S3Bucket776FD46E": { "Type": "String", diff --git a/packages/@aws-cdk/custom-resources/test/provider-framework/runtime.test.ts b/packages/@aws-cdk/custom-resources/test/provider-framework/runtime.test.ts index 1fd1a2615b57e..b9c3a0586f617 100644 --- a/packages/@aws-cdk/custom-resources/test/provider-framework/runtime.test.ts +++ b/packages/@aws-cdk/custom-resources/test/provider-framework/runtime.test.ts @@ -241,6 +241,38 @@ test('fails if user handler returns a non-object response', async () => { expectCloudFormationFailed('return values from user-handlers must be JSON objects. got: \"string\"'); }); +describe('if CREATE fails, the subsequent DELETE will be ignored', () => { + + it('FAILED response sets PhysicalResourceId to a special marker', async () => { + // WHEN + mocks.onEventImplMock = async () => { throw new Error('CREATE FAILED'); }; + + // THEN + await simulateEvent({ + RequestType: 'Create' + }); + + expectCloudFormationFailed('CREATE FAILED', { + PhysicalResourceId: cfnResponse.CREATE_FAILED_PHYSICAL_ID_MARKER, + }); + }); + + it('DELETE request with the marker succeeds without calling user handler', async () => { + // GIVEN + // user handler is not assigned + + // WHEN + await simulateEvent({ + RequestType: 'Delete', + PhysicalResourceId: cfnResponse.CREATE_FAILED_PHYSICAL_ID_MARKER + }); + + // THEN + expectCloudFormationSuccess(); + }); + +}); + // ----------------------------------------------------------------------------------------------------------------------- /** @@ -292,10 +324,11 @@ async function simulateEvent(req: Partial) { expectCloudFormationResponse({ Status: 'FAILED', - Reason: expectedReason + Reason: expectedReason, + ...resp }); } From cb65da3cd31425265aadd2f2e806a45b244fab8b Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Tue, 24 Dec 2019 10:04:53 +0200 Subject: [PATCH 19/80] feat(cloudformation): update cloudformation spec to v10.2.0 (#5542) --- packages/@aws-cdk/cfnspec/CHANGELOG.md | 62 ++++ ...0_CloudFormationResourceSpecification.json | 282 +++++++++++++++++- 2 files changed, 333 insertions(+), 11 deletions(-) diff --git a/packages/@aws-cdk/cfnspec/CHANGELOG.md b/packages/@aws-cdk/cfnspec/CHANGELOG.md index c02614520f667..3ea868ab5ea30 100644 --- a/packages/@aws-cdk/cfnspec/CHANGELOG.md +++ b/packages/@aws-cdk/cfnspec/CHANGELOG.md @@ -1,3 +1,65 @@ +# CloudFormation Resource Specification v10.2.0 + +## New Resource Types + +* AWS::CodeBuild::ReportGroup +* AWS::EC2::GatewayRouteTableAssociation + +## Attribute Changes + + +## Property Changes + +* AWS::Cloud9::EnvironmentEC2 Tags (__added__) +* AWS::Cloud9::EnvironmentEC2 Description.UpdateType (__changed__) + * Old: Immutable + * New: Mutable +* AWS::Cloud9::EnvironmentEC2 Name.UpdateType (__changed__) + * Old: Immutable + * New: Mutable +* AWS::EC2::EIP InstanceId.UpdateType (__changed__) + * Old: Mutable + * New: Conditional +* AWS::EC2::EIP PublicIpv4Pool.UpdateType (__changed__) + * Old: Mutable + * New: Conditional +* AWS::MSK::Cluster OpenMonitoring (__added__) +* AWS::MSK::Cluster EnhancedMonitoring.UpdateType (__changed__) + * Old: Immutable + * New: Mutable +* AWS::RDS::DBInstance MaxAllocatedStorage (__added__) +* AWS::SSM::Document Name (__added__) + +## Property Type Changes + +* AWS::DLM::LifecyclePolicy.CrossRegionCopyRetainRule (__added__) +* AWS::DLM::LifecyclePolicy.CrossRegionCopyRule (__added__) +* AWS::MSK::Cluster.JmxExporter (__added__) +* AWS::MSK::Cluster.NodeExporter (__added__) +* AWS::MSK::Cluster.OpenMonitoring (__added__) +* AWS::MSK::Cluster.Prometheus (__added__) +* AWS::DLM::LifecyclePolicy.FastRestoreRule Interval (__added__) +* AWS::DLM::LifecyclePolicy.FastRestoreRule IntervalUnit (__added__) +* AWS::DLM::LifecyclePolicy.FastRestoreRule Count.Required (__changed__) + * Old: true + * New: false +* AWS::DLM::LifecyclePolicy.PolicyDetails ResourceTypes.Required (__changed__) + * Old: false + * New: true +* AWS::DLM::LifecyclePolicy.PolicyDetails Schedules.Required (__changed__) + * Old: false + * New: true +* AWS::DLM::LifecyclePolicy.PolicyDetails TargetTags.Required (__changed__) + * Old: false + * New: true +* AWS::DLM::LifecyclePolicy.RetainRule Interval (__added__) +* AWS::DLM::LifecyclePolicy.RetainRule IntervalUnit (__added__) +* AWS::DLM::LifecyclePolicy.RetainRule Count.Required (__changed__) + * Old: true + * New: false +* AWS::DLM::LifecyclePolicy.Schedule CrossRegionCopyRules (__added__) + + # CloudFormation Resource Specification v10.1.0 ## New Resource Types diff --git a/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json b/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json index fb97381fd7c8f..261ae2ebaaf1f 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json +++ b/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json @@ -5778,6 +5778,58 @@ } } }, + "AWS::CodeBuild::ReportGroup.ReportExportConfig": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codebuild-reportgroup-reportexportconfig.html", + "Properties": { + "ExportConfigType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codebuild-reportgroup-reportexportconfig.html#cfn-codebuild-reportgroup-reportexportconfig-exportconfigtype", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "S3Destination": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codebuild-reportgroup-reportexportconfig.html#cfn-codebuild-reportgroup-reportexportconfig-s3destination", + "Required": false, + "Type": "S3ReportExportConfig", + "UpdateType": "Mutable" + } + } + }, + "AWS::CodeBuild::ReportGroup.S3ReportExportConfig": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codebuild-reportgroup-s3reportexportconfig.html", + "Properties": { + "Bucket": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codebuild-reportgroup-s3reportexportconfig.html#cfn-codebuild-reportgroup-s3reportexportconfig-bucket", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "EncryptionDisabled": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codebuild-reportgroup-s3reportexportconfig.html#cfn-codebuild-reportgroup-s3reportexportconfig-encryptiondisabled", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "EncryptionKey": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codebuild-reportgroup-s3reportexportconfig.html#cfn-codebuild-reportgroup-s3reportexportconfig-encryptionkey", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Packaging": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codebuild-reportgroup-s3reportexportconfig.html#cfn-codebuild-reportgroup-s3reportexportconfig-packaging", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Path": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codebuild-reportgroup-s3reportexportconfig.html#cfn-codebuild-reportgroup-s3reportexportconfig-path", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, "AWS::CodeCommit::Repository.Code": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codecommit-repository-code.html", "Properties": { @@ -7736,6 +7788,58 @@ } } }, + "AWS::DLM::LifecyclePolicy.CrossRegionCopyRetainRule": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-crossregioncopyretainrule.html", + "Properties": { + "Interval": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-crossregioncopyretainrule.html#cfn-dlm-lifecyclepolicy-crossregioncopyretainrule-interval", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "IntervalUnit": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-crossregioncopyretainrule.html#cfn-dlm-lifecyclepolicy-crossregioncopyretainrule-intervalunit", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::DLM::LifecyclePolicy.CrossRegionCopyRule": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-crossregioncopyrule.html", + "Properties": { + "CmkArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-crossregioncopyrule.html#cfn-dlm-lifecyclepolicy-crossregioncopyrule-cmkarn", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "CopyTags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-crossregioncopyrule.html#cfn-dlm-lifecyclepolicy-crossregioncopyrule-copytags", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "Encrypted": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-crossregioncopyrule.html#cfn-dlm-lifecyclepolicy-crossregioncopyrule-encrypted", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "RetainRule": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-crossregioncopyrule.html#cfn-dlm-lifecyclepolicy-crossregioncopyrule-retainrule", + "Required": false, + "Type": "CrossRegionCopyRetainRule", + "UpdateType": "Mutable" + }, + "TargetRegion": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-crossregioncopyrule.html#cfn-dlm-lifecyclepolicy-crossregioncopyrule-targetregion", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, "AWS::DLM::LifecyclePolicy.FastRestoreRule": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-fastrestorerule.html", "Properties": { @@ -7749,7 +7853,19 @@ "Count": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-fastrestorerule.html#cfn-dlm-lifecyclepolicy-fastrestorerule-count", "PrimitiveType": "Integer", - "Required": true, + "Required": false, + "UpdateType": "Mutable" + }, + "Interval": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-fastrestorerule.html#cfn-dlm-lifecyclepolicy-fastrestorerule-interval", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "IntervalUnit": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-fastrestorerule.html#cfn-dlm-lifecyclepolicy-fastrestorerule-intervalunit", + "PrimitiveType": "String", + "Required": false, "UpdateType": "Mutable" } } @@ -7783,21 +7899,21 @@ "ResourceTypes": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-policydetails.html#cfn-dlm-lifecyclepolicy-policydetails-resourcetypes", "PrimitiveItemType": "String", - "Required": false, + "Required": true, "Type": "List", "UpdateType": "Mutable" }, "Schedules": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-policydetails.html#cfn-dlm-lifecyclepolicy-policydetails-schedules", "ItemType": "Schedule", - "Required": false, + "Required": true, "Type": "List", "UpdateType": "Mutable" }, "TargetTags": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-policydetails.html#cfn-dlm-lifecyclepolicy-policydetails-targettags", "ItemType": "Tag", - "Required": false, + "Required": true, "Type": "List", "UpdateType": "Mutable" } @@ -7809,7 +7925,19 @@ "Count": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-retainrule.html#cfn-dlm-lifecyclepolicy-retainrule-count", "PrimitiveType": "Integer", - "Required": true, + "Required": false, + "UpdateType": "Mutable" + }, + "Interval": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-retainrule.html#cfn-dlm-lifecyclepolicy-retainrule-interval", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "IntervalUnit": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-retainrule.html#cfn-dlm-lifecyclepolicy-retainrule-intervalunit", + "PrimitiveType": "String", + "Required": false, "UpdateType": "Mutable" } } @@ -7829,6 +7957,13 @@ "Type": "CreateRule", "UpdateType": "Mutable" }, + "CrossRegionCopyRules": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-schedule.html#cfn-dlm-lifecyclepolicy-schedule-crossregioncopyrules", + "ItemType": "CrossRegionCopyRule", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, "FastRestoreRule": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-schedule.html#cfn-dlm-lifecyclepolicy-schedule-fastrestorerule", "Required": false, @@ -21442,6 +21577,56 @@ } } }, + "AWS::MSK::Cluster.JmxExporter": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-msk-cluster-jmxexporter.html", + "Properties": { + "EnabledInBroker": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-msk-cluster-jmxexporter.html#cfn-msk-cluster-jmxexporter-enabledinbroker", + "PrimitiveType": "Boolean", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::MSK::Cluster.NodeExporter": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-msk-cluster-nodeexporter.html", + "Properties": { + "EnabledInBroker": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-msk-cluster-nodeexporter.html#cfn-msk-cluster-nodeexporter-enabledinbroker", + "PrimitiveType": "Boolean", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::MSK::Cluster.OpenMonitoring": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-msk-cluster-openmonitoring.html", + "Properties": { + "Prometheus": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-msk-cluster-openmonitoring.html#cfn-msk-cluster-openmonitoring-prometheus", + "Required": true, + "Type": "Prometheus", + "UpdateType": "Mutable" + } + } + }, + "AWS::MSK::Cluster.Prometheus": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-msk-cluster-prometheus.html", + "Properties": { + "JmxExporter": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-msk-cluster-prometheus.html#cfn-msk-cluster-prometheus-jmxexporter", + "Required": false, + "Type": "JmxExporter", + "UpdateType": "Mutable" + }, + "NodeExporter": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-msk-cluster-prometheus.html#cfn-msk-cluster-prometheus-nodeexporter", + "Required": false, + "Type": "NodeExporter", + "UpdateType": "Mutable" + } + } + }, "AWS::MSK::Cluster.StorageInfo": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-msk-cluster-storageinfo.html", "Properties": { @@ -29209,7 +29394,7 @@ } } }, - "ResourceSpecificationVersion": "10.1.0", + "ResourceSpecificationVersion": "10.2.0", "ResourceTypes": { "AWS::AccessAnalyzer::Analyzer": { "Attributes": { @@ -32972,7 +33157,7 @@ "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloud9-environmentec2.html#cfn-cloud9-environmentec2-description", "PrimitiveType": "String", "Required": false, - "UpdateType": "Immutable" + "UpdateType": "Mutable" }, "InstanceType": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloud9-environmentec2.html#cfn-cloud9-environmentec2-instancetype", @@ -32984,7 +33169,7 @@ "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloud9-environmentec2.html#cfn-cloud9-environmentec2-name", "PrimitiveType": "String", "Required": false, - "UpdateType": "Immutable" + "UpdateType": "Mutable" }, "OwnerArn": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloud9-environmentec2.html#cfn-cloud9-environmentec2-ownerarn", @@ -33004,6 +33189,13 @@ "PrimitiveType": "String", "Required": false, "UpdateType": "Immutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloud9-environmentec2.html#cfn-cloud9-environmentec2-tags", + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" } } }, @@ -33642,6 +33834,34 @@ } } }, + "AWS::CodeBuild::ReportGroup": { + "Attributes": { + "Arn": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-codebuild-reportgroup.html", + "Properties": { + "ExportConfig": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-codebuild-reportgroup.html#cfn-codebuild-reportgroup-exportconfig", + "Required": true, + "Type": "ReportExportConfig", + "UpdateType": "Mutable" + }, + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-codebuild-reportgroup.html#cfn-codebuild-reportgroup-name", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "Type": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-codebuild-reportgroup.html#cfn-codebuild-reportgroup-type", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, "AWS::CodeBuild::SourceCredential": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-codebuild-sourcecredential.html", "Properties": { @@ -36499,13 +36719,13 @@ "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-eip.html#cfn-ec2-eip-instanceid", "PrimitiveType": "String", "Required": false, - "UpdateType": "Mutable" + "UpdateType": "Conditional" }, "PublicIpv4Pool": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-eip.html#cfn-ec2-eip-publicipv4pool", "PrimitiveType": "String", "Required": false, - "UpdateType": "Mutable" + "UpdateType": "Conditional" }, "Tags": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-eip.html#cfn-ec2-eip-tags", @@ -36610,6 +36830,28 @@ } } }, + "AWS::EC2::GatewayRouteTableAssociation": { + "Attributes": { + "AssociationId": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-gatewayroutetableassociation.html", + "Properties": { + "GatewayId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-gatewayroutetableassociation.html#cfn-ec2-gatewayroutetableassociation-gatewayid", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "RouteTableId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-gatewayroutetableassociation.html#cfn-ec2-gatewayroutetableassociation-routetableid", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, "AWS::EC2::Host": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-host.html", "Properties": { @@ -44366,7 +44608,7 @@ "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-msk-cluster.html#cfn-msk-cluster-enhancedmonitoring", "PrimitiveType": "String", "Required": false, - "UpdateType": "Immutable" + "UpdateType": "Mutable" }, "KafkaVersion": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-msk-cluster.html#cfn-msk-cluster-kafkaversion", @@ -44380,6 +44622,12 @@ "Required": true, "UpdateType": "Mutable" }, + "OpenMonitoring": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-msk-cluster.html#cfn-msk-cluster-openmonitoring", + "Required": false, + "Type": "OpenMonitoring", + "UpdateType": "Mutable" + }, "Tags": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-msk-cluster.html#cfn-msk-cluster-tags", "PrimitiveType": "Json", @@ -47311,6 +47559,12 @@ "Required": false, "UpdateType": "Immutable" }, + "MaxAllocatedStorage": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-rds-database-instance.html#cfn-rds-dbinstance-maxallocatedstorage", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, "MonitoringInterval": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-rds-database-instance.html#cfn-rds-dbinstance-monitoringinterval", "PrimitiveType": "Integer", @@ -49092,6 +49346,12 @@ "Required": false, "UpdateType": "Immutable" }, + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssm-document.html#cfn-ssm-document-name", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, "Tags": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssm-document.html#cfn-ssm-document-tags", "DuplicatesAllowed": true, From c3ac965b5c4cb816e9ecc974b741dc7e29fb86b1 Mon Sep 17 00:00:00 2001 From: Matt Shober <35979100+mshober@users.noreply.github.com> Date: Tue, 24 Dec 2019 03:43:39 -0500 Subject: [PATCH 20/80] fix(stepfunctions): permission race condition on state machine deletion (#5466) Lambda State Machines now depend on their policies. Fixes #5336 --- .../test/integ.ec2-task.expected.json | 6 +++- .../integ.evaluate-expression.expected.json | 6 +++- .../test/integ.fargate-task.expected.json | 6 +++- .../test/integ.invoke-function.expected.json | 6 +++- .../test/integ.job-poller.expected.json | 5 ++- .../test/integ.sagemaker.expected.json | 6 +++- .../aws-stepfunctions/lib/state-machine.ts | 2 ++ .../test/test.state-machine-resources.ts | 35 +++++++++++++++++-- .../integ.provider.expected.json | 6 +++- 9 files changed, 69 insertions(+), 9 deletions(-) diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.ec2-task.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.ec2-task.expected.json index 1cc438369d59b..879684a397616 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.ec2-task.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.ec2-task.expected.json @@ -897,7 +897,11 @@ "Arn" ] } - } + }, + "DependsOn": [ + "StateMachineRoleDefaultPolicyDF1E6607", + "StateMachineRoleB840431D" + ] } }, "Parameters": { diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.evaluate-expression.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.evaluate-expression.expected.json index 163b196f3b4ae..95f39c7a95538 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.evaluate-expression.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.evaluate-expression.expected.json @@ -170,7 +170,11 @@ "Arn" ] } - } + }, + "DependsOn": [ + "StateMachineRoleDefaultPolicyDF1E6607", + "StateMachineRoleB840431D" + ] } }, "Parameters": { diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.fargate-task.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.fargate-task.expected.json index ceffe5509fef9..8a4cde77c2fcd 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.fargate-task.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.fargate-task.expected.json @@ -489,7 +489,11 @@ "Arn" ] } - } + }, + "DependsOn": [ + "StateMachineRoleDefaultPolicyDF1E6607", + "StateMachineRoleB840431D" + ] } }, "Parameters": { diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.invoke-function.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.invoke-function.expected.json index 7628fb9a21aa2..7306c708756c4 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.invoke-function.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.invoke-function.expected.json @@ -266,7 +266,11 @@ "Arn" ] } - } + }, + "DependsOn": [ + "StateMachineRoleDefaultPolicyDF1E6607", + "StateMachineRoleB840431D" + ] } }, "Parameters": { diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.job-poller.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.job-poller.expected.json index 00bbd00975308..5087e1d80e41e 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.job-poller.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.job-poller.expected.json @@ -69,7 +69,10 @@ "Arn" ] } - } + }, + "DependsOn": [ + "StateMachineRoleB840431D" + ] } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.sagemaker.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.sagemaker.expected.json index a1b42cd21b43d..c3dbcccb44841 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.sagemaker.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.sagemaker.expected.json @@ -390,7 +390,11 @@ "Arn" ] } - } + }, + "DependsOn": [ + "StateMachineRoleDefaultPolicyDF1E6607", + "StateMachineRoleB840431D" + ] } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts b/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts index 980fd1266d22f..9dd2f6c620509 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts @@ -139,6 +139,8 @@ export class StateMachine extends StateMachineBase { definitionString: Stack.of(this).toJsonString(graph.toGraphJson()), }); + resource.node.addDependency(this.role); + for (const statement of graph.policyStatements) { this.addToRolePolicy(statement); } diff --git a/packages/@aws-cdk/aws-stepfunctions/test/test.state-machine-resources.ts b/packages/@aws-cdk/aws-stepfunctions/test/test.state-machine-resources.ts index a0de1f1c2d13f..51e372b3f14ea 100644 --- a/packages/@aws-cdk/aws-stepfunctions/test/test.state-machine-resources.ts +++ b/packages/@aws-cdk/aws-stepfunctions/test/test.state-machine-resources.ts @@ -1,4 +1,4 @@ -import { expect, haveResource } from '@aws-cdk/assert'; +import { expect, haveResource, ResourcePart } from '@aws-cdk/assert'; import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; import { Test } from 'nodeunit'; @@ -250,6 +250,37 @@ export = { }); test.done(); - } + }, + + 'State machines must depend on their roles'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const task = new stepfunctions.Task(stack, 'Task', { + task: { + bind: () => ({ + resourceArn: 'resource', + policyStatements: [ + new iam.PolicyStatement({ + resources: ['resource'], + actions: ["lambda:InvokeFunction"], + }) + ], + }) + } + }); + new stepfunctions.StateMachine(stack, 'StateMachine', { + definition: task + }); + + // THEN + expect(stack).to(haveResource('AWS::StepFunctions::StateMachine', { + DependsOn: [ + 'StateMachineRoleDefaultPolicyDF1E6607', + 'StateMachineRoleB840431D' + ] + }, ResourcePart.CompleteDefinition)); + + test.done(); + }, }; diff --git a/packages/@aws-cdk/custom-resources/test/provider-framework/integ.provider.expected.json b/packages/@aws-cdk/custom-resources/test/provider-framework/integ.provider.expected.json index 561e2e3b18680..80d3cd61aa9e3 100644 --- a/packages/@aws-cdk/custom-resources/test/provider-framework/integ.provider.expected.json +++ b/packages/@aws-cdk/custom-resources/test/provider-framework/integ.provider.expected.json @@ -1022,7 +1022,11 @@ "Arn" ] } - } + }, + "DependsOn": [ + "comamazonawscdkcustomresourcess3assertproviderwaiterstatemachineRoleDefaultPolicy9882AB39", + "comamazonawscdkcustomresourcess3assertproviderwaiterstatemachineRole39E8529F" + ] } }, "Parameters": { From 38a0d9b9a4c9ee1cb9338f630c03801485a4a58f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 24 Dec 2019 09:48:14 +0000 Subject: [PATCH 21/80] chore(deps-dev): bump sinon from 7.5.0 to 8.0.1 (#5526) Bumps [sinon](https://github.com/sinonjs/sinon) from 7.5.0 to 8.0.1. - [Release notes](https://github.com/sinonjs/sinon/releases) - [Changelog](https://github.com/sinonjs/sinon/blob/master/CHANGELOG.md) - [Commits](https://github.com/sinonjs/sinon/compare/v7.5.0...v8.0.1) Signed-off-by: dependabot-preview[bot] Co-authored-by: Elad Ben-Israel --- packages/@aws-cdk/assets/package.json | 2 +- packages/@aws-cdk/aws-lambda/package.json | 2 +- packages/@aws-cdk/aws-s3-assets/package.json | 2 +- .../@aws-cdk/custom-resources/package.json | 2 +- packages/aws-cdk/package.json | 2 +- yarn.lock | 179 +++++++----------- 6 files changed, 75 insertions(+), 114 deletions(-) diff --git a/packages/@aws-cdk/assets/package.json b/packages/@aws-cdk/assets/package.json index cfae2db26746a..2e2744dfcc1b3 100644 --- a/packages/@aws-cdk/assets/package.json +++ b/packages/@aws-cdk/assets/package.json @@ -72,7 +72,7 @@ "cdk-integ-tools": "1.19.0", "nodeunit": "^0.11.3", "pkglint": "1.19.0", - "sinon": "^7.5.0", + "sinon": "^8.0.1", "ts-mock-imports": "^1.2.6" }, "dependencies": { diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json index 6db50a8984b0a..a80c5e5188411 100644 --- a/packages/@aws-cdk/aws-lambda/package.json +++ b/packages/@aws-cdk/aws-lambda/package.json @@ -80,7 +80,7 @@ "nock": "^11.7.0", "nodeunit": "^0.11.3", "pkglint": "1.19.0", - "sinon": "^7.5.0" + "sinon": "^8.0.1" }, "dependencies": { "@aws-cdk/aws-cloudwatch": "1.19.0", diff --git a/packages/@aws-cdk/aws-s3-assets/package.json b/packages/@aws-cdk/aws-s3-assets/package.json index 911351556322e..5a1c7c8213369 100644 --- a/packages/@aws-cdk/aws-s3-assets/package.json +++ b/packages/@aws-cdk/aws-s3-assets/package.json @@ -67,7 +67,7 @@ "cdk-integ-tools": "1.19.0", "nodeunit": "^0.11.3", "pkglint": "1.19.0", - "sinon": "^7.5.0", + "sinon": "^8.0.1", "ts-mock-imports": "^1.2.6" }, "dependencies": { diff --git a/packages/@aws-cdk/custom-resources/package.json b/packages/@aws-cdk/custom-resources/package.json index 764ca8eb0994c..698aedf947214 100644 --- a/packages/@aws-cdk/custom-resources/package.json +++ b/packages/@aws-cdk/custom-resources/package.json @@ -81,7 +81,7 @@ "fs-extra": "^8.1.0", "nock": "^11.7.0", "pkglint": "1.19.0", - "sinon": "^7.5.0" + "sinon": "^8.0.1" }, "dependencies": { "@aws-cdk/aws-cloudformation": "1.19.0", diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index 531491a8bdf6f..915ca3d93209e 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -66,7 +66,7 @@ "mockery": "^2.1.0", "nodeunit": "^0.11.3", "pkglint": "1.19.0", - "sinon": "^7.5.0" + "sinon": "^8.0.1" }, "dependencies": { "@aws-cdk/cloudformation-diff": "1.19.0", diff --git a/yarn.lock b/yarn.lock index e52f1bac1fd96..e79f1f0e46978 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1121,10 +1121,10 @@ dependencies: "@types/node" ">= 8" -"@sinonjs/commons@^1", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.4.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.6.0.tgz#ec7670432ae9c8eb710400d112c201a362d83393" - integrity sha512-w4/WHG7C4WWFyE5geCieFJF6MZkbW4VAriol5KlmQXpAQdxvV0p26sqNZOW6Qyw6Y0l9K4g+cHvvczR2sEEpqg== +"@sinonjs/commons@^1", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.4.0", "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.7.0.tgz#f90ffc52a2e519f018b13b6c4da03cbff36ebed6" + integrity sha512-qbk9AP+cZUsKdW1GJsBpxPKFmCJ0T8swwzVje3qFd+AkQb74Q/tiuzrdfFg8AD2g5HH/XbE/I8Uc1KYHVYWfhg== dependencies: type-detect "4.0.8" @@ -1136,6 +1136,14 @@ "@sinonjs/commons" "^1" "@sinonjs/samsam" "^3.1.0" +"@sinonjs/formatio@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-4.0.1.tgz#50ac1da0c3eaea117ca258b06f4f88a471668bdb" + integrity sha512-asIdlLFrla/WZybhm0C8eEzaDNNrzymiTqHMeJl6zPW2881l3uuVRpm0QlRQEjqYWv6CcKMGYME3LbrLJsORBw== + dependencies: + "@sinonjs/commons" "^1" + "@sinonjs/samsam" "^4.2.0" + "@sinonjs/samsam@^3.1.0", "@sinonjs/samsam@^3.3.3": version "3.3.3" resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-3.3.3.tgz#46682efd9967b259b81136b9f120fd54585feb4a" @@ -1145,6 +1153,15 @@ array-from "^2.1.1" lodash "^4.17.15" +"@sinonjs/samsam@^4.0.1", "@sinonjs/samsam@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-4.2.0.tgz#64a9994331cc3da0a0d7cc06cae44faa09cb5f2c" + integrity sha512-yG7QbUz38ZPIegfuSMEcbOo0kkLGmPa8a0Qlz4dk7+cXYALDScWjIZzAm/u2+Frh+bcdZF6wZJZwwuJjY0WAjA== + dependencies: + "@sinonjs/commons" "^1.6.0" + array-from "^2.1.1" + lodash.get "^4.4.2" + "@sinonjs/text-encoding@^0.7.1": version "0.7.1" resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" @@ -1589,11 +1606,6 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" -app-root-path@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.2.1.tgz#d0df4a682ee408273583d43f6f79e9892624bc9a" - integrity sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA== - append-transform@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-1.0.0.tgz#046a52ae582a228bd72f58acfbe2967c678759ab" @@ -1826,21 +1838,6 @@ aws-sdk@^2.483.0, aws-sdk@^2.595.0: uuid "3.3.2" xml2js "0.4.19" -aws-sdk@^2.540.0: - version "2.594.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.594.0.tgz#0aa86a7d7e5ff7e74e4ebff1bb6c55edab7c38b0" - integrity sha512-z6PTM6Tz+UHB5o/5cG63E5san9788mSRH/7A7Qz0hNC4nVe9GlKrPcgf3+bceaP8YB0hf1TJ51Ki3BhkM9nfKg== - dependencies: - buffer "4.9.1" - events "1.1.1" - ieee754 "1.1.13" - jmespath "0.15.0" - querystring "0.2.0" - sax "1.2.1" - url "0.10.3" - uuid "3.3.2" - xml2js "0.4.19" - aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" @@ -3087,16 +3084,6 @@ dot-prop@^4.2.0: dependencies: is-obj "^1.0.0" -dotenv-json@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/dotenv-json/-/dotenv-json-1.0.0.tgz#fc7f672aafea04bed33818733b9f94662332815c" - integrity sha512-jAssr+6r4nKhKRudQ0HOzMskOFFi9+ubXWwmrSGJFgTvpjyPXCXsCsYbjif6mXp7uxA7xY3/LGaiTQukZzSbOQ== - -dotenv@^8.0.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" - integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== - dreamopt@~0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/dreamopt/-/dreamopt-0.6.0.tgz#d813ccdac8d39d8ad526775514a13dda664d6b4b" @@ -3264,11 +3251,6 @@ escodegen@^1.9.1: optionalDependencies: source-map "~0.6.1" -eslint-config-standard@^14.1.0: - version "14.1.0" - resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-14.1.0.tgz#b23da2b76fe5a2eba668374f246454e7058f15d4" - integrity sha512-EF6XkrrGVbvv8hL/kYa/m6vnvmUT+K82pJJc4JJVMM6+Qgqh0pnwprSxdduDLB9p/7bIxD+YV5O0wfb8lmcPbA== - eslint-import-resolver-node@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a" @@ -3296,15 +3278,7 @@ eslint-module-utils@^2.4.1: debug "^2.6.9" pkg-dir "^2.0.0" -eslint-plugin-es@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-2.0.0.tgz#0f5f5da5f18aa21989feebe8a73eadefb3432976" - integrity sha512-f6fceVtg27BR02EYnBhgWLFQfK6bN4Ll0nQFrBHOlCsAyxeZkn0NHns5O0YZOPrV1B3ramd6cgFwaoFLcSkwEQ== - dependencies: - eslint-utils "^1.4.2" - regexpp "^3.0.0" - -eslint-plugin-import@^2.18.2, eslint-plugin-import@^2.19.1: +eslint-plugin-import@^2.19.1: version "2.19.1" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.19.1.tgz#5654e10b7839d064dd0d46cd1b88ec2133a11448" integrity sha512-x68131aKoCZlCae7rDXKSAQmbT5DQuManyXo2sK6fJJ0aK5CWAkv6A6HJZGgqC8IhjQxYPgo6/IY4Oz8AFsbBw== @@ -3322,28 +3296,6 @@ eslint-plugin-import@^2.18.2, eslint-plugin-import@^2.19.1: read-pkg-up "^2.0.0" resolve "^1.12.0" -eslint-plugin-node@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-10.0.0.tgz#fd1adbc7a300cf7eb6ac55cf4b0b6fc6e577f5a6" - integrity sha512-1CSyM/QCjs6PXaT18+zuAXsjXGIGo5Rw630rSKwokSs2jrYURQc4R5JZpoanNCqwNmepg+0eZ9L7YiRUJb8jiQ== - dependencies: - eslint-plugin-es "^2.0.0" - eslint-utils "^1.4.2" - ignore "^5.1.1" - minimatch "^3.0.4" - resolve "^1.10.1" - semver "^6.1.0" - -eslint-plugin-promise@^4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz#845fd8b2260ad8f82564c1222fce44ad71d9418a" - integrity sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw== - -eslint-plugin-standard@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz#ff0519f7ffaff114f76d1bd7c3996eef0f6e20b4" - integrity sha512-v/KBnfyaOMPmZc/dmc6ozOdWqekGp7bBGq4jLAecEfPGmfKiWS4sA8sC0LqiV9w5qmXAtXVn4M3p1jSyhY85SQ== - eslint-scope@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" @@ -3352,7 +3304,7 @@ eslint-scope@^5.0.0: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-utils@^1.4.2, eslint-utils@^1.4.3: +eslint-utils@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== @@ -4105,6 +4057,11 @@ has-flag@^3.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + has-symbols@^1.0.0, has-symbols@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" @@ -4248,11 +4205,6 @@ ignore@^4.0.3, ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.1.1: - version "5.1.4" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.4.tgz#84b7b3dbe64552b6ef0eca99f6743dbec6d97adf" - integrity sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A== - immediate@~3.0.5: version "3.0.6" resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" @@ -5313,24 +5265,6 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== -lambda-leak@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lambda-leak/-/lambda-leak-2.0.0.tgz#771985d3628487f6e885afae2b54510dcfb2cd7e" - integrity sha1-dxmF02KEh/boha+uK1RRDc+yzX4= - -lambda-tester@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/lambda-tester/-/lambda-tester-3.6.0.tgz#ceb7d4f4f0da768487a05cff37dcd088508b5247" - integrity sha512-F2ZTGWCLyIR95o/jWK46V/WnOCFAEUG/m/V7/CLhPJ7PCM+pror1rZ6ujP3TkItSGxUfpJi0kqwidw+M/nEqWw== - dependencies: - app-root-path "^2.2.1" - dotenv "^8.0.0" - dotenv-json "^1.0.0" - lambda-leak "^2.0.0" - semver "^6.1.1" - uuid "^3.3.2" - vandium-utils "^1.1.1" - lazystream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" @@ -5572,6 +5506,13 @@ lolex@^4.1.0, lolex@^4.2.0: resolved "https://registry.yarnpkg.com/lolex/-/lolex-4.2.0.tgz#ddbd7f6213ca1ea5826901ab1222b65d714b3cd7" integrity sha512-gKO5uExCXvSm6zbF562EvM+rd1kQDnB9AZBbiQVzf1ZmdDpxUSvpnAaVOP83N/31mRK8Ml8/VE8DMvsAZQ+7wg== +lolex@^5.0.1, lolex@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/lolex/-/lolex-5.1.2.tgz#953694d098ce7c07bc5ed6d0e42bc6c0c6d5a367" + integrity sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A== + dependencies: + "@sinonjs/commons" "^1.7.0" + loose-envify@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -6023,7 +5964,19 @@ nise@^1.5.2: lolex "^4.1.0" path-to-regexp "^1.7.0" -nock@^11.3.5, nock@^11.7.0: +nise@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/nise/-/nise-3.0.0.tgz#811330bf07ed3661c4b8bd72b18d9b6c6eb76f9f" + integrity sha512-EObFx5tioBMePHpU/gGczaY2YDqL255iwjmZwswu2CiwEW8xIGrr3E2xij+efIppS1nLQo9NyXSIUySGHUOhHQ== + dependencies: + "@sinonjs/commons" "^1.7.0" + "@sinonjs/formatio" "^4.0.1" + "@sinonjs/text-encoding" "^0.7.1" + just-extend "^4.0.2" + lolex "^5.0.1" + path-to-regexp "^1.7.0" + +nock@^11.7.0: version "11.7.0" resolved "https://registry.yarnpkg.com/nock/-/nock-11.7.0.tgz#5eaae8b8a55c0dfc014d05692c8cf3d31d61a342" integrity sha512-7c1jhHew74C33OBeRYyQENT+YXQiejpwIrEjinh6dRurBae+Ei4QjeUaPlkptIF0ZacEiVCnw8dWaxqepkiihg== @@ -7210,13 +7163,6 @@ resolve@1.x, resolve@^1.10.0, resolve@^1.11.1, resolve@^1.3.2: dependencies: path-parse "^1.0.6" -resolve@^1.10.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.14.1.tgz#9e018c540fcf0c427d678b9931cbf45e984bcaff" - integrity sha512-fn5Wobh4cxbLzuHaE+nphztHy43/b++4M6SsGFC2gB8uYwf0C8LcarfCz1un7UTW8OFQg9iNjZ4xpcFVGebDPg== - dependencies: - path-parse "^1.0.6" - resolve@^1.12.0, resolve@^1.5.0: version "1.13.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.13.1.tgz#be0aa4c06acd53083505abb35f4d66932ab35d16" @@ -7347,7 +7293,7 @@ sax@>=0.6.0, sax@^1.2.4: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: +semver@^6.0.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -7411,7 +7357,7 @@ signal-exit@^3.0.0, signal-exit@^3.0.2: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= -sinon@^7.3.2, sinon@^7.5.0: +sinon@^7.3.2: version "7.5.0" resolved "https://registry.yarnpkg.com/sinon/-/sinon-7.5.0.tgz#e9488ea466070ea908fd44a3d6478fd4923c67ec" integrity sha512-AoD0oJWerp0/rY9czP/D6hDTTUYGpObhZjMpd7Cl/A6+j0xBE+ayL/ldfggkBXUs0IkvIiM1ljM8+WkOc5k78Q== @@ -7424,6 +7370,19 @@ sinon@^7.3.2, sinon@^7.5.0: nise "^1.5.2" supports-color "^5.5.0" +sinon@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-8.0.1.tgz#c256376cd133dbb0c69ba4d73ed5bdd591597502" + integrity sha512-vbXMHBszVioyPsuRDLEiPEgvkZnbjfdCFvLYV4jONNJqZNLWTwZ/gYSNh3SuiT1w9MRXUz+S7aX0B4Ar2XI8iw== + dependencies: + "@sinonjs/commons" "^1.7.0" + "@sinonjs/formatio" "^4.0.1" + "@sinonjs/samsam" "^4.0.1" + diff "^4.0.1" + lolex "^5.1.2" + nise "^3.0.0" + supports-color "^7.1.0" + sisteransi@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.4.tgz#386713f1ef688c7c0304dc4c0632898941cad2e3" @@ -7860,6 +7819,13 @@ supports-color@^6.1.0: dependencies: has-flag "^3.0.0" +supports-color@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" + integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== + dependencies: + has-flag "^4.0.0" + symbol-tree@^3.2.2: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" @@ -8440,11 +8406,6 @@ validate-npm-package-name@^3.0.0: dependencies: builtins "^1.0.3" -vandium-utils@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/vandium-utils/-/vandium-utils-1.2.0.tgz#44735de4b7641a05de59ebe945f174e582db4f59" - integrity sha1-RHNd5LdkGgXeWevpRfF05YLbT1k= - verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" From 8cf2f02cedde6ea17e36622ad2d8c130ea63b871 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 24 Dec 2019 12:33:50 +0000 Subject: [PATCH 22/80] chore(deps-dev): bump @types/yargs from 13.0.3 to 13.0.4 (#5534) --- packages/aws-cdk/package.json | 2 +- packages/decdk/package.json | 2 +- tools/awslint/package.json | 2 +- tools/cdk-build-tools/package.json | 2 +- tools/cdk-integ-tools/package.json | 2 +- tools/cfn2ts/package.json | 2 +- tools/pkglint/package.json | 2 +- tools/pkgtools/package.json | 2 +- yarn.lock | 8 ++++---- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index 915ca3d93209e..d1632cf35ef74 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -59,7 +59,7 @@ "@types/table": "^4.0.7", "@types/uuid": "^3.4.6", "@types/yaml": "^1.2.0", - "@types/yargs": "^13.0.3", + "@types/yargs": "^13.0.4", "aws-sdk-mock": "^4.5.0", "cdk-build-tools": "1.19.0", "jszip": "^3.2.2", diff --git a/packages/decdk/package.json b/packages/decdk/package.json index 52e603a9c891a..892b9729b72fc 100644 --- a/packages/decdk/package.json +++ b/packages/decdk/package.json @@ -167,7 +167,7 @@ "@types/fs-extra": "^8.0.1", "@types/jest": "^24.0.23", "@types/yaml": "1.2.0", - "@types/yargs": "^13.0.3", + "@types/yargs": "^13.0.4", "jest": "^24.9.0", "jsii": "^0.20.11" }, diff --git a/tools/awslint/package.json b/tools/awslint/package.json index 541f821fb9bb9..74a1a0a3a3cee 100644 --- a/tools/awslint/package.json +++ b/tools/awslint/package.json @@ -25,7 +25,7 @@ }, "devDependencies": { "@types/fs-extra": "^8.0.1", - "@types/yargs": "^13.0.3", + "@types/yargs": "^13.0.4", "tslint": "^5.20.1", "typescript": "~3.7.4" }, diff --git a/tools/cdk-build-tools/package.json b/tools/cdk-build-tools/package.json index 98fcc2e3e56ca..66b56bfefe0b9 100644 --- a/tools/cdk-build-tools/package.json +++ b/tools/cdk-build-tools/package.json @@ -33,7 +33,7 @@ "devDependencies": { "@types/fs-extra": "^8.0.1", "@types/jest": "^24.0.23", - "@types/yargs": "^13.0.3", + "@types/yargs": "^13.0.4", "pkglint": "1.19.0" }, "dependencies": { diff --git a/tools/cdk-integ-tools/package.json b/tools/cdk-integ-tools/package.json index 66e799ea7291d..a3bc07edb490a 100644 --- a/tools/cdk-integ-tools/package.json +++ b/tools/cdk-integ-tools/package.json @@ -30,7 +30,7 @@ "license": "Apache-2.0", "devDependencies": { "@types/fs-extra": "^8.0.1", - "@types/yargs": "^13.0.3", + "@types/yargs": "^13.0.4", "cdk-build-tools": "1.19.0", "pkglint": "1.19.0" }, diff --git a/tools/cfn2ts/package.json b/tools/cfn2ts/package.json index 197bee5b53d64..0b879e8b654d0 100644 --- a/tools/cfn2ts/package.json +++ b/tools/cfn2ts/package.json @@ -38,7 +38,7 @@ "devDependencies": { "@types/fs-extra": "^8.0.1", "@types/jest": "^24.0.23", - "@types/yargs": "^13.0.3", + "@types/yargs": "^13.0.4", "cdk-build-tools": "1.19.0", "jest": "^24.9.0", "pkglint": "1.19.0" diff --git a/tools/pkglint/package.json b/tools/pkglint/package.json index a7136bb4e1fe4..7098395065c85 100644 --- a/tools/pkglint/package.json +++ b/tools/pkglint/package.json @@ -36,7 +36,7 @@ "devDependencies": { "@types/fs-extra": "^8.0.1", "@types/semver": "^6.2.0", - "@types/yargs": "^13.0.3" + "@types/yargs": "^13.0.4" }, "dependencies": { "case": "^1.6.2", diff --git a/tools/pkgtools/package.json b/tools/pkgtools/package.json index 5482e0741c27b..e0107f061762b 100644 --- a/tools/pkgtools/package.json +++ b/tools/pkgtools/package.json @@ -30,7 +30,7 @@ "license": "Apache-2.0", "devDependencies": { "@types/fs-extra": "^8.0.1", - "@types/yargs": "^13.0.3", + "@types/yargs": "^13.0.4", "cdk-build-tools": "1.19.0", "pkglint": "1.19.0" }, diff --git a/yarn.lock b/yarn.lock index e79f1f0e46978..f26a16fc4b9cb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1400,10 +1400,10 @@ resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-13.1.0.tgz#c563aa192f39350a1d18da36c5a8da382bbd8228" integrity sha512-gCubfBUZ6KxzoibJ+SCUc/57Ms1jz5NjHe4+dI2krNmU5zCPAphyLJYyTOg06ueIyfj+SaCUqmzun7ImlxDcKg== -"@types/yargs@^13.0.0", "@types/yargs@^13.0.3": - version "13.0.3" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.3.tgz#76482af3981d4412d65371a318f992d33464a380" - integrity sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ== +"@types/yargs@^13.0.0", "@types/yargs@^13.0.4": + version "13.0.4" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.4.tgz#53d231cebe1a540e7e13727fc1f0d13ad4a9ba3b" + integrity sha512-Ke1WmBbIkVM8bpvsNEcGgQM70XcEh/nbpxQhW7FhrsbCsXSY9BmLB1+LHtD7r9zrsOcFlLiF+a/UeJsdfw3C5A== dependencies: "@types/yargs-parser" "*" From dd7914149d95bce87764f00420772a07d1607842 Mon Sep 17 00:00:00 2001 From: Romain Marcadier-Muller Date: Tue, 24 Dec 2019 13:35:58 +0100 Subject: [PATCH 23/80] chore: stop having mergify try to auto-merge dependabot PRs These get auto-merged by dependabot itself, and attempts to update them to `HEAD` will cause dependabot to stop resolving merge conflicts automatically. --- .mergify.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.mergify.yml b/.mergify.yml index d58d427e27eb7..4bd8b174e4a1a 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -22,6 +22,8 @@ pull_request_rules: - -label~=(blocked|do-not-merge) - -merged - -closed + - author!=dependabot[bot] + - author!=dependabot-preview[bot] - "#approved-reviews-by>=1" - -approved-reviews-by~=author - "#changes-requested-reviews-by=0" From 544bbc2dd4d92dadaff17538407906bd21241e86 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 24 Dec 2019 13:13:36 +0000 Subject: [PATCH 24/80] chore(deps-dev): bump @types/jest from 24.0.23 to 24.0.24 (#5478) --- packages/@aws-cdk/assert/package.json | 2 +- packages/@aws-cdk/cx-api/package.json | 2 +- packages/cdk-dasm/package.json | 2 +- packages/decdk/package.json | 2 +- tools/cdk-build-tools/package.json | 2 +- tools/cfn2ts/package.json | 2 +- yarn.lock | 8 ++++---- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/@aws-cdk/assert/package.json b/packages/@aws-cdk/assert/package.json index 390800a41ad17..adb892536cddf 100644 --- a/packages/@aws-cdk/assert/package.json +++ b/packages/@aws-cdk/assert/package.json @@ -26,7 +26,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/jest": "^24.0.23", + "@types/jest": "^24.0.24", "@types/nodeunit": "^0.0.30", "cdk-build-tools": "1.19.0", "nodeunit": "^0.11.3", diff --git a/packages/@aws-cdk/cx-api/package.json b/packages/@aws-cdk/cx-api/package.json index ccfcf754b2bc4..c207365e30763 100644 --- a/packages/@aws-cdk/cx-api/package.json +++ b/packages/@aws-cdk/cx-api/package.json @@ -60,7 +60,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/jest": "^24.0.23", + "@types/jest": "^24.0.24", "@types/semver": "^6.2.0", "cdk-build-tools": "1.19.0", "jest": "^24.9.0", diff --git a/packages/cdk-dasm/package.json b/packages/cdk-dasm/package.json index 9ee8d8d702f41..505a493722d15 100644 --- a/packages/cdk-dasm/package.json +++ b/packages/cdk-dasm/package.json @@ -30,7 +30,7 @@ "yaml": "1.7.2" }, "devDependencies": { - "@types/jest": "^24.0.23", + "@types/jest": "^24.0.24", "@types/yaml": "1.2.0", "jest": "^24.9.0" }, diff --git a/packages/decdk/package.json b/packages/decdk/package.json index 892b9729b72fc..3f164af723f3b 100644 --- a/packages/decdk/package.json +++ b/packages/decdk/package.json @@ -165,7 +165,7 @@ }, "devDependencies": { "@types/fs-extra": "^8.0.1", - "@types/jest": "^24.0.23", + "@types/jest": "^24.0.24", "@types/yaml": "1.2.0", "@types/yargs": "^13.0.4", "jest": "^24.9.0", diff --git a/tools/cdk-build-tools/package.json b/tools/cdk-build-tools/package.json index 66b56bfefe0b9..5fec6175a35c0 100644 --- a/tools/cdk-build-tools/package.json +++ b/tools/cdk-build-tools/package.json @@ -32,7 +32,7 @@ "license": "Apache-2.0", "devDependencies": { "@types/fs-extra": "^8.0.1", - "@types/jest": "^24.0.23", + "@types/jest": "^24.0.24", "@types/yargs": "^13.0.4", "pkglint": "1.19.0" }, diff --git a/tools/cfn2ts/package.json b/tools/cfn2ts/package.json index 0b879e8b654d0..9c6faabbc4ea5 100644 --- a/tools/cfn2ts/package.json +++ b/tools/cfn2ts/package.json @@ -37,7 +37,7 @@ }, "devDependencies": { "@types/fs-extra": "^8.0.1", - "@types/jest": "^24.0.23", + "@types/jest": "^24.0.24", "@types/yargs": "^13.0.4", "cdk-build-tools": "1.19.0", "jest": "^24.9.0", diff --git a/yarn.lock b/yarn.lock index f26a16fc4b9cb..655bfff9b8cec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1268,10 +1268,10 @@ "@types/istanbul-lib-coverage" "*" "@types/istanbul-lib-report" "*" -"@types/jest@^24.0.23": - version "24.0.23" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.0.23.tgz#046f8e2ade026fe831623e361a36b6fb9a4463e4" - integrity sha512-L7MBvwfNpe7yVPTXLn32df/EK+AMBFAFvZrRuArGs7npEWnlziUXK+5GMIUTI4NIuwok3XibsjXCs5HxviYXjg== +"@types/jest@^24.0.24": + version "24.0.24" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.0.24.tgz#0f2f523dc77cc1bc6bef34eaf287ede887a73f05" + integrity sha512-vgaG968EDPSJPMunEDdZvZgvxYSmeH8wKqBlHSkBt1pV2XlLEVDzsj1ZhLuI4iG4Pv841tES61txSBF0obh4CQ== dependencies: jest-diff "^24.3.0" From 83be3c68fd182e60e9a672c3b36778dc99e2fc72 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 24 Dec 2019 13:50:13 +0000 Subject: [PATCH 25/80] chore(deps-dev): bump aws-sdk-mock from 4.5.0 to 5.0.0 (#5543) --- .../@aws-cdk/aws-events-targets/package.json | 2 +- packages/@aws-cdk/aws-lambda/package.json | 2 +- .../@aws-cdk/custom-resources/package.json | 2 +- packages/aws-cdk/package.json | 2 +- yarn.lock | 72 ++++--------------- 5 files changed, 17 insertions(+), 63 deletions(-) diff --git a/packages/@aws-cdk/aws-events-targets/package.json b/packages/@aws-cdk/aws-events-targets/package.json index b9dfd6e42b509..b6c84aa8c0b6d 100644 --- a/packages/@aws-cdk/aws-events-targets/package.json +++ b/packages/@aws-cdk/aws-events-targets/package.json @@ -81,7 +81,7 @@ "@aws-cdk/assert": "1.19.0", "@aws-cdk/aws-codecommit": "1.19.0", "aws-sdk": "^2.595.0", - "aws-sdk-mock": "^4.5.0", + "aws-sdk-mock": "^5.0.0", "cdk-build-tools": "1.19.0", "cdk-integ-tools": "1.19.0", "jest": "^24.9.0", diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json index a80c5e5188411..532e08537c988 100644 --- a/packages/@aws-cdk/aws-lambda/package.json +++ b/packages/@aws-cdk/aws-lambda/package.json @@ -72,7 +72,7 @@ "@types/nodeunit": "^0.0.30", "@types/sinon": "^7.5.0", "aws-sdk": "^2.595.0", - "aws-sdk-mock": "^4.5.0", + "aws-sdk-mock": "^5.0.0", "cdk-build-tools": "1.19.0", "cdk-integ-tools": "1.19.0", "cfn2ts": "1.19.0", diff --git a/packages/@aws-cdk/custom-resources/package.json b/packages/@aws-cdk/custom-resources/package.json index 698aedf947214..8eeb424a98eb6 100644 --- a/packages/@aws-cdk/custom-resources/package.json +++ b/packages/@aws-cdk/custom-resources/package.json @@ -74,7 +74,7 @@ "@types/fs-extra": "^8.0.1", "@types/sinon": "^7.5.0", "aws-sdk": "^2.595.0", - "aws-sdk-mock": "^4.5.0", + "aws-sdk-mock": "^5.0.0", "cdk-build-tools": "1.19.0", "cdk-integ-tools": "1.19.0", "cfn2ts": "1.19.0", diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index d1632cf35ef74..cf665f02edeb9 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -60,7 +60,7 @@ "@types/uuid": "^3.4.6", "@types/yaml": "^1.2.0", "@types/yargs": "^13.0.4", - "aws-sdk-mock": "^4.5.0", + "aws-sdk-mock": "^5.0.0", "cdk-build-tools": "1.19.0", "jszip": "^3.2.2", "mockery": "^2.1.0", diff --git a/yarn.lock b/yarn.lock index 655bfff9b8cec..92dcbcb92d219 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1121,21 +1121,13 @@ dependencies: "@types/node" ">= 8" -"@sinonjs/commons@^1", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.4.0", "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0": +"@sinonjs/commons@^1", "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0": version "1.7.0" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.7.0.tgz#f90ffc52a2e519f018b13b6c4da03cbff36ebed6" integrity sha512-qbk9AP+cZUsKdW1GJsBpxPKFmCJ0T8swwzVje3qFd+AkQb74Q/tiuzrdfFg8AD2g5HH/XbE/I8Uc1KYHVYWfhg== dependencies: type-detect "4.0.8" -"@sinonjs/formatio@^3.2.1": - version "3.2.2" - resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-3.2.2.tgz#771c60dfa75ea7f2d68e3b94c7e888a78781372c" - integrity sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ== - dependencies: - "@sinonjs/commons" "^1" - "@sinonjs/samsam" "^3.1.0" - "@sinonjs/formatio@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-4.0.1.tgz#50ac1da0c3eaea117ca258b06f4f88a471668bdb" @@ -1144,15 +1136,6 @@ "@sinonjs/commons" "^1" "@sinonjs/samsam" "^4.2.0" -"@sinonjs/samsam@^3.1.0", "@sinonjs/samsam@^3.3.3": - version "3.3.3" - resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-3.3.3.tgz#46682efd9967b259b81136b9f120fd54585feb4a" - integrity sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ== - dependencies: - "@sinonjs/commons" "^1.3.0" - array-from "^2.1.1" - lodash "^4.17.15" - "@sinonjs/samsam@^4.0.1", "@sinonjs/samsam@^4.2.0": version "4.2.0" resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-4.2.0.tgz#64a9994331cc3da0a0d7cc06cae44faa09cb5f2c" @@ -1814,19 +1797,19 @@ atob@^2.1.1: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -aws-sdk-mock@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/aws-sdk-mock/-/aws-sdk-mock-4.5.0.tgz#9de5feefaf9c755f22fcf029db3f202599a9219e" - integrity sha512-PAZKbQsdaVVoMr1JZbi04FUrkxCK16qnwBWLm4keeBrEfqYab/cFNsn5IVp/ThdMQpJGYHnmqUPyFq1plKaHZg== +aws-sdk-mock@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/aws-sdk-mock/-/aws-sdk-mock-5.0.0.tgz#2d2e9b6fbdb757c2e8f3787b60589759dcb2d49d" + integrity sha512-8vSdiRj4dEu3z4kkmIqHhP5Wrfiucgu0PaZxuT3KDSpuQIHMtw/SsuRsREkDQRBdkMtBg4BuddLZA8pWOs6QmQ== dependencies: - aws-sdk "^2.483.0" - sinon "^7.3.2" + aws-sdk "^2.596.0" + sinon "^8.0.1" traverse "^0.6.6" -aws-sdk@^2.483.0, aws-sdk@^2.595.0: - version "2.595.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.595.0.tgz#eb9f198716547d34a615e4bc32ce21056ee40959" - integrity sha512-bE/XzwlvEv3YPGfU7EfvAOi1IaEzmM+9VWP6xD9xN1lLhdBgCIiQIvSnr52LDR4J7ohqVP+oYpuBZcXrqZaP2Q== +aws-sdk@^2.595.0, aws-sdk@^2.596.0: + version "2.596.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.596.0.tgz#1a4af0609e174a50ffb8ed8981981e6d77a614fb" + integrity sha512-Bp+gyqhLw8tK4sgM1v1PDSw26H1mSXs6yhQInmGzDKqXJor6UyUb9JskFv0zC/bA84XizlshN1BBIgINqk6pNg== dependencies: buffer "4.9.1" events "1.1.1" @@ -3014,7 +2997,7 @@ diff-sequences@^24.9.0: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5" integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew== -diff@3.5.0, diff@^3.5.0: +diff@3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== @@ -5501,11 +5484,6 @@ log4js@^6.1.0: rfdc "^1.1.4" streamroller "^2.2.3" -lolex@^4.1.0, lolex@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lolex/-/lolex-4.2.0.tgz#ddbd7f6213ca1ea5826901ab1222b65d714b3cd7" - integrity sha512-gKO5uExCXvSm6zbF562EvM+rd1kQDnB9AZBbiQVzf1ZmdDpxUSvpnAaVOP83N/31mRK8Ml8/VE8DMvsAZQ+7wg== - lolex@^5.0.1, lolex@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/lolex/-/lolex-5.1.2.tgz#953694d098ce7c07bc5ed6d0e42bc6c0c6d5a367" @@ -5953,17 +5931,6 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -nise@^1.5.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/nise/-/nise-1.5.2.tgz#b6d29af10e48b321b307e10e065199338eeb2652" - integrity sha512-/6RhOUlicRCbE9s+94qCUsyE+pKlVJ5AhIv+jEE7ESKwnbXqulKZ1FYU+XAtHHWE9TinYvAxDUJAb912PwPoWA== - dependencies: - "@sinonjs/formatio" "^3.2.1" - "@sinonjs/text-encoding" "^0.7.1" - just-extend "^4.0.2" - lolex "^4.1.0" - path-to-regexp "^1.7.0" - nise@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/nise/-/nise-3.0.0.tgz#811330bf07ed3661c4b8bd72b18d9b6c6eb76f9f" @@ -7357,19 +7324,6 @@ signal-exit@^3.0.0, signal-exit@^3.0.2: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= -sinon@^7.3.2: - version "7.5.0" - resolved "https://registry.yarnpkg.com/sinon/-/sinon-7.5.0.tgz#e9488ea466070ea908fd44a3d6478fd4923c67ec" - integrity sha512-AoD0oJWerp0/rY9czP/D6hDTTUYGpObhZjMpd7Cl/A6+j0xBE+ayL/ldfggkBXUs0IkvIiM1ljM8+WkOc5k78Q== - dependencies: - "@sinonjs/commons" "^1.4.0" - "@sinonjs/formatio" "^3.2.1" - "@sinonjs/samsam" "^3.3.3" - diff "^3.5.0" - lolex "^4.2.0" - nise "^1.5.2" - supports-color "^5.5.0" - sinon@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/sinon/-/sinon-8.0.1.tgz#c256376cd133dbb0c69ba4d73ed5bdd591597502" @@ -7805,7 +7759,7 @@ supports-color@4.4.0: dependencies: has-flag "^2.0.0" -supports-color@^5.3.0, supports-color@^5.5.0: +supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== From 2df88f3481998db98d3bbd950998f1c0bf5e3b9c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 24 Dec 2019 14:27:18 +0000 Subject: [PATCH 26/80] chore(deps): bump @typescript-eslint/parser from 2.12.0 to 2.13.0 (#5536) --- tools/cdk-build-tools/package.json | 2 +- yarn.lock | 34 ++++++++++++++++++++++++------ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/tools/cdk-build-tools/package.json b/tools/cdk-build-tools/package.json index 5fec6175a35c0..19ebc357ff220 100644 --- a/tools/cdk-build-tools/package.json +++ b/tools/cdk-build-tools/package.json @@ -38,7 +38,7 @@ }, "dependencies": { "@typescript-eslint/eslint-plugin": "^2.12.0", - "@typescript-eslint/parser": "^2.12.0", + "@typescript-eslint/parser": "^2.13.0", "awslint": "1.19.0", "colors": "^1.4.0", "eslint": "^6.8.0", diff --git a/yarn.lock b/yarn.lock index 92dcbcb92d219..ebfc486b18014 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1410,14 +1410,23 @@ "@typescript-eslint/typescript-estree" "2.12.0" eslint-scope "^5.0.0" -"@typescript-eslint/parser@^2.12.0": - version "2.12.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.12.0.tgz#393f1604943a4ca570bb1a45bc8834e9b9158884" - integrity sha512-lPdkwpdzxEfjI8TyTzZqPatkrswLSVu4bqUgnB03fHSOwpC7KSerPgJRgIAf11UGNf7HKjJV6oaPZI4AghLU6g== +"@typescript-eslint/experimental-utils@2.13.0": + version "2.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.13.0.tgz#958614faa6f77599ee2b241740e0ea402482533d" + integrity sha512-+Hss3clwa6aNiC8ZjA45wEm4FutDV5HsVXPl/rDug1THq6gEtOYRGLqS3JlTk7mSnL5TbJz0LpEbzbPnKvY6sw== + dependencies: + "@types/json-schema" "^7.0.3" + "@typescript-eslint/typescript-estree" "2.13.0" + eslint-scope "^5.0.0" + +"@typescript-eslint/parser@^2.13.0": + version "2.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.13.0.tgz#ea1ab394cf9ca17467e3da7f96eca9309f57c326" + integrity sha512-vbDeLr5QRJ1K7x5iRK8J9wuGwR9OVyd1zDAY9XFAQvAosHVjSVbDgkm328ayE6hx2QWVGhwvGaEhedcqAbfQcA== dependencies: "@types/eslint-visitor-keys" "^1.0.0" - "@typescript-eslint/experimental-utils" "2.12.0" - "@typescript-eslint/typescript-estree" "2.12.0" + "@typescript-eslint/experimental-utils" "2.13.0" + "@typescript-eslint/typescript-estree" "2.13.0" eslint-visitor-keys "^1.1.0" "@typescript-eslint/typescript-estree@2.12.0": @@ -1433,6 +1442,19 @@ semver "^6.3.0" tsutils "^3.17.1" +"@typescript-eslint/typescript-estree@2.13.0": + version "2.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.13.0.tgz#a2e746867da772c857c13853219fced10d2566bc" + integrity sha512-t21Mg5cc8T3ADEUGwDisHLIubgXKjuNRbkpzDMLb7/JMmgCe/gHM9FaaujokLey+gwTuLF5ndSQ7/EfQqrQx4g== + dependencies: + debug "^4.1.1" + eslint-visitor-keys "^1.1.0" + glob "^7.1.6" + is-glob "^4.0.1" + lodash.unescape "4.0.1" + semver "^6.3.0" + tsutils "^3.17.1" + "@zkochan/cmd-shim@^3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@zkochan/cmd-shim/-/cmd-shim-3.1.0.tgz#2ab8ed81f5bb5452a85f25758eb9b8681982fd2e" From 03b3b7a9ea0f8449e60d46370e9e4daec81d7c54 Mon Sep 17 00:00:00 2001 From: Jonathan Goldwasser Date: Thu, 26 Dec 2019 19:18:25 +0100 Subject: [PATCH 27/80] fix(rds): do not allow aurora engines when using DatabaseInstance (#5367) * fix(rds): do not allow aurora engines when using DatabaseInstance Aurora instances can only be created inside a cluster and should use the `DatabaseCluster` construct. Closes #5345 * compile-time error with isDatabaseInstanceEngine * remove test on engine name Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- packages/@aws-cdk/aws-rds/lib/instance.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/@aws-cdk/aws-rds/lib/instance.ts b/packages/@aws-cdk/aws-rds/lib/instance.ts index 6e6396aa69e74..32e8f6e0ba4e4 100644 --- a/packages/@aws-cdk/aws-rds/lib/instance.ts +++ b/packages/@aws-cdk/aws-rds/lib/instance.ts @@ -166,6 +166,9 @@ export class DatabaseInstanceEngine extends DatabaseClusterEngine { public static readonly SQL_SERVER_EX = new DatabaseInstanceEngine('sqlserver-ex', secretsmanager.SecretRotationApplication.SQLSERVER_ROTATION_SINGLE_USER, secretsmanager.SecretRotationApplication.SQLSERVER_ROTATION_MULTI_USER); public static readonly SQL_SERVER_WEB = new DatabaseInstanceEngine('sqlserver-web', secretsmanager.SecretRotationApplication.SQLSERVER_ROTATION_SINGLE_USER, secretsmanager.SecretRotationApplication.SQLSERVER_ROTATION_MULTI_USER); /* tslint:enable max-line-length */ + + /** To make it a compile-time error to pass a DatabaseClusterEngine where a DatabaseInstanceEngine is expected. */ + public readonly isDatabaseInstanceEngine = true; } /** From 8d9b58bbf58c0b7281dbce79720e471e533a18c0 Mon Sep 17 00:00:00 2001 From: Meng Xin Zhu Date: Fri, 27 Dec 2019 04:35:05 +0800 Subject: [PATCH 28/80] feat(dynamodb): Implement importing existing dynamodb table. (#5280) Closes #3895. Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- packages/@aws-cdk/aws-dynamodb/README.md | 12 + packages/@aws-cdk/aws-dynamodb/lib/table.ts | 406 +++++++++++++----- .../aws-dynamodb/test/test.dynamodb.ts | 121 +++++- 3 files changed, 427 insertions(+), 112 deletions(-) diff --git a/packages/@aws-cdk/aws-dynamodb/README.md b/packages/@aws-cdk/aws-dynamodb/README.md index e476c4feb59ac..1b254f0d17e90 100644 --- a/packages/@aws-cdk/aws-dynamodb/README.md +++ b/packages/@aws-cdk/aws-dynamodb/README.md @@ -19,6 +19,18 @@ const table = new dynamodb.Table(this, 'Table', { }); ``` +### Importing existing tables + +To import an existing table into your CDK application, use the `Table.fromTableName` or `Table.fromTableArn` +factory method. This method accepts table name or table ARN which describes the properties of an already +existing table: + +```ts +const table = Table.fromTableArn(this, 'ImportedTable', 'arn:aws:dynamodb:us-east-1:111111111:table/my-table'); +// now you can just call methods on the table +table.grantReadWriteData(user); +``` + ### Keys When a table is defined, you must define it's schema using the `partitionKey` diff --git a/packages/@aws-cdk/aws-dynamodb/lib/table.ts b/packages/@aws-cdk/aws-dynamodb/lib/table.ts index 10f47fd397ef2..8d21402acbfe9 100644 --- a/packages/@aws-cdk/aws-dynamodb/lib/table.ts +++ b/packages/@aws-cdk/aws-dynamodb/lib/table.ts @@ -1,6 +1,6 @@ import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; import * as iam from '@aws-cdk/aws-iam'; -import { Aws, Construct, Lazy, RemovalPolicy, Resource, Stack } from '@aws-cdk/core'; +import { Aws, Construct, IResource, Lazy, RemovalPolicy, Resource, Stack } from '@aws-cdk/core'; import { CfnTable } from './dynamodb.generated'; import { EnableScalingProps, IScalableTableAttribute } from './scalable-attribute-api'; import { ScalableTableAttribute } from './scalable-table-attribute'; @@ -182,10 +182,170 @@ export interface LocalSecondaryIndexProps extends SecondaryIndexProps { readonly sortKey: Attribute; } +/** + * An interface that represents a DynamoDB Table - either created with the CDK, or an existing one. + */ +export interface ITable extends IResource { + /** + * Arn of the dynamodb table. + * + * @attribute + */ + readonly tableArn: string; + + /** + * Table name of the dynamodb table. + * + * @attribute + */ + readonly tableName: string; + + /** + * Permits an IAM principal all data read operations from this table: + * BatchGetItem, GetRecords, GetShardIterator, Query, GetItem, Scan. + * @param grantee The principal to grant access to + */ + grantReadData(grantee: iam.IGrantable): iam.Grant; + + /** + * Permits an IAM Principal to list streams attached to current dynamodb table. + * + * @param grantee The principal (no-op if undefined) + */ + grantTableListStreams(grantee: iam.IGrantable): iam.Grant; + + /** + * Permits an IAM principal all stream data read operations for this + * table's stream: + * DescribeStream, GetRecords, GetShardIterator, ListStreams. + * @param grantee The principal to grant access to + */ + grantStreamRead(grantee: iam.IGrantable): iam.Grant; + + /** + * Permits an IAM principal all data write operations to this table: + * BatchWriteItem, PutItem, UpdateItem, DeleteItem. + * @param grantee The principal to grant access to + */ + grantWriteData(grantee: iam.IGrantable): iam.Grant; + + /** + * Permits an IAM principal to all data read/write operations to this table. + * BatchGetItem, GetRecords, GetShardIterator, Query, GetItem, Scan, + * BatchWriteItem, PutItem, UpdateItem, DeleteItem + * @param grantee The principal to grant access to + */ + grantReadWriteData(grantee: iam.IGrantable): iam.Grant; +} + +/** + * Reference to a dynamodb table. + */ +export interface TableAttributes { + /** + * The ARN of the dynamodb table. + * One of this, or {@link tabeName}, is required. + * + * @default no table arn + */ + readonly tableArn?: string; + + /** + * The table name of the dynamodb table. + * One of this, or {@link tabeArn}, is required. + * + * @default no table name + */ + readonly tableName?: string; +} + +abstract class TableBase extends Resource implements ITable { + /** + * @attribute + */ + public abstract readonly tableArn: string; + + /** + * @attribute + */ + public abstract readonly tableName: string; + + /** + * Adds an IAM policy statement associated with this table to an IAM + * principal's policy. + * @param grantee The principal (no-op if undefined) + * @param actions The set of actions to allow (i.e. "dynamodb:PutItem", "dynamodb:GetItem", ...) + */ + public grant(grantee: iam.IGrantable, ...actions: string[]): iam.Grant { + return iam.Grant.addToPrincipal({ + grantee, + actions, + resourceArns: [ + this.tableArn, + Lazy.stringValue({ produce: () => this.hasIndex ? `${this.tableArn}/index/*` : Aws.NO_VALUE }) + ], + scope: this, + }); + } + + /** + * Permits an IAM principal all data read operations from this table: + * BatchGetItem, GetRecords, GetShardIterator, Query, GetItem, Scan. + * @param grantee The principal to grant access to + */ + public grantReadData(grantee: iam.IGrantable): iam.Grant { + return this.grant(grantee, ...READ_DATA_ACTIONS); + } + + /** + * Permits an IAM Principal to list streams attached to current dynamodb table. + * + * @param _grantee The principal (no-op if undefined) + */ + public abstract grantTableListStreams(_grantee: iam.IGrantable): iam.Grant; + + /** + * Permits an IAM principal all stream data read operations for this + * table's stream: + * DescribeStream, GetRecords, GetShardIterator, ListStreams. + * @param grantee The principal to grant access to + */ + public abstract grantStreamRead(grantee: iam.IGrantable): iam.Grant; + + /** + * Permits an IAM principal all data write operations to this table: + * BatchWriteItem, PutItem, UpdateItem, DeleteItem. + * @param grantee The principal to grant access to + */ + public grantWriteData(grantee: iam.IGrantable): iam.Grant { + return this.grant(grantee, ...WRITE_DATA_ACTIONS); + } + + /** + * Permits an IAM principal to all data read/write operations to this table. + * BatchGetItem, GetRecords, GetShardIterator, Query, GetItem, Scan, + * BatchWriteItem, PutItem, UpdateItem, DeleteItem + * @param grantee The principal to grant access to + */ + public grantReadWriteData(grantee: iam.IGrantable): iam.Grant { + return this.grant(grantee, ...READ_DATA_ACTIONS, ...WRITE_DATA_ACTIONS); + } + + /** + * Permits all DynamoDB operations ("dynamodb:*") to an IAM principal. + * @param grantee The principal to grant access to + */ + public grantFullAccess(grantee: iam.IGrantable) { + return this.grant(grantee, 'dynamodb:*'); + } + + protected abstract get hasIndex(): boolean; +} + /** * Provides a DynamoDB table. */ -export class Table extends Resource { +export class Table extends TableBase { /** * Permits an IAM Principal to list all DynamoDB Streams. * @deprecated Use {@link #grantTableListStreams} for more granular permission @@ -197,11 +357,89 @@ export class Table extends Resource { actions: ['dynamodb:ListStreams'], resourceArns: ['*'], }); - } + } + + /** + * Creates a Table construct that represents an external table via table name. + * + * @param scope The parent creating construct (usually `this`). + * @param id The construct's name. + * @param tableName The table's name. + */ + public static fromTableName(scope: Construct, id: string, tableName: string): ITable { + return Table.fromTableAttributes(scope, id, { tableName }); + } + + /** + * Creates a Table construct that represents an external table via table arn. + * + * @param scope The parent creating construct (usually `this`). + * @param id The construct's name. + * @param tableArn The table's ARN. + */ + public static fromTableArn(scope: Construct, id: string, tableArn: string): ITable { + return Table.fromTableAttributes(scope, id, { tableArn }); + } + + /** + * Creates a Table construct that represents an external table. + * + * @param scope The parent creating construct (usually `this`). + * @param id The construct's name. + * @param attrs A `TableAttributes` object. + */ + public static fromTableAttributes(scope: Construct, id: string, attrs: TableAttributes): ITable { + + class Import extends TableBase { + + public readonly tableName: string; + public readonly tableArn: string; + + constructor(_scope: Construct, _id: string, _tableArn: string, _tableName: string) { + super(_scope, _id); + this.tableArn = _tableArn; + this.tableName = _tableName; + } + + protected get hasIndex(): boolean { + return false; + } + + public grantTableListStreams(_grantee: iam.IGrantable): iam.Grant { + throw new Error("Method not implemented."); + } + + public grantStreamRead(_grantee: iam.IGrantable): iam.Grant { + throw new Error("Method not implemented."); + } + } + + let tableName: string; + let tableArn: string; + const stack = Stack.of(scope); + if (!attrs.tableName) { + if (!attrs.tableArn) { throw new Error('One of tableName or tableArn is required!'); } + + tableArn = attrs.tableArn; + const maybeTableName = stack.parseArn(attrs.tableArn).resourceName; + if (!maybeTableName) { throw new Error('ARN for DynamoDB table must be in the form: ...'); } + tableName = maybeTableName; + } else { + if (attrs.tableArn) { throw new Error("Only one of tableArn or tableName can be provided"); } + tableName = attrs.tableName; + tableArn = stack.formatArn({ + service: 'dynamodb', + resource: 'table', + resourceName: attrs.tableName, + }); + } + + return new Import(scope, id, tableArn, tableName); + } - /** - * @attribute - */ + /** + * @attribute + */ public readonly tableArn: string; /** @@ -280,6 +518,54 @@ export class Table extends Resource { } } + /** + * Adds an IAM policy statement associated with this table's stream to an + * IAM principal's policy. + * @param grantee The principal (no-op if undefined) + * @param actions The set of actions to allow (i.e. "dynamodb:DescribeStream", "dynamodb:GetRecords", ...) + */ + public grantStream(grantee: iam.IGrantable, ...actions: string[]): iam.Grant { + if (!this.tableStreamArn) { + throw new Error(`DynamoDB Streams must be enabled on the table ${this.node.path}`); + } + + return iam.Grant.addToPrincipal({ + grantee, + actions, + resourceArns: [this.tableStreamArn], + scope: this, + }); + } + + /** + * Permits an IAM Principal to list streams attached to current dynamodb table. + * + * @param grantee The principal (no-op if undefined) + */ + public grantTableListStreams(grantee: iam.IGrantable): iam.Grant { + if (!this.tableStreamArn) { + throw new Error(`DynamoDB Streams must be enabled on the table ${this.node.path}`); + } + return iam.Grant.addToPrincipal({ + grantee, + actions: ['dynamodb:ListStreams'], + resourceArns: [ + Lazy.stringValue({ produce: () => `${this.tableArn}/stream/*` }) + ], + }); + } + + /** + * Permits an IAM principal all stream data read operations for this + * table's stream: + * DescribeStream, GetRecords, GetShardIterator, ListStreams. + * @param grantee The principal to grant access to + */ + public grantStreamRead(grantee: iam.IGrantable): iam.Grant { + this.grantTableListStreams(grantee); + return this.grantStream(grantee, ...READ_STREAM_DATA_ACTIONS); + } + /** * Add a global secondary index of table. * @@ -428,108 +714,6 @@ export class Table extends Resource { }); } - /** - * Adds an IAM policy statement associated with this table to an IAM - * principal's policy. - * @param grantee The principal (no-op if undefined) - * @param actions The set of actions to allow (i.e. "dynamodb:PutItem", "dynamodb:GetItem", ...) - */ - public grant(grantee: iam.IGrantable, ...actions: string[]): iam.Grant { - return iam.Grant.addToPrincipal({ - grantee, - actions, - resourceArns: [ - this.tableArn, - Lazy.stringValue({ produce: () => this.hasIndex ? `${this.tableArn}/index/*` : Aws.NO_VALUE }) - ], - scope: this, - }); - } - - /** - * Adds an IAM policy statement associated with this table's stream to an - * IAM principal's policy. - * @param grantee The principal (no-op if undefined) - * @param actions The set of actions to allow (i.e. "dynamodb:DescribeStream", "dynamodb:GetRecords", ...) - */ - public grantStream(grantee: iam.IGrantable, ...actions: string[]): iam.Grant { - if (!this.tableStreamArn) { - throw new Error(`DynamoDB Streams must be enabled on the table ${this.node.path}`); - } - - return iam.Grant.addToPrincipal({ - grantee, - actions, - resourceArns: [this.tableStreamArn], - scope: this, - }); - } - - /** - * Permits an IAM principal all data read operations from this table: - * BatchGetItem, GetRecords, GetShardIterator, Query, GetItem, Scan. - * @param grantee The principal to grant access to - */ - public grantReadData(grantee: iam.IGrantable) { - return this.grant(grantee, ...READ_DATA_ACTIONS); - } - - /** - * Permits an IAM Principal to list streams attached to current dynamodb table. - * - * @param grantee The principal (no-op if undefined) - */ - public grantTableListStreams(grantee: iam.IGrantable): iam.Grant { - if (!this.tableStreamArn) { - throw new Error(`DynamoDB Streams must be enabled on the table ${this.node.path}`); - } - return iam.Grant.addToPrincipal({ - grantee, - actions: ['dynamodb:ListStreams'], - resourceArns: [ - Lazy.stringValue({ produce: () => `${this.tableArn}/stream/*`}) - ], - }); - } - - /** - * Permits an IAM principal all stream data read operations for this - * table's stream: - * DescribeStream, GetRecords, GetShardIterator, ListStreams. - * @param grantee The principal to grant access to - */ - public grantStreamRead(grantee: iam.IGrantable): iam.Grant { - this.grantTableListStreams(grantee); - return this.grantStream(grantee, ...READ_STREAM_DATA_ACTIONS); - } - - /** - * Permits an IAM principal all data write operations to this table: - * BatchWriteItem, PutItem, UpdateItem, DeleteItem. - * @param grantee The principal to grant access to - */ - public grantWriteData(grantee: iam.IGrantable) { - return this.grant(grantee, ...WRITE_DATA_ACTIONS); - } - - /** - * Permits an IAM principal to all data read/write operations to this table. - * BatchGetItem, GetRecords, GetShardIterator, Query, GetItem, Scan, - * BatchWriteItem, PutItem, UpdateItem, DeleteItem - * @param grantee The principal to grant access to - */ - public grantReadWriteData(grantee: iam.IGrantable) { - return this.grant(grantee, ...READ_DATA_ACTIONS, ...WRITE_DATA_ACTIONS); - } - - /** - * Permits all DynamoDB operations ("dynamodb:*") to an IAM principal. - * @param grantee The principal to grant access to - */ - public grantFullAccess(grantee: iam.IGrantable) { - return this.grant(grantee, 'dynamodb:*'); - } - /** * Validate the table construct. * @@ -553,7 +737,7 @@ export class Table extends Resource { * * @param props read and write capacity properties */ - private validateProvisioning(props: { readCapacity?: number, writeCapacity?: number}): void { + private validateProvisioning(props: { readCapacity?: number, writeCapacity?: number }): void { if (this.billingMode === BillingMode.PAY_PER_REQUEST) { if (props.readCapacity !== undefined || props.writeCapacity !== undefined) { throw new Error('you cannot provision read and write capacity for a table with PAY_PER_REQUEST billing mode'); @@ -686,7 +870,7 @@ export class Table extends Resource { /** * Whether this table has indexes */ - private get hasIndex(): boolean { + protected get hasIndex(): boolean { return this.globalSecondaryIndexes.length + this.localSecondaryIndexes.length > 0; } } @@ -740,4 +924,4 @@ export enum StreamViewType { interface ScalableAttributePair { scalableReadAttribute?: ScalableTableAttribute; scalableWriteAttribute?: ScalableTableAttribute; -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts b/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts index 62f3d0be8c7c8..59b1dd6a0c0fa 100644 --- a/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts +++ b/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts @@ -11,7 +11,7 @@ import { LocalSecondaryIndexProps, ProjectionType, StreamViewType, - Table + Table, } from '../lib'; // tslint:disable:object-literal-key-quotes @@ -1333,6 +1333,125 @@ export = { test.done(); } }, + + 'import': { + 'report error when importing an external/existing table from invalid arn missing resource name'(test: Test) { + const stack = new Stack(); + + const tableArn = 'arn:aws:dynamodb:us-east-1::table/'; + // WHEN + test.throws(() => Table.fromTableArn(stack, 'ImportedTable', tableArn), /ARN for DynamoDB table must be in the form: .../); + + test.done(); + }, + 'static import(ref) allows importing an external/existing table from arn'(test: Test) { + const stack = new Stack(); + + const tableArn = 'arn:aws:dynamodb:us-east-1:11111111:table/MyTable'; + const table = Table.fromTableArn(stack, 'ImportedTable', tableArn); + + const role = new iam.Role(stack, 'NewRole', { + assumedBy: new iam.ServicePrincipal('ecs-tasks.amazonaws.com'), + }); + table.grantReadData(role); + + // it is possible to obtain a permission statement for a ref + expect(stack).to(haveResource('AWS::IAM::Policy', { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + 'dynamodb:BatchGetItem', + 'dynamodb:GetRecords', + 'dynamodb:GetShardIterator', + 'dynamodb:Query', + 'dynamodb:GetItem', + 'dynamodb:Scan' + ], + "Effect": "Allow", + "Resource": [ + tableArn, + { "Ref": "AWS::NoValue" } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": 'NewRoleDefaultPolicy90E8F49D', + "Roles": [ { "Ref": 'NewRole99763075' } ] + })); + + test.deepEqual(table.tableArn, tableArn); + test.deepEqual(stack.resolve(table.tableName), 'MyTable'); + test.done(); + }, + 'static import(ref) allows importing an external/existing table from table name'(test: Test) { + const stack = new Stack(); + + const tableName = 'MyTable'; + const table = Table.fromTableName(stack, 'ImportedTable', tableName); + + const role = new iam.Role(stack, 'NewRole', { + assumedBy: new iam.ServicePrincipal('ecs-tasks.amazonaws.com'), + }); + table.grantReadWriteData(role); + + // it is possible to obtain a permission statement for a ref + expect(stack).to(haveResource('AWS::IAM::Policy', { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + 'dynamodb:BatchGetItem', + 'dynamodb:GetRecords', + 'dynamodb:GetShardIterator', + 'dynamodb:Query', + 'dynamodb:GetItem', + 'dynamodb:Scan', + 'dynamodb:BatchWriteItem', + 'dynamodb:PutItem', + 'dynamodb:UpdateItem', + 'dynamodb:DeleteItem' + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":dynamodb:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":table/MyTable" + ] + ] + }, + { + "Ref": "AWS::NoValue" + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": 'NewRoleDefaultPolicy90E8F49D', + "Roles": [ { "Ref": 'NewRole99763075' } ] + })); + + test.deepEqual(table.tableArn, 'arn:${Token[AWS::Partition.3]}:dynamodb:${Token[AWS::Region.4]}:${Token[AWS::AccountId.0]}:table/MyTable'); + test.deepEqual(stack.resolve(table.tableName), tableName); + test.done(); + }, + }, }; function testGrant(test: Test, expectedActions: string[], invocation: (user: iam.IPrincipal, table: Table) => void) { From 8523c90ce13422470fbdd5cc86eb1ec19373cfc4 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 27 Dec 2019 11:37:18 +0000 Subject: [PATCH 29/80] chore(deps-dev): bump @types/jest from 24.0.24 to 24.0.25 (#5559) --- packages/@aws-cdk/assert/package.json | 2 +- packages/@aws-cdk/cx-api/package.json | 2 +- packages/cdk-dasm/package.json | 2 +- packages/decdk/package.json | 2 +- tools/cdk-build-tools/package.json | 2 +- tools/cfn2ts/package.json | 2 +- yarn.lock | 8 ++++---- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/@aws-cdk/assert/package.json b/packages/@aws-cdk/assert/package.json index adb892536cddf..876a308f27438 100644 --- a/packages/@aws-cdk/assert/package.json +++ b/packages/@aws-cdk/assert/package.json @@ -26,7 +26,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/jest": "^24.0.24", + "@types/jest": "^24.0.25", "@types/nodeunit": "^0.0.30", "cdk-build-tools": "1.19.0", "nodeunit": "^0.11.3", diff --git a/packages/@aws-cdk/cx-api/package.json b/packages/@aws-cdk/cx-api/package.json index c207365e30763..5206884a234c4 100644 --- a/packages/@aws-cdk/cx-api/package.json +++ b/packages/@aws-cdk/cx-api/package.json @@ -60,7 +60,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/jest": "^24.0.24", + "@types/jest": "^24.0.25", "@types/semver": "^6.2.0", "cdk-build-tools": "1.19.0", "jest": "^24.9.0", diff --git a/packages/cdk-dasm/package.json b/packages/cdk-dasm/package.json index 505a493722d15..4316420032330 100644 --- a/packages/cdk-dasm/package.json +++ b/packages/cdk-dasm/package.json @@ -30,7 +30,7 @@ "yaml": "1.7.2" }, "devDependencies": { - "@types/jest": "^24.0.24", + "@types/jest": "^24.0.25", "@types/yaml": "1.2.0", "jest": "^24.9.0" }, diff --git a/packages/decdk/package.json b/packages/decdk/package.json index 3f164af723f3b..943dc67daa546 100644 --- a/packages/decdk/package.json +++ b/packages/decdk/package.json @@ -165,7 +165,7 @@ }, "devDependencies": { "@types/fs-extra": "^8.0.1", - "@types/jest": "^24.0.24", + "@types/jest": "^24.0.25", "@types/yaml": "1.2.0", "@types/yargs": "^13.0.4", "jest": "^24.9.0", diff --git a/tools/cdk-build-tools/package.json b/tools/cdk-build-tools/package.json index 19ebc357ff220..d6920b3ba11ea 100644 --- a/tools/cdk-build-tools/package.json +++ b/tools/cdk-build-tools/package.json @@ -32,7 +32,7 @@ "license": "Apache-2.0", "devDependencies": { "@types/fs-extra": "^8.0.1", - "@types/jest": "^24.0.24", + "@types/jest": "^24.0.25", "@types/yargs": "^13.0.4", "pkglint": "1.19.0" }, diff --git a/tools/cfn2ts/package.json b/tools/cfn2ts/package.json index 9c6faabbc4ea5..88e75d06a05ec 100644 --- a/tools/cfn2ts/package.json +++ b/tools/cfn2ts/package.json @@ -37,7 +37,7 @@ }, "devDependencies": { "@types/fs-extra": "^8.0.1", - "@types/jest": "^24.0.24", + "@types/jest": "^24.0.25", "@types/yargs": "^13.0.4", "cdk-build-tools": "1.19.0", "jest": "^24.9.0", diff --git a/yarn.lock b/yarn.lock index ebfc486b18014..61a3faf8f4125 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1251,10 +1251,10 @@ "@types/istanbul-lib-coverage" "*" "@types/istanbul-lib-report" "*" -"@types/jest@^24.0.24": - version "24.0.24" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.0.24.tgz#0f2f523dc77cc1bc6bef34eaf287ede887a73f05" - integrity sha512-vgaG968EDPSJPMunEDdZvZgvxYSmeH8wKqBlHSkBt1pV2XlLEVDzsj1ZhLuI4iG4Pv841tES61txSBF0obh4CQ== +"@types/jest@^24.0.25": + version "24.0.25" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.0.25.tgz#2aba377824ce040114aa906ad2cac2c85351360f" + integrity sha512-hnP1WpjN4KbGEK4dLayul6lgtys6FPz0UfxMeMQCv0M+sTnzN3ConfiO72jHgLxl119guHgI8gLqDOrRLsyp2g== dependencies: jest-diff "^24.3.0" From 55d74f541b54328caf940ca1b9b0b68126d48533 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 27 Dec 2019 12:08:12 +0000 Subject: [PATCH 30/80] chore(deps-dev): bump @types/node from 10.17.11 to 10.17.13 (#5560) --- packages/aws-cdk/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index cf665f02edeb9..6128cc859ce4d 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -50,7 +50,7 @@ "@types/jszip": "^3.1.6", "@types/minimatch": "^3.0.3", "@types/mockery": "^1.4.29", - "@types/node": "^10.17.11", + "@types/node": "^10.17.13", "@types/nodeunit": "^0.0.30", "@types/promptly": "^3.0.0", "@types/request": "^2.48.4", diff --git a/yarn.lock b/yarn.lock index 61a3faf8f4125..d644a8ca69137 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1302,10 +1302,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.12.tgz#529bc3e73dbb35dd9e90b0a1c83606a9d3264bdb" integrity sha512-MGuvYJrPU0HUwqF7LqvIj50RZUX23Z+m583KBygKYUZLlZ88n6w28XRNJRJgsHukLEnLz6w6SvxZoLgbr5wLqQ== -"@types/node@^10.17.11": - version "10.17.11" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.11.tgz#46ba035fb917b31c948280dbea22ab8838f386a4" - integrity sha512-dNd2pp8qTzzNLAs3O8nH3iU9DG9866KHq9L3ISPB7DOGERZN81nW/5/g/KzMJpCU8jrbCiMRBzV9/sCEdRosig== +"@types/node@^10.17.13": + version "10.17.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.13.tgz#ccebcdb990bd6139cd16e84c39dc2fb1023ca90c" + integrity sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg== "@types/nodeunit@^0.0.30": version "0.0.30" From 78b3909e6124c6e4e0697a642640077bf1c0066e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 27 Dec 2019 12:40:03 +0000 Subject: [PATCH 31/80] chore(deps-dev): bump fast-check from 1.20.1 to 1.21.0 (#5549) --- packages/@aws-cdk/app-delivery/package.json | 2 +- packages/@aws-cdk/aws-applicationautoscaling/package.json | 2 +- packages/@aws-cdk/aws-autoscaling-common/package.json | 2 +- packages/@aws-cdk/cloudformation-diff/package.json | 2 +- packages/@aws-cdk/core/package.json | 2 +- yarn.lock | 8 ++++---- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/@aws-cdk/app-delivery/package.json b/packages/@aws-cdk/app-delivery/package.json index 1a11a6a53c71a..c369e6c6ad524 100644 --- a/packages/@aws-cdk/app-delivery/package.json +++ b/packages/@aws-cdk/app-delivery/package.json @@ -55,7 +55,7 @@ "@types/nodeunit": "^0.0.30", "cdk-build-tools": "1.19.0", "cdk-integ-tools": "1.19.0", - "fast-check": "^1.20.1", + "fast-check": "^1.21.0", "nodeunit": "^0.11.3", "pkglint": "1.19.0" }, diff --git a/packages/@aws-cdk/aws-applicationautoscaling/package.json b/packages/@aws-cdk/aws-applicationautoscaling/package.json index ad5b58d00d09d..6852810150025 100644 --- a/packages/@aws-cdk/aws-applicationautoscaling/package.json +++ b/packages/@aws-cdk/aws-applicationautoscaling/package.json @@ -66,7 +66,7 @@ "@types/nodeunit": "^0.0.30", "cdk-build-tools": "1.19.0", "cfn2ts": "1.19.0", - "fast-check": "^1.20.1", + "fast-check": "^1.21.0", "nodeunit": "^0.11.3", "pkglint": "1.19.0" }, diff --git a/packages/@aws-cdk/aws-autoscaling-common/package.json b/packages/@aws-cdk/aws-autoscaling-common/package.json index be470faa4b409..7edd7b190e522 100644 --- a/packages/@aws-cdk/aws-autoscaling-common/package.json +++ b/packages/@aws-cdk/aws-autoscaling-common/package.json @@ -62,7 +62,7 @@ "@types/nodeunit": "^0.0.30", "cdk-build-tools": "1.19.0", "cdk-integ-tools": "1.19.0", - "fast-check": "^1.20.1", + "fast-check": "^1.21.0", "nodeunit": "^0.11.3", "pkglint": "1.19.0" }, diff --git a/packages/@aws-cdk/cloudformation-diff/package.json b/packages/@aws-cdk/cloudformation-diff/package.json index 2d24cfd072c05..24fa07090d670 100644 --- a/packages/@aws-cdk/cloudformation-diff/package.json +++ b/packages/@aws-cdk/cloudformation-diff/package.json @@ -40,7 +40,7 @@ "@types/string-width": "^4.0.1", "@types/table": "^4.0.7", "cdk-build-tools": "1.19.0", - "fast-check": "^1.20.1", + "fast-check": "^1.21.0", "nodeunit": "^0.11.3", "pkglint": "1.19.0" }, diff --git a/packages/@aws-cdk/core/package.json b/packages/@aws-cdk/core/package.json index 20ed3a2db8666..026db296ebce5 100644 --- a/packages/@aws-cdk/core/package.json +++ b/packages/@aws-cdk/core/package.json @@ -151,7 +151,7 @@ "@types/nodeunit": "^0.0.30", "cdk-build-tools": "1.19.0", "cfn2ts": "1.19.0", - "fast-check": "^1.20.1", + "fast-check": "^1.21.0", "lodash": "^4.17.15", "nodeunit": "^0.11.3", "pkglint": "1.19.0" diff --git a/yarn.lock b/yarn.lock index d644a8ca69137..cf284974185a2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3528,10 +3528,10 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= -fast-check@^1.20.1: - version "1.20.1" - resolved "https://registry.yarnpkg.com/fast-check/-/fast-check-1.20.1.tgz#71ababf12038c8f18648233161ef85ea13f2e09a" - integrity sha512-VyW+/5QPewaF7EUA6sDp2k6UkBvy8ZlIruXFSEnHJmql8vfc+3PtiP1klo+pvxua1H2mnFhc5xlW4d8nwJjKQg== +fast-check@^1.21.0: + version "1.21.0" + resolved "https://registry.yarnpkg.com/fast-check/-/fast-check-1.21.0.tgz#2bb4f7898197fa9a01a8d1078f1ea60c7eb94cb6" + integrity sha512-t9E+rNNV8DNSUqxvUwJ8DBAVm0qiGsVktvUXB3zUC9PTnNbYqGvGVycL6osOo+CRWH8nJYLk/fXynqdag2StdQ== dependencies: pure-rand "^1.7.0" tslib "^1.10.0" From 3cb2311b2c7bfa91088d52e95e324c5cb4fda849 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 27 Dec 2019 13:59:56 +0000 Subject: [PATCH 32/80] chore(deps): bump @typescript-eslint/eslint-plugin from 2.12.0 to 2.13.0 (#5535) --- tools/cdk-build-tools/package.json | 2 +- yarn.lock | 32 +++++------------------------- 2 files changed, 6 insertions(+), 28 deletions(-) diff --git a/tools/cdk-build-tools/package.json b/tools/cdk-build-tools/package.json index d6920b3ba11ea..a645bb3202f18 100644 --- a/tools/cdk-build-tools/package.json +++ b/tools/cdk-build-tools/package.json @@ -37,7 +37,7 @@ "pkglint": "1.19.0" }, "dependencies": { - "@typescript-eslint/eslint-plugin": "^2.12.0", + "@typescript-eslint/eslint-plugin": "^2.13.0", "@typescript-eslint/parser": "^2.13.0", "awslint": "1.19.0", "colors": "^1.4.0", diff --git a/yarn.lock b/yarn.lock index cf284974185a2..e0adcbd08af27 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1390,26 +1390,17 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^2.12.0": - version "2.12.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.12.0.tgz#0da7cbca7b24f4c6919e9eb31c704bfb126f90ad" - integrity sha512-1t4r9rpLuEwl3hgt90jY18wJHSyb0E3orVL3DaqwmpiSDHmHiSspVsvsFF78BJ/3NNG3qmeso836jpuBWYziAA== +"@typescript-eslint/eslint-plugin@^2.13.0": + version "2.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.13.0.tgz#57e933fe16a2fc66dbac059af0d6d85d921d748e" + integrity sha512-QoiANo0MMGNa8ej/yX3BrW5dZj5d8HYcKiM2fyYUlezECqn8Xc7T/e4EUdiGinn8jhBrn+9X47E9TWaaup3u1g== dependencies: - "@typescript-eslint/experimental-utils" "2.12.0" + "@typescript-eslint/experimental-utils" "2.13.0" eslint-utils "^1.4.3" functional-red-black-tree "^1.0.1" regexpp "^3.0.0" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@2.12.0": - version "2.12.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.12.0.tgz#e0a76ffb6293e058748408a191921e453c31d40d" - integrity sha512-jv4gYpw5N5BrWF3ntROvCuLe1IjRenLy5+U57J24NbPGwZFAjhnM45qpq0nDH1y/AZMb3Br25YiNVwyPbz6RkA== - dependencies: - "@types/json-schema" "^7.0.3" - "@typescript-eslint/typescript-estree" "2.12.0" - eslint-scope "^5.0.0" - "@typescript-eslint/experimental-utils@2.13.0": version "2.13.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.13.0.tgz#958614faa6f77599ee2b241740e0ea402482533d" @@ -1429,19 +1420,6 @@ "@typescript-eslint/typescript-estree" "2.13.0" eslint-visitor-keys "^1.1.0" -"@typescript-eslint/typescript-estree@2.12.0": - version "2.12.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.12.0.tgz#bd9e547ccffd17dfab0c3ab0947c80c8e2eb914c" - integrity sha512-rGehVfjHEn8Frh9UW02ZZIfJs6SIIxIu/K1bbci8rFfDE/1lQ8krIJy5OXOV3DVnNdDPtoiPOdEANkLMrwXbiQ== - dependencies: - debug "^4.1.1" - eslint-visitor-keys "^1.1.0" - glob "^7.1.6" - is-glob "^4.0.1" - lodash.unescape "4.0.1" - semver "^6.3.0" - tsutils "^3.17.1" - "@typescript-eslint/typescript-estree@2.13.0": version "2.13.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.13.0.tgz#a2e746867da772c857c13853219fced10d2566bc" From 06adb58f0d1b7c40c02eb9b7fc6d3c705b2b3fe5 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 27 Dec 2019 17:06:13 +0100 Subject: [PATCH 33/80] chore(deps): bump nyc from 14.1.1 to 15.0.0 (#5520) Bumps [nyc](https://github.com/istanbuljs/nyc) from 14.1.1 to 15.0.0. - [Release notes](https://github.com/istanbuljs/nyc/releases) - [Changelog](https://github.com/istanbuljs/nyc/blob/master/CHANGELOG.md) - [Commits](https://github.com/istanbuljs/nyc/compare/v14.1.1...v15.0.0) Signed-off-by: dependabot-preview[bot] --- packages/@aws-cdk/alexa-ask/.gitignore | 2 +- packages/@aws-cdk/alexa-ask/package.json | 10 +- packages/@aws-cdk/app-delivery/.gitignore | 2 +- packages/@aws-cdk/assert/.gitignore | 4 +- packages/@aws-cdk/assert/package.json | 36 +- ...{test.assertions.ts => assertions.test.ts} | 73 ++- .../assert/test/have-resource.test.ts | 98 ++++ .../@aws-cdk/assert/test/synth-utils.test.ts | 14 + .../assert/test/test.have-resource.ts | 107 ---- .../@aws-cdk/assert/test/test.synth-utils.ts | 18 - packages/@aws-cdk/assets/.gitignore | 4 +- .../@aws-cdk/aws-accessanalyzer/.gitignore | 2 +- .../@aws-cdk/aws-accessanalyzer/package.json | 10 +- packages/@aws-cdk/aws-amazonmq/.gitignore | 4 +- packages/@aws-cdk/aws-amazonmq/package.json | 10 +- packages/@aws-cdk/aws-amplify/.gitignore | 2 +- packages/@aws-cdk/aws-amplify/package.json | 10 +- packages/@aws-cdk/aws-apigateway/.gitignore | 4 +- .../aws-applicationautoscaling/.gitignore | 4 +- .../aws-applicationautoscaling/package.json | 4 + packages/@aws-cdk/aws-appmesh/.gitignore | 2 +- packages/@aws-cdk/aws-appstream/.gitignore | 2 +- packages/@aws-cdk/aws-appstream/package.json | 10 +- packages/@aws-cdk/aws-appsync/.gitignore | 4 +- packages/@aws-cdk/aws-appsync/package.json | 10 +- packages/@aws-cdk/aws-athena/.gitignore | 4 +- packages/@aws-cdk/aws-athena/package.json | 10 +- .../aws-autoscaling-common/.gitignore | 2 +- .../aws-autoscaling-hooktargets/.gitignore | 4 +- .../aws-autoscaling-hooktargets/package.json | 10 +- packages/@aws-cdk/aws-autoscaling/.gitignore | 4 +- .../@aws-cdk/aws-autoscalingplans/.gitignore | 4 +- .../aws-autoscalingplans/package.json | 10 +- packages/@aws-cdk/aws-backup/.gitignore | 2 +- packages/@aws-cdk/aws-backup/package.json | 10 +- packages/@aws-cdk/aws-batch/.gitignore | 4 +- packages/@aws-cdk/aws-batch/package.json | 10 +- packages/@aws-cdk/aws-budgets/.gitignore | 4 +- packages/@aws-cdk/aws-budgets/package.json | 10 +- .../aws-certificatemanager/.gitignore | 2 +- .../package.json | 12 +- packages/@aws-cdk/aws-cloud9/.gitignore | 4 +- packages/@aws-cdk/aws-cloud9/package.json | 10 +- .../@aws-cdk/aws-cloudformation/.gitignore | 4 +- packages/@aws-cdk/aws-cloudfront/.gitignore | 4 +- packages/@aws-cdk/aws-cloudfront/package.json | 2 +- packages/@aws-cdk/aws-cloudtrail/.gitignore | 4 +- packages/@aws-cdk/aws-cloudtrail/package.json | 2 +- .../aws-cloudwatch-actions/.gitignore | 4 +- .../aws-cloudwatch-actions/package.json | 10 +- packages/@aws-cdk/aws-cloudwatch/.gitignore | 4 +- packages/@aws-cdk/aws-codebuild/.gitignore | 4 +- packages/@aws-cdk/aws-codebuild/package.json | 2 +- packages/@aws-cdk/aws-codecommit/.gitignore | 4 +- packages/@aws-cdk/aws-codecommit/package.json | 2 +- packages/@aws-cdk/aws-codedeploy/.gitignore | 4 +- .../aws-codepipeline-actions/.gitignore | 4 +- packages/@aws-cdk/aws-codepipeline/.gitignore | 4 +- packages/@aws-cdk/aws-codestar/.gitignore | 2 +- packages/@aws-cdk/aws-codestar/package.json | 10 +- .../aws-codestarnotifications/.gitignore | 2 +- .../aws-codestarnotifications/package.json | 10 +- packages/@aws-cdk/aws-cognito/.gitignore | 4 +- packages/@aws-cdk/aws-config/.gitignore | 4 +- packages/@aws-cdk/aws-datapipeline/.gitignore | 4 +- .../@aws-cdk/aws-datapipeline/package.json | 10 +- packages/@aws-cdk/aws-dax/.gitignore | 4 +- packages/@aws-cdk/aws-dax/package.json | 10 +- .../@aws-cdk/aws-directoryservice/.gitignore | 4 +- .../aws-directoryservice/package.json | 10 +- packages/@aws-cdk/aws-dlm/.gitignore | 2 +- packages/@aws-cdk/aws-dlm/package.json | 10 +- packages/@aws-cdk/aws-dms/.gitignore | 4 +- packages/@aws-cdk/aws-dms/package.json | 10 +- packages/@aws-cdk/aws-docdb/.gitignore | 2 +- packages/@aws-cdk/aws-docdb/package.json | 10 +- .../@aws-cdk/aws-dynamodb-global/.gitignore | 2 +- .../aws-global-table-coordinator/package.json | 10 +- packages/@aws-cdk/aws-dynamodb/.gitignore | 4 +- packages/@aws-cdk/aws-ec2/.gitignore | 4 +- packages/@aws-cdk/aws-ecr-assets/.gitignore | 4 +- packages/@aws-cdk/aws-ecr/.gitignore | 4 +- packages/@aws-cdk/aws-ecs-patterns/.gitignore | 4 +- packages/@aws-cdk/aws-ecs/.gitignore | 2 +- packages/@aws-cdk/aws-efs/.gitignore | 4 +- packages/@aws-cdk/aws-efs/package.json | 10 +- packages/@aws-cdk/aws-eks/.gitignore | 4 +- packages/@aws-cdk/aws-elasticache/.gitignore | 4 +- .../@aws-cdk/aws-elasticache/package.json | 10 +- .../@aws-cdk/aws-elasticbeanstalk/.gitignore | 4 +- .../aws-elasticbeanstalk/package.json | 10 +- .../aws-elasticloadbalancing/.gitignore | 4 +- .../aws-elasticloadbalancing/package.json | 3 + .../.gitignore | 2 +- .../package.json | 8 +- .../aws-elasticloadbalancingv2/.gitignore | 4 +- .../@aws-cdk/aws-elasticsearch/.gitignore | 4 +- .../@aws-cdk/aws-elasticsearch/package.json | 10 +- packages/@aws-cdk/aws-emr/.gitignore | 4 +- packages/@aws-cdk/aws-emr/package.json | 10 +- .../@aws-cdk/aws-events-targets/.gitignore | 2 +- .../@aws-cdk/aws-events-targets/package.json | 12 +- packages/@aws-cdk/aws-events/.gitignore | 4 +- packages/@aws-cdk/aws-eventschemas/.gitignore | 2 +- .../@aws-cdk/aws-eventschemas/package.json | 10 +- packages/@aws-cdk/aws-fsx/.gitignore | 2 +- packages/@aws-cdk/aws-fsx/package.json | 10 +- packages/@aws-cdk/aws-gamelift/.gitignore | 4 +- packages/@aws-cdk/aws-gamelift/package.json | 10 +- packages/@aws-cdk/aws-glue/.gitignore | 4 +- packages/@aws-cdk/aws-greengrass/.gitignore | 2 +- packages/@aws-cdk/aws-greengrass/package.json | 10 +- packages/@aws-cdk/aws-guardduty/.gitignore | 4 +- packages/@aws-cdk/aws-guardduty/package.json | 10 +- packages/@aws-cdk/aws-iam/.gitignore | 4 +- packages/@aws-cdk/aws-iam/package.json | 10 +- packages/@aws-cdk/aws-inspector/.gitignore | 4 +- packages/@aws-cdk/aws-inspector/package.json | 10 +- packages/@aws-cdk/aws-iot/.gitignore | 4 +- packages/@aws-cdk/aws-iot/package.json | 10 +- packages/@aws-cdk/aws-iot1click/.gitignore | 4 +- packages/@aws-cdk/aws-iot1click/package.json | 10 +- packages/@aws-cdk/aws-iotanalytics/.gitignore | 2 +- .../@aws-cdk/aws-iotanalytics/package.json | 10 +- packages/@aws-cdk/aws-iotevents/.gitignore | 2 +- packages/@aws-cdk/aws-iotevents/package.json | 10 +- .../@aws-cdk/aws-iotthingsgraph/.gitignore | 2 +- .../@aws-cdk/aws-iotthingsgraph/package.json | 10 +- packages/@aws-cdk/aws-kinesis/.gitignore | 4 +- .../@aws-cdk/aws-kinesisanalytics/.gitignore | 4 +- .../aws-kinesisanalytics/package.json | 10 +- .../@aws-cdk/aws-kinesisfirehose/.gitignore | 4 +- .../@aws-cdk/aws-kinesisfirehose/package.json | 10 +- packages/@aws-cdk/aws-kms/.gitignore | 4 +- .../@aws-cdk/aws-lakeformation/.gitignore | 2 +- .../@aws-cdk/aws-lakeformation/package.json | 10 +- .../aws-lambda-destinations/.gitignore | 4 +- .../aws-lambda-destinations/package.json | 11 +- .../aws-lambda-event-sources/.gitignore | 4 +- packages/@aws-cdk/aws-lambda/.gitignore | 4 +- packages/@aws-cdk/aws-lambda/package.json | 2 +- .../@aws-cdk/aws-logs-destinations/.gitignore | 4 +- .../aws-logs-destinations/package.json | 10 +- packages/@aws-cdk/aws-logs/.gitignore | 4 +- .../@aws-cdk/aws-managedblockchain/.gitignore | 2 +- .../aws-managedblockchain/package.json | 10 +- packages/@aws-cdk/aws-mediaconvert/.gitignore | 2 +- .../@aws-cdk/aws-mediaconvert/package.json | 10 +- packages/@aws-cdk/aws-medialive/.gitignore | 2 +- packages/@aws-cdk/aws-medialive/package.json | 10 +- packages/@aws-cdk/aws-mediastore/.gitignore | 2 +- packages/@aws-cdk/aws-mediastore/package.json | 10 +- packages/@aws-cdk/aws-msk/.gitignore | 2 +- packages/@aws-cdk/aws-msk/package.json | 10 +- packages/@aws-cdk/aws-neptune/.gitignore | 4 +- packages/@aws-cdk/aws-neptune/package.json | 10 +- packages/@aws-cdk/aws-opsworks/.gitignore | 4 +- packages/@aws-cdk/aws-opsworks/package.json | 10 +- packages/@aws-cdk/aws-opsworkscm/.gitignore | 2 +- packages/@aws-cdk/aws-opsworkscm/package.json | 10 +- packages/@aws-cdk/aws-pinpoint/.gitignore | 2 +- packages/@aws-cdk/aws-pinpoint/package.json | 10 +- .../@aws-cdk/aws-pinpointemail/.gitignore | 2 +- .../@aws-cdk/aws-pinpointemail/package.json | 10 +- packages/@aws-cdk/aws-qldb/.gitignore | 2 +- packages/@aws-cdk/aws-qldb/package.json | 10 +- packages/@aws-cdk/aws-ram/.gitignore | 2 +- packages/@aws-cdk/aws-ram/package.json | 10 +- packages/@aws-cdk/aws-rds/.gitignore | 4 +- packages/@aws-cdk/aws-redshift/.gitignore | 4 +- packages/@aws-cdk/aws-redshift/package.json | 10 +- packages/@aws-cdk/aws-robomaker/.gitignore | 2 +- packages/@aws-cdk/aws-robomaker/package.json | 10 +- .../@aws-cdk/aws-route53-patterns/.gitignore | 4 +- .../aws-route53-patterns/package.json | 10 +- .../@aws-cdk/aws-route53-targets/.gitignore | 4 +- .../@aws-cdk/aws-route53-targets/package.json | 10 +- packages/@aws-cdk/aws-route53/.gitignore | 4 +- packages/@aws-cdk/aws-route53/package.json | 2 +- .../@aws-cdk/aws-route53resolver/.gitignore | 2 +- .../@aws-cdk/aws-route53resolver/package.json | 10 +- packages/@aws-cdk/aws-s3-assets/.gitignore | 4 +- .../@aws-cdk/aws-s3-deployment/.gitignore | 4 +- .../@aws-cdk/aws-s3-notifications/.gitignore | 4 +- .../aws-s3-notifications/package.json | 10 +- packages/@aws-cdk/aws-s3/.gitignore | 4 +- packages/@aws-cdk/aws-sagemaker/.gitignore | 4 +- packages/@aws-cdk/aws-sagemaker/package.json | 10 +- packages/@aws-cdk/aws-sam/.gitignore | 4 +- packages/@aws-cdk/aws-sam/package.json | 26 +- packages/@aws-cdk/aws-sam/test/sam.test.ts | 37 ++ packages/@aws-cdk/aws-sam/test/test.sam.ts | 42 -- packages/@aws-cdk/aws-sdb/.gitignore | 4 +- packages/@aws-cdk/aws-sdb/package.json | 10 +- .../@aws-cdk/aws-secretsmanager/.gitignore | 2 +- packages/@aws-cdk/aws-securityhub/.gitignore | 2 +- .../@aws-cdk/aws-securityhub/package.json | 10 +- .../@aws-cdk/aws-servicecatalog/.gitignore | 4 +- .../@aws-cdk/aws-servicecatalog/package.json | 10 +- .../@aws-cdk/aws-servicediscovery/.gitignore | 4 +- packages/@aws-cdk/aws-ses-actions/.gitignore | 4 +- .../@aws-cdk/aws-ses-actions/package.json | 10 +- packages/@aws-cdk/aws-ses/.gitignore | 4 +- .../@aws-cdk/aws-sns-subscriptions/.gitignore | 4 +- .../aws-sns-subscriptions/package.json | 10 +- packages/@aws-cdk/aws-sns/.gitignore | 4 +- packages/@aws-cdk/aws-sqs/.gitignore | 4 +- packages/@aws-cdk/aws-sqs/package.json | 2 +- packages/@aws-cdk/aws-ssm/.gitignore | 4 +- .../aws-stepfunctions-tasks/.gitignore | 2 +- .../aws-stepfunctions-tasks/package.json | 10 +- .../@aws-cdk/aws-stepfunctions/.gitignore | 4 +- packages/@aws-cdk/aws-transfer/.gitignore | 2 +- packages/@aws-cdk/aws-transfer/package.json | 10 +- packages/@aws-cdk/aws-waf/.gitignore | 4 +- packages/@aws-cdk/aws-waf/package.json | 10 +- packages/@aws-cdk/aws-wafregional/.gitignore | 4 +- .../@aws-cdk/aws-wafregional/package.json | 10 +- packages/@aws-cdk/aws-wafv2/.gitignore | 2 +- packages/@aws-cdk/aws-wafv2/package.json | 10 +- packages/@aws-cdk/aws-workspaces/.gitignore | 4 +- packages/@aws-cdk/aws-workspaces/package.json | 10 +- packages/@aws-cdk/cfnspec/.gitignore | 2 +- .../@aws-cdk/cloudformation-diff/.gitignore | 4 +- .../@aws-cdk/cloudformation-diff/package.json | 31 +- .../test/diff-template.test.ts | 323 ++++++++++++ .../test/iam/broadening.test.ts | 194 ++++++++ .../test/iam/detect-changes.test.ts | 335 +++++++++++++ .../test/iam/statement.test.ts | 214 ++++++++ .../test/iam/test.broadening.ts | 211 -------- .../test/iam/test.detect-changes.ts | 359 -------------- .../test/iam/test.statement.ts | 235 --------- .../test/network/detect-changes.test.ts | 76 +++ .../test/network/rule.test.ts | 107 ++++ .../test/network/test.detect-changes.ts | 81 --- .../test/network/test.rule.ts | 120 ----- .../test/render-intrinsics.test.ts | 50 ++ .../test/test.diff-template.ts | 338 ------------- .../test/test.render-intrinsics.ts | 101 ---- packages/@aws-cdk/core/.gitignore | 4 +- packages/@aws-cdk/custom-resources/.gitignore | 2 +- .../@aws-cdk/custom-resources/package.json | 10 +- packages/@aws-cdk/cx-api/.gitignore | 2 +- packages/@aws-cdk/cx-api/package.json | 8 +- packages/@aws-cdk/region-info/.gitignore | 2 +- packages/@aws-cdk/region-info/package.json | 7 +- packages/aws-cdk/.gitignore | 2 +- packages/aws-cdk/package.json | 2 +- tools/awslint/.gitignore | 2 +- tools/cdk-build-tools/bin/cdk-test.ts | 8 +- tools/cdk-build-tools/config/.gitignore | 1 + tools/cdk-build-tools/config/nyc.config.js | 31 ++ tools/cdk-build-tools/config/nycrc | 22 - tools/cdk-build-tools/package.json | 4 +- tools/cfn2ts/.gitignore | 4 +- tools/pkglint/lib/rules.ts | 8 +- yarn.lock | 464 +++++++++++++++++- 257 files changed, 2929 insertions(+), 2112 deletions(-) rename packages/@aws-cdk/assert/test/{test.assertions.ts => assertions.test.ts} (80%) create mode 100644 packages/@aws-cdk/assert/test/have-resource.test.ts create mode 100644 packages/@aws-cdk/assert/test/synth-utils.test.ts delete mode 100644 packages/@aws-cdk/assert/test/test.have-resource.ts delete mode 100644 packages/@aws-cdk/assert/test/test.synth-utils.ts create mode 100644 packages/@aws-cdk/aws-sam/test/sam.test.ts delete mode 100644 packages/@aws-cdk/aws-sam/test/test.sam.ts create mode 100644 packages/@aws-cdk/cloudformation-diff/test/diff-template.test.ts create mode 100644 packages/@aws-cdk/cloudformation-diff/test/iam/broadening.test.ts create mode 100644 packages/@aws-cdk/cloudformation-diff/test/iam/detect-changes.test.ts create mode 100644 packages/@aws-cdk/cloudformation-diff/test/iam/statement.test.ts delete mode 100644 packages/@aws-cdk/cloudformation-diff/test/iam/test.broadening.ts delete mode 100644 packages/@aws-cdk/cloudformation-diff/test/iam/test.detect-changes.ts delete mode 100644 packages/@aws-cdk/cloudformation-diff/test/iam/test.statement.ts create mode 100644 packages/@aws-cdk/cloudformation-diff/test/network/detect-changes.test.ts create mode 100644 packages/@aws-cdk/cloudformation-diff/test/network/rule.test.ts delete mode 100644 packages/@aws-cdk/cloudformation-diff/test/network/test.detect-changes.ts delete mode 100644 packages/@aws-cdk/cloudformation-diff/test/network/test.rule.ts create mode 100644 packages/@aws-cdk/cloudformation-diff/test/render-intrinsics.test.ts delete mode 100644 packages/@aws-cdk/cloudformation-diff/test/test.diff-template.ts delete mode 100644 packages/@aws-cdk/cloudformation-diff/test/test.render-intrinsics.ts create mode 100644 tools/cdk-build-tools/config/.gitignore create mode 100644 tools/cdk-build-tools/config/nyc.config.js delete mode 100644 tools/cdk-build-tools/config/nycrc diff --git a/packages/@aws-cdk/alexa-ask/.gitignore b/packages/@aws-cdk/alexa-ask/.gitignore index 7b20ed5f53385..95ee96981d6b4 100644 --- a/packages/@aws-cdk/alexa-ask/.gitignore +++ b/packages/@aws-cdk/alexa-ask/.gitignore @@ -6,7 +6,7 @@ .jsii .LAST_BUILD .LAST_PACKAGE -.nycrc +nyc.config.js .nyc_output coverage dist diff --git a/packages/@aws-cdk/alexa-ask/package.json b/packages/@aws-cdk/alexa-ask/package.json index 36084acf566e5..87ba87f815189 100644 --- a/packages/@aws-cdk/alexa-ask/package.json +++ b/packages/@aws-cdk/alexa-ask/package.json @@ -70,7 +70,13 @@ "branches": 60, "statements": 80 } - } + }, + "collectCoverage": true, + "coverageReporters": [ + "lcov", + "html", + "text-summary" + ] }, "license": "Apache-2.0", "devDependencies": { @@ -89,4 +95,4 @@ "node": ">= 10.3.0" }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/app-delivery/.gitignore b/packages/@aws-cdk/app-delivery/.gitignore index c49007df54187..e83cd3c5e30bb 100644 --- a/packages/@aws-cdk/app-delivery/.gitignore +++ b/packages/@aws-cdk/app-delivery/.gitignore @@ -3,7 +3,7 @@ dist .LAST_BUILD .jsii .nyc_output -.nycrc +nyc.config.js tsconfig.json *.js *.d.ts diff --git a/packages/@aws-cdk/assert/.gitignore b/packages/@aws-cdk/assert/.gitignore index 6cff5540e188a..eb142198f6493 100644 --- a/packages/@aws-cdk/assert/.gitignore +++ b/packages/@aws-cdk/assert/.gitignore @@ -7,6 +7,6 @@ dist .LAST_BUILD .nyc_output coverage -.nycrc +nyc.config.js .LAST_PACKAGE -*.snk \ No newline at end of file +*.snk diff --git a/packages/@aws-cdk/assert/package.json b/packages/@aws-cdk/assert/package.json index 876a308f27438..d485dc658a68f 100644 --- a/packages/@aws-cdk/assert/package.json +++ b/packages/@aws-cdk/assert/package.json @@ -14,10 +14,25 @@ "build+test+package": "npm run build+test && npm run package", "build+test": "npm run build && npm test" }, - "nyc": { - "statements": 40, - "lines": 40, - "branches": 30 + "jest": { + "collectCoverage": true, + "coverageReporters": [ + "lcov", + "html", + "text-summary" + ], + "coverageThreshold": { + "global": { + "statements": 80, + "lines": 80, + "branches": 60 + } + }, + "preset": "ts-jest", + "testMatch": [ + "**/__tests__/**/*.ts?(x)", + "**/?(*.)+(spec|test).ts?(x)" + ] }, "author": { "name": "Amazon Web Services", @@ -27,17 +42,18 @@ "license": "Apache-2.0", "devDependencies": { "@types/jest": "^24.0.25", - "@types/nodeunit": "^0.0.30", "cdk-build-tools": "1.19.0", - "nodeunit": "^0.11.3", - "pkglint": "1.19.0" + "jest": "^24.9.0", + "pkglint": "1.19.0", + "ts-jest": "^24.2.0" }, "dependencies": { "@aws-cdk/cloudformation-diff": "1.19.0", "@aws-cdk/core": "1.19.0", - "@aws-cdk/cx-api": "1.19.0", - "jest": "^24.9.0", - "source-map-support": "^0.5.16" + "@aws-cdk/cx-api": "1.19.0" + }, + "peerDependencies": { + "jest": "^24.9.0" }, "repository": { "url": "https://github.com/aws/aws-cdk.git", diff --git a/packages/@aws-cdk/assert/test/test.assertions.ts b/packages/@aws-cdk/assert/test/assertions.test.ts similarity index 80% rename from packages/@aws-cdk/assert/test/test.assertions.ts rename to packages/@aws-cdk/assert/test/assertions.test.ts index dcfa5a602505f..26e5cb1707c5f 100644 --- a/packages/@aws-cdk/assert/test/test.assertions.ts +++ b/packages/@aws-cdk/assert/test/assertions.test.ts @@ -1,37 +1,34 @@ -import 'source-map-support/register'; - import * as cdk from '@aws-cdk/core'; import * as cx from '@aws-cdk/cx-api'; -import { Test } from 'nodeunit'; -import { countResources, exist, expect, haveType, MatchStyle, matchTemplate } from '../lib/index'; +import { countResources, exist, expect as cdkExpect, haveType, MatchStyle, matchTemplate } from '../lib/index'; passingExample('expect at to have ', () => { const resourceType = 'Test::Resource'; const synthStack = synthesizedStack(stack => { new TestResource(stack, 'TestResource', { type: resourceType }); }); - expect(synthStack).at('/TestResource').to(haveType(resourceType)); + cdkExpect(synthStack).at('/TestResource').to(haveType(resourceType)); }); passingExample('expect non-synthesized stack at to have ', () => { const resourceType = 'Test::Resource'; const stack = new cdk.Stack(); new TestResource(stack, 'TestResource', { type: resourceType }); - expect(stack).at('/TestResource').to(haveType(resourceType)); + cdkExpect(stack).at('/TestResource').to(haveType(resourceType)); }); passingExample('expect at *not* to have ', () => { const resourceType = 'Test::Resource'; const synthStack = synthesizedStack(stack => { new TestResource(stack, 'TestResource', { type: resourceType }); }); - expect(synthStack).at('/TestResource').notTo(haveType('Foo::Bar')); + cdkExpect(synthStack).at('/TestResource').notTo(haveType('Foo::Bar')); }); passingExample('expect at to exist', () => { const resourceType = 'Test::Resource'; const synthStack = synthesizedStack(stack => { new TestResource(stack, 'TestResource', { type: resourceType }); }); - expect(synthStack).at('/TestResource').to(exist()); + cdkExpect(synthStack).at('/TestResource').to(exist()); }); passingExample('expect to match (exactly)