From ba582edb7fb5fc483ea6961e1023014eb8ec557d Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Thu, 7 Nov 2019 12:42:22 +0100 Subject: [PATCH 1/8] feat(ec2): support NAT instances, AMI lookups Add support for NAT instances (as opposed to NAT gateways) on VPCs. This change introduces the concept of a 'NAT provider', and provides two implementations out of the box: one for gateways, one for instances. Instances are not guarded against termination; a future implementation should use ASGs to make sure there are always instances running. To make it easier to pick the right AMI for the NAT instance, add an AMI context provider, which will look up AMIs available to the user. Fixes #4876. --- packages/@aws-cdk/aws-ec2/README.md | 32 +- packages/@aws-cdk/aws-ec2/lib/index.ts | 1 + .../@aws-cdk/aws-ec2/lib/machine-image.ts | 92 ++- packages/@aws-cdk/aws-ec2/lib/nat.ts | 218 +++++ packages/@aws-cdk/aws-ec2/lib/vpc.ts | 171 +++- .../aws-ec2/test/example.images.lit.ts | 16 +- .../integ.nat-instances.lit.expected.json | 576 +++++++++++++ .../aws-ec2/test/integ.nat-instances.lit.ts | 31 + .../aws-ec2/test/test.machine-image.ts | 35 +- packages/@aws-cdk/aws-ec2/test/test.vpc.ts | 57 +- .../lib/sdk-api-metadata.json | 772 ++++++++++++++++++ .../@aws-cdk/cx-api/lib/cloud-assembly.ts | 4 +- packages/@aws-cdk/cx-api/lib/context/ami.ts | 23 + packages/@aws-cdk/cx-api/lib/index.ts | 1 + packages/@aws-cdk/cx-api/lib/versioning.ts | 7 +- .../test/cloud-assembly-builder.test.ts | 12 + packages/aws-cdk/lib/context-providers/ami.ts | 54 ++ .../aws-cdk/lib/context-providers/index.ts | 2 + .../test/context-providers/test.amis.ts | 83 ++ tools/cdk-integ-tools/lib/integ-helpers.ts | 2 + 20 files changed, 2138 insertions(+), 51 deletions(-) create mode 100644 packages/@aws-cdk/aws-ec2/lib/nat.ts create mode 100644 packages/@aws-cdk/aws-ec2/test/integ.nat-instances.lit.expected.json create mode 100644 packages/@aws-cdk/aws-ec2/test/integ.nat-instances.lit.ts create mode 100644 packages/@aws-cdk/custom-resources/lib/sdk-api-metadata.json create mode 100644 packages/@aws-cdk/cx-api/lib/context/ami.ts create mode 100644 packages/aws-cdk/lib/context-providers/ami.ts create mode 100644 packages/aws-cdk/test/context-providers/test.amis.ts diff --git a/packages/@aws-cdk/aws-ec2/README.md b/packages/@aws-cdk/aws-ec2/README.md index ac1907d197f47..58d2731f62f34 100644 --- a/packages/@aws-cdk/aws-ec2/README.md +++ b/packages/@aws-cdk/aws-ec2/README.md @@ -86,6 +86,20 @@ itself to 2 Availability Zones. Therefore, to get the VPC to spread over 3 or more availability zones, you must specify the environment where the stack will be deployed. +### Using NAT instances + +By default, the `Vpc` construct will create NAT *gateways* for you, which +are managed by AWS. If you would prefer to use your own managed NAT +*instances* instead, specify a different value for the `natGatewayProvider` +property, as follows: + +[using NAT instances](test/integ.nat-instances.lit.ts) + +The construct will automatically search for the most recent NAT gateway AMI. +If you prefer to use a custom AMI, pass a `GenericLinuxImage` instance +for the instance's `machineImage` parameter and configure the right AMI ID +for the regions you want to deploy to. + ### Advanced Subnet Configuration If the default VPC configuration (public and private subnets spanning the @@ -341,19 +355,19 @@ fleet.connections.allowDefaultPortTo(rdsDatabase, 'Fleet can access database'); AMIs control the OS that gets launched when you start your EC2 instance. The EC2 library contains constructs to select the AMI you want to use. -Depending on the type of AMI, you select it a different way. - -The latest version of Amazon Linux and Microsoft Windows images are -selectable by instantiating one of these classes: +Depending on the type of AMI, you select it a different way. Here are some +examples of things you might want to use: [example of creating images](test/example.images.lit.ts) -> NOTE: The Amazon Linux images selected will be cached in your `cdk.json`, so that your -> AutoScalingGroups don't automatically change out from under you when you're making unrelated -> changes. To update to the latest version of Amazon Linux, remove the cache entry from the `context` -> section of your `cdk.json`. +> NOTE: The AMIs selected by `AmazonLinuxImage` or `LookupImage` will be cached in +> `cdk.context.json`, so that your AutoScalingGroup instances aren't replaced while +> you are making unrelated changes to your CDK app. > -> We will add command-line options to make this step easier in the future. +> To query for the latest AMI again, remove the relevant cache entry from +> `cdk.context.json`, or use the `cdk context` command. For more information, see +> [Runtime Context](https://docs.aws.amazon.com/cdk/latest/guide/context.html) in the CDK +> developer guide. ## VPN connections to a VPC diff --git a/packages/@aws-cdk/aws-ec2/lib/index.ts b/packages/@aws-cdk/aws-ec2/lib/index.ts index 3516e97586b9c..af52216f61464 100644 --- a/packages/@aws-cdk/aws-ec2/lib/index.ts +++ b/packages/@aws-cdk/aws-ec2/lib/index.ts @@ -3,6 +3,7 @@ export * from './connections'; export * from './instance-types'; export * from './instance'; export * from './machine-image'; +export * from './nat'; export * from './network-acl'; export * from './network-acl-types'; export * from './port'; diff --git a/packages/@aws-cdk/aws-ec2/lib/machine-image.ts b/packages/@aws-cdk/aws-ec2/lib/machine-image.ts index fe076034fe9f4..85aadaf0362c2 100644 --- a/packages/@aws-cdk/aws-ec2/lib/machine-image.ts +++ b/packages/@aws-cdk/aws-ec2/lib/machine-image.ts @@ -1,5 +1,6 @@ import ssm = require('@aws-cdk/aws-ssm'); -import { Construct, Stack, Token } from '@aws-cdk/core'; +import { Construct, ContextProvider, Stack, Token } from '@aws-cdk/core'; +import cxapi = require('@aws-cdk/cx-api'); import { UserData } from './user-data'; import { WindowsVersion } from './windows-versions'; @@ -230,7 +231,7 @@ export interface GenericLinuxImageProps { /** * Initial user data * - * @default - Empty UserData for Windows machines + * @default - Empty UserData for Linux machines */ readonly userData?: UserData; } @@ -311,3 +312,90 @@ export enum OperatingSystemType { LINUX, WINDOWS, } + +/** + * A machine image whose AMI ID will be searched using DescribeImages. + * + * The most recent, available, launchable image matching the given filter + * criteria will be used. Looking up AMIs may take a long time; specify + * as many filter criteria as possible to narrow down the search. + * + * The AMI selected will be cached in `cdk.context.json` and the same value + * will be used on future runs. To refresh the AMI lookup, you will have to + * evict the value from the cache using the `cdk context` command. See + * https://docs.aws.amazon.com/cdk/latest/guide/context.html for more information. + */ +export class LookupMachineImage implements IMachineImage { + constructor(private readonly props: LookupMachineImageProps) { + } + + public getImage(scope: Construct): MachineImageConfig { + // Need to know 'windows' or not before doing the query to return the right + // osType for the dummy value, so might as well add it to the filter. + const filters: Record = { + 'name': [this.props.name], + 'state': ['available'], + 'image-type': ['machine'], + 'platform': this.props.windows ? ['windows'] : undefined, + }; + Object.assign(filters, this.props.filters); + + const value = ContextProvider.getValue(scope, { + provider: cxapi.AMI_PROVIDER, + props: { + owners: this.props.owners, + filters, + } as cxapi.AmiContextQuery, + dummyValue: 'ami-1234', + }).value as cxapi.AmiContextResponse; + + if (typeof value !== 'string') { + throw new Error(`Response to AMI lookup invalid, got: ${value}`); + } + + return { + imageId: value, + osType: this.props.windows ? OperatingSystemType.WINDOWS : OperatingSystemType.LINUX, + userData: this.props.userData + }; + } +} + +/** + * Properties for looking up an image + */ +export interface LookupMachineImageProps { + /** + * Name of the image (may contain wildcards) + */ + name: string; + + /** + * Owner account IDs or aliases + * + * @default - All owners + */ + owners?: string[]; + + /** + * Additional filters on the AMI + * + * @see https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeImages.html + * @default - No additional filters + */ + filters?: {[key: string]: string[]}; + + /** + * Look for Windows images + * + * @default false + */ + windows?: boolean; + + /** + * Custom userdata for this image + * + * @default - Empty user data appropriate for the platform type + */ + userData?: UserData; +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2/lib/nat.ts b/packages/@aws-cdk/aws-ec2/lib/nat.ts new file mode 100644 index 0000000000000..8be0039c82235 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2/lib/nat.ts @@ -0,0 +1,218 @@ +import iam = require('@aws-cdk/aws-iam'); +import { Instance } from './instance'; +import { InstanceType } from './instance-types'; +import { IMachineImage, LookupMachineImage } from "./machine-image"; +import { Port } from './port'; +import { SecurityGroup } from './security-group'; +import { PrivateSubnet, PublicSubnet, RouterType, Vpc } from './vpc'; + +/** + * NAT providers + * + * Determines what type of NAT provider to create, either NAT gateways or NAT + * instance. + * + * @experimental + */ +export abstract class NatProvider { + /** + * Use NAT Gateways to provide NAT services for your VPC + * + * NAT gateways are managed by AWS. + * + * @see https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html + */ + public static gateway(): NatProvider { + return new NatGateway(); + } + + /** + * Use NAT instances to provide NAT services for your VPC + * + * NAT instances are managed by you, but in return allow more configuration. + * + * Be aware that instances created using this provider will not be + * automatically replaced if they are stopped for any reason. You should implement + * your own NatProvider based on AutoScaling groups if you need that. + * + * @see https://docs.aws.amazon.com/vpc/latest/userguide/VPC_NAT_Instance.html + */ + public static instance(props: NatInstanceProps): NatProvider { + return new NatInstance(props); + } + + /** + * Called by the VPC to configure NAT + */ + public abstract configureNat(options: ConfigureNatOptions): void; +} + +/** + * Options passed by the VPC when NAT needs to be configured + * + * @experimental + */ +export interface ConfigureNatOptions { + /** + * The VPC we're configuring NAT for + */ + vpc: Vpc; + + /** + * The public subnets where the NAT providers need to be placed + */ + natSubnets: PublicSubnet[]; + + /** + * The private subnets that need to route through the NAT providers. + * + * There may be more private subnets than public subnets with NAT providers. + */ + privateSubnets: PrivateSubnet[]; +} + +/** + * Properties for a NAT instance + * + * @experimental + */ +export interface NatInstanceProps { + /** + * The machine image (AMI) to use + * + * By default, will do an AMI lookup for the latest NAT instance image. + * + * If you have a specific AMI ID you want to use, pass a `GenericLinuxImage`. For example: + * + * ```ts + * NatProvider.instance({ + * instanceType: new InstanceType('t3.micro'), + * machineImage: new GenericLinuxImage({ + * 'us-east-2': 'ami-0f9c61b5a562a16af' + * }) + * }) + * ``` + * + * @default - Latest NAT instance image + */ + machineImage?: IMachineImage; + + /** + * Instance type of the NAT instance + */ + instanceType: InstanceType; + + /** + * Name of SSH keypair to grant access to instance + * + * @default - No SSH access will be possible. + */ + readonly keyName?: string; +} + +class NatGateway extends NatProvider { + public configureNat(options: ConfigureNatOptions) { + // Create the NAT gateways + const gatewayIds = new PrefSet(); + for (const sub of options.natSubnets) { + const gateway = sub.addNatGateway(); + gatewayIds.add(sub.availabilityZone, gateway.ref); + } + + // Add routes to them in the private subnets + for (const sub of options.privateSubnets) { + sub.addRoute('DefaultRoute', { + routerType: RouterType.NAT_GATEWAY, + routerId: gatewayIds.pick(sub.availabilityZone), + enablesInternetConnectivity: true, + }); + } + } +} + +class NatInstance extends NatProvider { + constructor(private readonly props: NatInstanceProps) { + super(); + } + + public configureNat(options: ConfigureNatOptions) { + // Create the NAT instances. They can share a security group and a Role. + const instances = new PrefSet(); + const machineImage = this.props.machineImage || new NatInstanceImage(); + const sg = new SecurityGroup(options.vpc, 'NatSecurityGroup', { + vpc: options.vpc, + description: 'Security Group for NAT instances', + }); + sg.connections.allowFromAnyIpv4(Port.allTcp()); + + // FIXME: Ideally, NAT instances don't have a role at all, but + // 'Instance' does not allow that right now. + const role = new iam.Role(options.vpc, 'NatRole', { + assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com') + }); + + for (const sub of options.natSubnets) { + const natInstance = new Instance(sub, 'NatInstance', { + instanceType: this.props.instanceType, + machineImage, + sourceDestCheck: false, // Required for NAT + vpc: options.vpc, + vpcSubnets: { subnets: [sub] }, + securityGroup: sg, + role, + keyName: this.props.keyName + }); + // NAT instance routes all traffic, both ways + instances.add(sub.availabilityZone, natInstance); + } + + // Add routes to them in the private subnets + for (const sub of options.privateSubnets) { + sub.addRoute('DefaultRoute', { + routerType: RouterType.INSTANCE, + routerId: instances.pick(sub.availabilityZone).instanceId, + enablesInternetConnectivity: true, + }); + } + } +} + +/** + * Preferential set + * + * Picks the value with the given key if available, otherwise distributes + * evenly among the available options. + */ +class PrefSet { + private readonly map: Record = {}; + private readonly vals = new Array(); + private next: number = 0; + + public add(pref: string, value: A) { + this.map[pref] = value; + this.vals.push(value); + } + + public pick(pref: string): A { + if (this.vals.length === 0) { + throw new Error('Cannot pick, set is empty'); + } + + if (pref in this.map) { return this.map[pref]; } + return this.vals[this.next++ % this.vals.length]; + } +} + +/** + * Machine image representing the latest NAT instance image + * + * @experimental + */ +export class NatInstanceImage extends LookupMachineImage { + constructor() { + super({ + name: 'amzn-ami-vpc-nat-*', + owners: ['amazon'], + }); + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2/lib/vpc.ts b/packages/@aws-cdk/aws-ec2/lib/vpc.ts index db3a590d171b4..ba43af3fd88c0 100644 --- a/packages/@aws-cdk/aws-ec2/lib/vpc.ts +++ b/packages/@aws-cdk/aws-ec2/lib/vpc.ts @@ -4,6 +4,7 @@ import cxapi = require('@aws-cdk/cx-api'); import { CfnEIP, CfnInternetGateway, CfnNatGateway, CfnRoute, CfnRouteTable, CfnSubnet, CfnSubnetRouteTableAssociation, CfnVPC, CfnVPCGatewayAttachment, CfnVPNGateway, CfnVPNGatewayRoutePropagation } from './ec2.generated'; +import { NatProvider } from './nat'; import { INetworkAcl, NetworkAcl, SubnetNetworkAclAssociation } from './network-acl'; import { NetworkBuilder } from './network-util'; import { allRouteTableIds, defaultSubnetName, ImportSubnetGroup, subnetGroupNameFromConstructId, subnetId } from './util'; @@ -291,6 +292,8 @@ abstract class VpcBase extends Resource implements IVpc { /** * Dependencies for NAT connectivity + * + * @deprecated - This value is no longer used. */ protected readonly natDependencies = new Array(); @@ -606,18 +609,21 @@ export interface VpcProps { readonly maxAzs?: number; /** - * The number of NAT Gateways to create. + * The number of NAT Gateways/Instances to create. + * + * The type of NAT gateway or instance will be determined by the + * `natGatewayProvider` parameter. * * You can set this number lower than the number of Availability Zones in your - * VPC in order to save on NAT gateway cost. Be aware you may be charged for + * VPC in order to save on NAT cost. Be aware you may be charged for * cross-AZ data traffic instead. * - * @default - One NAT gateway per Availability Zone + * @default - One NAT gateway/instance per Availability Zone */ readonly natGateways?: number; /** - * Configures the subnets which will have NAT Gateways + * Configures the subnets which will have NAT Gateways/Instances * * You can pick a specific group of subnets by specifying the group name; * the picked subnets must be public subnets. @@ -628,6 +634,17 @@ export interface VpcProps { */ readonly natGatewaySubnets?: SubnetSelection; + /** + * What type of NAT provider to use + * + * Select between NAT gateways or NAT instances. NAT gateways + * may not be available in all AWS regions. + * + * @default - NatProvider.gateway() + * @experimental + */ + readonly natGatewayProvider?: NatProvider; + /** * Configure the subnets to build for each AZ * @@ -938,11 +955,6 @@ export class Vpc extends VpcBase { */ private networkBuilder: NetworkBuilder; - /** - * Mapping of NatGateway by AZ - */ - private natGatewayByAZ: { [az: string]: string } = {}; - /** * Subnet configurations for this VPC */ @@ -1028,17 +1040,8 @@ export class Vpc extends VpcBase { // if gateways are needed create them if (natGatewayCount > 0) { - this.createNatGateways(natGatewayCount, natGatewayPlacement); - - (this.privateSubnets as PrivateSubnet[]).forEach((privateSubnet, i) => { - let ngwId = this.natGatewayByAZ[privateSubnet.availabilityZone]; - if (ngwId === undefined) { - const ngwArray = Array.from(Object.values(this.natGatewayByAZ)); - // round robin the available NatGW since one is not in your AZ - ngwId = ngwArray[i % ngwArray.length]; - } - privateSubnet.addDefaultNatRoute(ngwId); - }); + const provider = props.natGatewayProvider || NatProvider.gateway(); + this.createNatGateways(provider, natGatewayCount, natGatewayPlacement); } } @@ -1115,20 +1118,19 @@ export class Vpc extends VpcBase { }); } - private createNatGateways(natCount: number, placement: SubnetSelection): void { - let natSubnets: PublicSubnet[] = this.selectSubnetObjects(placement) as PublicSubnet[]; + private createNatGateways(provider: NatProvider, natCount: number, placement: SubnetSelection): void { + const natSubnets: PublicSubnet[] = this.selectSubnetObjects(placement) as PublicSubnet[]; for (const sub of natSubnets) { if (this.publicSubnets.indexOf(sub) === -1) { throw new Error(`natGatewayPlacement ${placement} contains non public subnet ${sub}`); } } - natSubnets = natSubnets.slice(0, natCount); - for (const sub of natSubnets) { - const gateway = sub.addNatGateway(); - this.natGatewayByAZ[sub.availabilityZone] = gateway.ref; - this.natDependencies.push(gateway); - } + provider.configureNat({ + vpc: this, + natSubnets: natSubnets.slice(0, natCount), + privateSubnets: this.privateSubnets as PrivateSubnet[] + }); } /** @@ -1376,12 +1378,31 @@ export class Subnet extends Resource implements ISubnet { * @param natGatewayId The ID of the NAT gateway */ public addDefaultNatRoute(natGatewayId: string) { - const route = new CfnRoute(this, `DefaultRoute`, { + this.addRoute('DefaultRoute', { + routerType: RouterType.NAT_GATEWAY, + routerId: natGatewayId, + enablesInternetConnectivity: true, + }); + } + + /** + * Adds an entry to this subnets route table + */ + public addRoute(id: string, options: AddRouteOptions) { + if (options.destinationCidrBlock && options.destinationIpv6CidrBlock) { + throw new Error(`Cannot specify both 'destinationCidrBlock' and 'destinationIpv6CidrBlock'`); + } + + const route = new CfnRoute(this, id, { routeTableId: this.routeTable.routeTableId, - destinationCidrBlock: '0.0.0.0/0', - natGatewayId + destinationCidrBlock: options.destinationCidrBlock || (options.destinationIpv6CidrBlock === undefined ? '0.0.0.0/0' : undefined), + destinationIpv6CidrBlock: options.destinationCidrBlock, + [routerTypeToPropName(options.routerType)]: options.routerId, }); - this._internetConnectivityEstablished.add(route); + + if (options.enablesInternetConnectivity) { + this._internetConnectivityEstablished.add(route); + } } public associateNetworkAcl(id: string, networkAcl: INetworkAcl) { @@ -1396,12 +1417,98 @@ export class Subnet extends Resource implements ISubnet { } } +/** + * Options for adding a new route to a subnet + */ +export interface AddRouteOptions { + /** + * IPv4 range this route applies to + * + * @default '0.0.0.0/0' + */ + destinationCidrBlock?: string; + + /** + * IPv6 range this route applies to + * + * @default - Uses IPv6 + */ + destinationIpv6CidrBlock?: string; + + /** + * What type of router to route this traffic to + */ + routerType: RouterType; + + /** + * The ID of the router + * + * Can be an instance ID, gateway ID, etc, depending on the router type. + */ + routerId: string; + + /** + * Whether this route will enable internet connectivity + * + * If true, this route will be added before any AWS resources that depend + * on internet connectivity in the VPC will be created. + */ + enablesInternetConnectivity?: boolean; +} + +/** + * Type of router used in route + */ +export enum RouterType { + /** + * Egress-only Internet Gateway + */ + EGRESS_ONLY_INTERNET_GATEWAY = 'EgressOnlyInternetGateway', + + /** + * Internet Gateway + */ + GATEWAY = 'Gateway', + + /** + * Instance + */ + INSTANCE = 'Instance', + + /** + * NAT Gateway + */ + NAT_GATEWAY = 'NatGateway', + + /** + * Network Interface + */ + NETWORK_INTERFACE = 'NetworkInterface', + + /** + * VPC peering connection + */ + VPC_PEERING_CONNECTION = 'VpcPeeringConnection', +} + +function routerTypeToPropName(routerType: RouterType) { + return ({ + [RouterType.EGRESS_ONLY_INTERNET_GATEWAY]: 'egressOnlyInternetGatewayId', + [RouterType.GATEWAY]: 'gatewayId', + [RouterType.INSTANCE]: 'instanceId', + [RouterType.NAT_GATEWAY]: 'natGatewayId', + [RouterType.NETWORK_INTERFACE]: 'networkInterfaceId', + [RouterType.VPC_PEERING_CONNECTION]: 'vpcPeeringConnectionId', + })[routerType]; +} + // tslint:disable-next-line:no-empty-interface export interface PublicSubnetProps extends SubnetProps { } export interface IPublicSubnet extends ISubnet { } + export interface PublicSubnetAttributes extends SubnetAttributes { } /** diff --git a/packages/@aws-cdk/aws-ec2/test/example.images.lit.ts b/packages/@aws-cdk/aws-ec2/test/example.images.lit.ts index 6214a61627fb3..80a318c8225f8 100644 --- a/packages/@aws-cdk/aws-ec2/test/example.images.lit.ts +++ b/packages/@aws-cdk/aws-ec2/test/example.images.lit.ts @@ -1,9 +1,7 @@ import ec2 = require("../lib"); +import { LookupMachineImage } from "../lib"; /// !show -// Pick a Windows edition to use -const windows = new ec2.WindowsImage(ec2.WindowsVersion.WINDOWS_SERVER_2019_ENGLISH_FULL_BASE); - // Pick the right Amazon Linux edition. All arguments shown are optional // and will default to these values when omitted. const amznLinux = new ec2.AmazonLinuxImage({ @@ -13,6 +11,17 @@ const amznLinux = new ec2.AmazonLinuxImage({ storage: ec2.AmazonLinuxStorage.GENERAL_PURPOSE, }); +// Pick a Windows edition to use +const windows = new ec2.WindowsImage(ec2.WindowsVersion.WINDOWS_SERVER_2019_ENGLISH_FULL_BASE); + +// Look up the most recent image matching a set of AMI filters. +// In this case, look up the NAT instance AMI, by using a wildcard +// in the 'name' field: +const natAmi = new LookupMachineImage({ + name: 'amzn-ami-vpc-nat-*', + owners: ['amazon'], +}); + // For other custom (Linux) images, instantiate a `GenericLinuxImage` with // a map giving the AMI to in for each region: @@ -36,3 +45,4 @@ Array.isArray(windows); Array.isArray(amznLinux); Array.isArray(linux); Array.isArray(genericWindows); +Array.isArray(natAmi); diff --git a/packages/@aws-cdk/aws-ec2/test/integ.nat-instances.lit.expected.json b/packages/@aws-cdk/aws-ec2/test/integ.nat-instances.lit.expected.json new file mode 100644 index 0000000000000..62344c06aa839 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2/test/integ.nat-instances.lit.expected.json @@ -0,0 +1,576 @@ +{ + "Resources": { + "MyVpcF9F0CA6F": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-nat-instances/MyVpc" + } + ] + } + }, + "MyVpcPublicSubnet1SubnetF6608456": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.0.0/19", + "VpcId": { + "Ref": "MyVpcF9F0CA6F" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-nat-instances/MyVpc/PublicSubnet1" + }, + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + } + ] + } + }, + "MyVpcPublicSubnet1RouteTableC46AB2F4": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "MyVpcF9F0CA6F" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-nat-instances/MyVpc/PublicSubnet1" + } + ] + } + }, + "MyVpcPublicSubnet1RouteTableAssociation2ECEE1CB": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "MyVpcPublicSubnet1RouteTableC46AB2F4" + }, + "SubnetId": { + "Ref": "MyVpcPublicSubnet1SubnetF6608456" + } + } + }, + "MyVpcPublicSubnet1DefaultRoute95FDF9EB": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "MyVpcPublicSubnet1RouteTableC46AB2F4" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "MyVpcIGW5C4A4F63" + } + }, + "DependsOn": [ + "MyVpcVPCGW488ACE0D" + ] + }, + "MyVpcPublicSubnet1NatInstanceInstanceProfile2FD934CB": { + "Type": "AWS::IAM::InstanceProfile", + "Properties": { + "Roles": [ + { + "Ref": "MyVpcNatRoleF1616EE9" + } + ] + } + }, + "MyVpcPublicSubnet1NatInstance8E94E5F7": { + "Type": "AWS::EC2::Instance", + "Properties": { + "AvailabilityZone": "test-region-1a", + "IamInstanceProfile": { + "Ref": "MyVpcPublicSubnet1NatInstanceInstanceProfile2FD934CB" + }, + "ImageId": "ami-1234", + "InstanceType": "t3.small", + "SecurityGroupIds": [ + { + "Fn::GetAtt": [ + "MyVpcNatSecurityGroupAA76397E", + "GroupId" + ] + } + ], + "SourceDestCheck": false, + "SubnetId": { + "Ref": "MyVpcPublicSubnet1SubnetF6608456" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-nat-instances/MyVpc/PublicSubnet1/NatInstance" + } + ], + "UserData": { + "Fn::Base64": "#!/bin/bash" + } + }, + "DependsOn": [ + "MyVpcNatRoleF1616EE9" + ] + }, + "MyVpcPublicSubnet2Subnet492B6BFB": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.32.0/19", + "VpcId": { + "Ref": "MyVpcF9F0CA6F" + }, + "AvailabilityZone": "test-region-1b", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-nat-instances/MyVpc/PublicSubnet2" + }, + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + } + ] + } + }, + "MyVpcPublicSubnet2RouteTable1DF17386": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "MyVpcF9F0CA6F" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-nat-instances/MyVpc/PublicSubnet2" + } + ] + } + }, + "MyVpcPublicSubnet2RouteTableAssociation227DE78D": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "MyVpcPublicSubnet2RouteTable1DF17386" + }, + "SubnetId": { + "Ref": "MyVpcPublicSubnet2Subnet492B6BFB" + } + } + }, + "MyVpcPublicSubnet2DefaultRoute052936F6": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "MyVpcPublicSubnet2RouteTable1DF17386" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "MyVpcIGW5C4A4F63" + } + }, + "DependsOn": [ + "MyVpcVPCGW488ACE0D" + ] + }, + "MyVpcPublicSubnet2NatInstanceInstanceProfile5AB09EF6": { + "Type": "AWS::IAM::InstanceProfile", + "Properties": { + "Roles": [ + { + "Ref": "MyVpcNatRoleF1616EE9" + } + ] + } + }, + "MyVpcPublicSubnet2NatInstance04BCE4E3": { + "Type": "AWS::EC2::Instance", + "Properties": { + "AvailabilityZone": "test-region-1b", + "IamInstanceProfile": { + "Ref": "MyVpcPublicSubnet2NatInstanceInstanceProfile5AB09EF6" + }, + "ImageId": "ami-1234", + "InstanceType": "t3.small", + "SecurityGroupIds": [ + { + "Fn::GetAtt": [ + "MyVpcNatSecurityGroupAA76397E", + "GroupId" + ] + } + ], + "SourceDestCheck": false, + "SubnetId": { + "Ref": "MyVpcPublicSubnet2Subnet492B6BFB" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-nat-instances/MyVpc/PublicSubnet2/NatInstance" + } + ], + "UserData": { + "Fn::Base64": "#!/bin/bash" + } + }, + "DependsOn": [ + "MyVpcNatRoleF1616EE9" + ] + }, + "MyVpcPublicSubnet3Subnet57EEE236": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.64.0/19", + "VpcId": { + "Ref": "MyVpcF9F0CA6F" + }, + "AvailabilityZone": "test-region-1c", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-nat-instances/MyVpc/PublicSubnet3" + }, + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + } + ] + } + }, + "MyVpcPublicSubnet3RouteTable15028F08": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "MyVpcF9F0CA6F" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-nat-instances/MyVpc/PublicSubnet3" + } + ] + } + }, + "MyVpcPublicSubnet3RouteTableAssociation5C27DDA4": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "MyVpcPublicSubnet3RouteTable15028F08" + }, + "SubnetId": { + "Ref": "MyVpcPublicSubnet3Subnet57EEE236" + } + } + }, + "MyVpcPublicSubnet3DefaultRoute3A83AB36": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "MyVpcPublicSubnet3RouteTable15028F08" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "MyVpcIGW5C4A4F63" + } + }, + "DependsOn": [ + "MyVpcVPCGW488ACE0D" + ] + }, + "MyVpcPrivateSubnet1Subnet5057CF7E": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.96.0/19", + "VpcId": { + "Ref": "MyVpcF9F0CA6F" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-nat-instances/MyVpc/PrivateSubnet1" + }, + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + } + ] + } + }, + "MyVpcPrivateSubnet1RouteTable8819E6E2": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "MyVpcF9F0CA6F" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-nat-instances/MyVpc/PrivateSubnet1" + } + ] + } + }, + "MyVpcPrivateSubnet1RouteTableAssociation56D38C7E": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "MyVpcPrivateSubnet1RouteTable8819E6E2" + }, + "SubnetId": { + "Ref": "MyVpcPrivateSubnet1Subnet5057CF7E" + } + } + }, + "MyVpcPrivateSubnet1DefaultRouteA8CDE2FA": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "MyVpcPrivateSubnet1RouteTable8819E6E2" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "InstanceId": { + "Ref": "MyVpcPublicSubnet1NatInstance8E94E5F7" + } + } + }, + "MyVpcPrivateSubnet2Subnet0040C983": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.128.0/19", + "VpcId": { + "Ref": "MyVpcF9F0CA6F" + }, + "AvailabilityZone": "test-region-1b", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-nat-instances/MyVpc/PrivateSubnet2" + }, + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + } + ] + } + }, + "MyVpcPrivateSubnet2RouteTableCEDCEECE": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "MyVpcF9F0CA6F" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-nat-instances/MyVpc/PrivateSubnet2" + } + ] + } + }, + "MyVpcPrivateSubnet2RouteTableAssociation86A610DA": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "MyVpcPrivateSubnet2RouteTableCEDCEECE" + }, + "SubnetId": { + "Ref": "MyVpcPrivateSubnet2Subnet0040C983" + } + } + }, + "MyVpcPrivateSubnet2DefaultRoute9CE96294": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "MyVpcPrivateSubnet2RouteTableCEDCEECE" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "InstanceId": { + "Ref": "MyVpcPublicSubnet2NatInstance04BCE4E3" + } + } + }, + "MyVpcPrivateSubnet3Subnet772D6AD7": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.160.0/19", + "VpcId": { + "Ref": "MyVpcF9F0CA6F" + }, + "AvailabilityZone": "test-region-1c", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-nat-instances/MyVpc/PrivateSubnet3" + }, + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + } + ] + } + }, + "MyVpcPrivateSubnet3RouteTableB790927C": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "MyVpcF9F0CA6F" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-nat-instances/MyVpc/PrivateSubnet3" + } + ] + } + }, + "MyVpcPrivateSubnet3RouteTableAssociationD951741C": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "MyVpcPrivateSubnet3RouteTableB790927C" + }, + "SubnetId": { + "Ref": "MyVpcPrivateSubnet3Subnet772D6AD7" + } + } + }, + "MyVpcPrivateSubnet3DefaultRouteEC11C0C5": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "MyVpcPrivateSubnet3RouteTableB790927C" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "InstanceId": { + "Ref": "MyVpcPublicSubnet1NatInstance8E94E5F7" + } + } + }, + "MyVpcIGW5C4A4F63": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-nat-instances/MyVpc" + } + ] + } + }, + "MyVpcVPCGW488ACE0D": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "VpcId": { + "Ref": "MyVpcF9F0CA6F" + }, + "InternetGatewayId": { + "Ref": "MyVpcIGW5C4A4F63" + } + } + }, + "MyVpcNatSecurityGroupAA76397E": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "Security Group for NAT instances", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "SecurityGroupIngress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "from 0.0.0.0/0:ALL PORTS", + "FromPort": 0, + "IpProtocol": "tcp", + "ToPort": 65535 + } + ], + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-nat-instances/MyVpc" + } + ], + "VpcId": { + "Ref": "MyVpcF9F0CA6F" + } + } + }, + "MyVpcNatRoleF1616EE9": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": { + "Fn::Join": [ + "", + [ + "ec2.", + { + "Ref": "AWS::URLSuffix" + } + ] + ] + } + } + } + ], + "Version": "2012-10-17" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-nat-instances/MyVpc" + } + ] + } + } + } +} diff --git a/packages/@aws-cdk/aws-ec2/test/integ.nat-instances.lit.ts b/packages/@aws-cdk/aws-ec2/test/integ.nat-instances.lit.ts new file mode 100644 index 0000000000000..af676d5a53931 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2/test/integ.nat-instances.lit.ts @@ -0,0 +1,31 @@ +import cdk = require('@aws-cdk/core'); +import ec2 = require('../lib'); + +class NatInstanceStack extends cdk.Stack { + constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + /// !show + // Configure the `natGatewayProvider` when defining a Vpc + const vpc = new ec2.Vpc(this, 'MyVpc', { + natGatewayProvider: ec2.NatProvider.instance({ + instanceType: new ec2.InstanceType('t3.small') + }), + + // The 'natGateways' parameter now controls the number of NAT instances + natGateways: 2, + }); + /// !hide + + Array.isArray(vpc); + } +} + +const app = new cdk.App(); +new NatInstanceStack(app, 'aws-cdk-vpc-nat-instances', { + env: { + account: process.env.CDK_INTEG_ACCOUNT || process.env.CDK_DEFAULT_ACCOUNT, + region: process.env.CDK_INTEG_REGION || process.env.CDK_DEFAULT_REGION, + } +}); +app.synth(); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2/test/test.machine-image.ts b/packages/@aws-cdk/aws-ec2/test/test.machine-image.ts index 85d3d9dfac8b7..5f6b57f8335e8 100644 --- a/packages/@aws-cdk/aws-ec2/test/test.machine-image.ts +++ b/packages/@aws-cdk/aws-ec2/test/test.machine-image.ts @@ -1,6 +1,7 @@ -import { Stack } from '@aws-cdk/core'; +import { App, Stack } from '@aws-cdk/core'; import { Test } from 'nodeunit'; import ec2 = require('../lib'); +import { LookupMachineImage } from '../lib'; export = { 'can make and use a Windows image'(test: Test) { @@ -43,4 +44,36 @@ export = { test.done(); }, + + 'LookupMachineImage default search'(test: Test) { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'Stack', { + env: { account: '1234', region: 'testregion' } + }); + + // WHEN + new LookupMachineImage({ name: 'bla*', owners: ['amazon'] }).getImage(stack); + + // THEN + const missing = app.synth().manifest.missing || []; + test.deepEqual(missing, [ + { + key: 'ami:account=1234:filters.image-type.0=machine:filters.name.0=bla*:filters.state.0=available:owners.0=amazon:region=testregion', + props: { + account: '1234', + region: 'testregion', + owners: [ 'amazon' ], + filters: { + 'name': [ 'bla*' ], + 'state': [ 'available' ], + 'image-type': [ 'machine' ] + } + }, + provider: 'ami' + } + ]); + + test.done(); + } }; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2/test/test.vpc.ts b/packages/@aws-cdk/aws-ec2/test/test.vpc.ts index 28d2f3017ae9c..72c8c9ad74eeb 100644 --- a/packages/@aws-cdk/aws-ec2/test/test.vpc.ts +++ b/packages/@aws-cdk/aws-ec2/test/test.vpc.ts @@ -1,8 +1,8 @@ import { countResources, expect, haveResource, haveResourceLike, isSuperObject, MatchStyle } from '@aws-cdk/assert'; import { CfnOutput, Lazy, Stack, Tag } from '@aws-cdk/core'; import { Test } from 'nodeunit'; -import { AclCidr, AclTraffic, CfnSubnet, CfnVPC, DefaultInstanceTenancy, NetworkAcl, NetworkAclEntry, - PrivateSubnet, Subnet, SubnetType, TrafficDirection, Vpc } from '../lib'; +import { AclCidr, AclTraffic, CfnSubnet, CfnVPC, DefaultInstanceTenancy, GenericLinuxImage, InstanceType, + NatProvider, NetworkAcl, NetworkAclEntry, PrivateSubnet, Subnet, SubnetType, TrafficDirection, Vpc } from '../lib'; export = { "When creating a VPC": { @@ -620,6 +620,59 @@ export = { }, }, + 'NAT instances': { + 'Can configure NAT instances instead of NAT gateways'(test: Test) { + // GIVEN + const stack = getTestStack(); + + // WHEN + new Vpc(stack, 'TheVPC', { + natGatewayProvider: NatProvider.instance({ + instanceType: new InstanceType('q86.mega'), + machineImage: new GenericLinuxImage({ + 'us-east-1': 'ami-1' + }) + }) + }); + + // THEN + expect(stack).to(countResources('AWS::EC2::Instance', 3)); + expect(stack).to(haveResource('AWS::EC2::Instance', { + ImageId: "ami-1", + InstanceType: "q86.mega", + SourceDestCheck: false, + })); + expect(stack).to(haveResource('AWS::EC2::Route', { + RouteTableId: { Ref: "TheVPCPrivateSubnet1RouteTableF6513BC2" }, + DestinationCidrBlock: "0.0.0.0/0", + InstanceId: { Ref: "TheVPCPublicSubnet1NatInstanceCC514192" } + })); + + test.done(); + }, + + 'natGateways controls amount of NAT instances'(test: Test) { + // GIVEN + const stack = getTestStack(); + + // WHEN + new Vpc(stack, 'TheVPC', { + natGatewayProvider: NatProvider.instance({ + instanceType: new InstanceType('q86.mega'), + machineImage: new GenericLinuxImage({ + 'us-east-1': 'ami-1' + }) + }), + natGateways: 1 + }); + + // THEN + expect(stack).to(countResources('AWS::EC2::Instance', 1)); + + test.done(); + }, + }, + 'Network ACL association': { 'by default uses default ACL reference'(test: Test) { // GIVEN diff --git a/packages/@aws-cdk/custom-resources/lib/sdk-api-metadata.json b/packages/@aws-cdk/custom-resources/lib/sdk-api-metadata.json new file mode 100644 index 0000000000000..374b098e90dbd --- /dev/null +++ b/packages/@aws-cdk/custom-resources/lib/sdk-api-metadata.json @@ -0,0 +1,772 @@ +{ + "acm": { + "name": "ACM", + "cors": true + }, + "apigateway": { + "name": "APIGateway", + "cors": true + }, + "applicationautoscaling": { + "prefix": "application-autoscaling", + "name": "ApplicationAutoScaling", + "cors": true + }, + "appstream": { + "name": "AppStream" + }, + "autoscaling": { + "name": "AutoScaling", + "cors": true + }, + "batch": { + "name": "Batch" + }, + "budgets": { + "name": "Budgets" + }, + "clouddirectory": { + "name": "CloudDirectory", + "versions": [ + "2016-05-10*" + ] + }, + "cloudformation": { + "name": "CloudFormation", + "cors": true + }, + "cloudfront": { + "name": "CloudFront", + "versions": [ + "2013-05-12*", + "2013-11-11*", + "2014-05-31*", + "2014-10-21*", + "2014-11-06*", + "2015-04-17*", + "2015-07-27*", + "2015-09-17*", + "2016-01-13*", + "2016-01-28*", + "2016-08-01*", + "2016-08-20*", + "2016-09-07*", + "2016-09-29*", + "2016-11-25*", + "2017-03-25*", + "2017-10-30*", + "2018-06-18*", + "2018-11-05*" + ], + "cors": true + }, + "cloudhsm": { + "name": "CloudHSM", + "cors": true + }, + "cloudsearch": { + "name": "CloudSearch" + }, + "cloudsearchdomain": { + "name": "CloudSearchDomain" + }, + "cloudtrail": { + "name": "CloudTrail", + "cors": true + }, + "cloudwatch": { + "prefix": "monitoring", + "name": "CloudWatch", + "cors": true + }, + "cloudwatchevents": { + "prefix": "events", + "name": "CloudWatchEvents", + "versions": [ + "2014-02-03*" + ], + "cors": true + }, + "cloudwatchlogs": { + "prefix": "logs", + "name": "CloudWatchLogs", + "cors": true + }, + "codebuild": { + "name": "CodeBuild", + "cors": true + }, + "codecommit": { + "name": "CodeCommit", + "cors": true + }, + "codedeploy": { + "name": "CodeDeploy", + "cors": true + }, + "codepipeline": { + "name": "CodePipeline", + "cors": true + }, + "cognitoidentity": { + "prefix": "cognito-identity", + "name": "CognitoIdentity", + "cors": true + }, + "cognitoidentityserviceprovider": { + "prefix": "cognito-idp", + "name": "CognitoIdentityServiceProvider", + "cors": true + }, + "cognitosync": { + "prefix": "cognito-sync", + "name": "CognitoSync", + "cors": true + }, + "configservice": { + "prefix": "config", + "name": "ConfigService", + "cors": true + }, + "cur": { + "name": "CUR", + "cors": true + }, + "datapipeline": { + "name": "DataPipeline" + }, + "devicefarm": { + "name": "DeviceFarm", + "cors": true + }, + "directconnect": { + "name": "DirectConnect", + "cors": true + }, + "directoryservice": { + "prefix": "ds", + "name": "DirectoryService" + }, + "discovery": { + "name": "Discovery" + }, + "dms": { + "name": "DMS" + }, + "dynamodb": { + "name": "DynamoDB", + "cors": true + }, + "dynamodbstreams": { + "prefix": "streams.dynamodb", + "name": "DynamoDBStreams", + "cors": true + }, + "ec2": { + "name": "EC2", + "versions": [ + "2013-06-15*", + "2013-10-15*", + "2014-02-01*", + "2014-05-01*", + "2014-06-15*", + "2014-09-01*", + "2014-10-01*", + "2015-03-01*", + "2015-04-15*", + "2015-10-01*", + "2016-04-01*", + "2016-09-15*" + ], + "cors": true + }, + "ecr": { + "name": "ECR", + "cors": true + }, + "ecs": { + "name": "ECS", + "cors": true + }, + "efs": { + "prefix": "elasticfilesystem", + "name": "EFS", + "cors": true + }, + "elasticache": { + "name": "ElastiCache", + "versions": [ + "2012-11-15*", + "2014-03-24*", + "2014-07-15*", + "2014-09-30*" + ], + "cors": true + }, + "elasticbeanstalk": { + "name": "ElasticBeanstalk", + "cors": true + }, + "elb": { + "prefix": "elasticloadbalancing", + "name": "ELB", + "cors": true + }, + "elbv2": { + "prefix": "elasticloadbalancingv2", + "name": "ELBv2", + "cors": true + }, + "emr": { + "prefix": "elasticmapreduce", + "name": "EMR", + "cors": true + }, + "es": { + "name": "ES" + }, + "elastictranscoder": { + "name": "ElasticTranscoder", + "cors": true + }, + "firehose": { + "name": "Firehose", + "cors": true + }, + "gamelift": { + "name": "GameLift", + "cors": true + }, + "glacier": { + "name": "Glacier" + }, + "health": { + "name": "Health" + }, + "iam": { + "name": "IAM", + "cors": true + }, + "importexport": { + "name": "ImportExport" + }, + "inspector": { + "name": "Inspector", + "versions": [ + "2015-08-18*" + ], + "cors": true + }, + "iot": { + "name": "Iot", + "cors": true + }, + "iotdata": { + "prefix": "iot-data", + "name": "IotData", + "cors": true + }, + "kinesis": { + "name": "Kinesis", + "cors": true + }, + "kinesisanalytics": { + "name": "KinesisAnalytics" + }, + "kms": { + "name": "KMS", + "cors": true + }, + "lambda": { + "name": "Lambda", + "cors": true + }, + "lexruntime": { + "prefix": "runtime.lex", + "name": "LexRuntime", + "cors": true + }, + "lightsail": { + "name": "Lightsail" + }, + "machinelearning": { + "name": "MachineLearning", + "cors": true + }, + "marketplacecommerceanalytics": { + "name": "MarketplaceCommerceAnalytics", + "cors": true + }, + "marketplacemetering": { + "prefix": "meteringmarketplace", + "name": "MarketplaceMetering" + }, + "mturk": { + "prefix": "mturk-requester", + "name": "MTurk", + "cors": true + }, + "mobileanalytics": { + "name": "MobileAnalytics", + "cors": true + }, + "opsworks": { + "name": "OpsWorks", + "cors": true + }, + "opsworkscm": { + "name": "OpsWorksCM" + }, + "organizations": { + "name": "Organizations" + }, + "pinpoint": { + "name": "Pinpoint" + }, + "polly": { + "name": "Polly", + "cors": true + }, + "rds": { + "name": "RDS", + "versions": [ + "2014-09-01*" + ], + "cors": true + }, + "redshift": { + "name": "Redshift", + "cors": true + }, + "rekognition": { + "name": "Rekognition", + "cors": true + }, + "resourcegroupstaggingapi": { + "name": "ResourceGroupsTaggingAPI" + }, + "route53": { + "name": "Route53", + "cors": true + }, + "route53domains": { + "name": "Route53Domains", + "cors": true + }, + "s3": { + "name": "S3", + "dualstackAvailable": true, + "cors": true + }, + "s3control": { + "name": "S3Control", + "dualstackAvailable": true + }, + "servicecatalog": { + "name": "ServiceCatalog", + "cors": true + }, + "ses": { + "prefix": "email", + "name": "SES", + "cors": true + }, + "shield": { + "name": "Shield" + }, + "simpledb": { + "prefix": "sdb", + "name": "SimpleDB" + }, + "sms": { + "name": "SMS" + }, + "snowball": { + "name": "Snowball" + }, + "sns": { + "name": "SNS", + "cors": true + }, + "sqs": { + "name": "SQS", + "cors": true + }, + "ssm": { + "name": "SSM", + "cors": true + }, + "storagegateway": { + "name": "StorageGateway", + "cors": true + }, + "stepfunctions": { + "prefix": "states", + "name": "StepFunctions" + }, + "sts": { + "name": "STS", + "cors": true + }, + "support": { + "name": "Support" + }, + "swf": { + "name": "SWF" + }, + "xray": { + "name": "XRay", + "cors": true + }, + "waf": { + "name": "WAF", + "cors": true + }, + "wafregional": { + "prefix": "waf-regional", + "name": "WAFRegional" + }, + "workdocs": { + "name": "WorkDocs", + "cors": true + }, + "workspaces": { + "name": "WorkSpaces" + }, + "codestar": { + "name": "CodeStar" + }, + "lexmodelbuildingservice": { + "prefix": "lex-models", + "name": "LexModelBuildingService", + "cors": true + }, + "marketplaceentitlementservice": { + "prefix": "entitlement.marketplace", + "name": "MarketplaceEntitlementService" + }, + "athena": { + "name": "Athena" + }, + "greengrass": { + "name": "Greengrass" + }, + "dax": { + "name": "DAX" + }, + "migrationhub": { + "prefix": "AWSMigrationHub", + "name": "MigrationHub" + }, + "cloudhsmv2": { + "name": "CloudHSMV2" + }, + "glue": { + "name": "Glue" + }, + "mobile": { + "name": "Mobile" + }, + "pricing": { + "name": "Pricing", + "cors": true + }, + "costexplorer": { + "prefix": "ce", + "name": "CostExplorer", + "cors": true + }, + "mediaconvert": { + "name": "MediaConvert" + }, + "medialive": { + "name": "MediaLive" + }, + "mediapackage": { + "name": "MediaPackage" + }, + "mediastore": { + "name": "MediaStore" + }, + "mediastoredata": { + "prefix": "mediastore-data", + "name": "MediaStoreData", + "cors": true + }, + "appsync": { + "name": "AppSync" + }, + "guardduty": { + "name": "GuardDuty" + }, + "mq": { + "name": "MQ" + }, + "comprehend": { + "name": "Comprehend", + "cors": true + }, + "iotjobsdataplane": { + "prefix": "iot-jobs-data", + "name": "IoTJobsDataPlane" + }, + "kinesisvideoarchivedmedia": { + "prefix": "kinesis-video-archived-media", + "name": "KinesisVideoArchivedMedia", + "cors": true + }, + "kinesisvideomedia": { + "prefix": "kinesis-video-media", + "name": "KinesisVideoMedia", + "cors": true + }, + "kinesisvideo": { + "name": "KinesisVideo", + "cors": true + }, + "sagemakerruntime": { + "prefix": "runtime.sagemaker", + "name": "SageMakerRuntime" + }, + "sagemaker": { + "name": "SageMaker" + }, + "translate": { + "name": "Translate", + "cors": true + }, + "resourcegroups": { + "prefix": "resource-groups", + "name": "ResourceGroups", + "cors": true + }, + "alexaforbusiness": { + "name": "AlexaForBusiness" + }, + "cloud9": { + "name": "Cloud9" + }, + "serverlessapplicationrepository": { + "prefix": "serverlessrepo", + "name": "ServerlessApplicationRepository" + }, + "servicediscovery": { + "name": "ServiceDiscovery" + }, + "workmail": { + "name": "WorkMail" + }, + "autoscalingplans": { + "prefix": "autoscaling-plans", + "name": "AutoScalingPlans" + }, + "transcribeservice": { + "prefix": "transcribe", + "name": "TranscribeService" + }, + "connect": { + "name": "Connect" + }, + "acmpca": { + "prefix": "acm-pca", + "name": "ACMPCA" + }, + "fms": { + "name": "FMS" + }, + "secretsmanager": { + "name": "SecretsManager", + "cors": true + }, + "iotanalytics": { + "name": "IoTAnalytics", + "cors": true + }, + "iot1clickdevicesservice": { + "prefix": "iot1click-devices", + "name": "IoT1ClickDevicesService" + }, + "iot1clickprojects": { + "prefix": "iot1click-projects", + "name": "IoT1ClickProjects" + }, + "pi": { + "name": "PI" + }, + "neptune": { + "name": "Neptune" + }, + "mediatailor": { + "name": "MediaTailor" + }, + "eks": { + "name": "EKS" + }, + "macie": { + "name": "Macie" + }, + "dlm": { + "name": "DLM" + }, + "signer": { + "name": "Signer" + }, + "chime": { + "name": "Chime" + }, + "pinpointemail": { + "prefix": "pinpoint-email", + "name": "PinpointEmail" + }, + "ram": { + "name": "RAM" + }, + "route53resolver": { + "name": "Route53Resolver" + }, + "pinpointsmsvoice": { + "prefix": "sms-voice", + "name": "PinpointSMSVoice" + }, + "quicksight": { + "name": "QuickSight" + }, + "rdsdataservice": { + "prefix": "rds-data", + "name": "RDSDataService" + }, + "amplify": { + "name": "Amplify" + }, + "datasync": { + "name": "DataSync" + }, + "robomaker": { + "name": "RoboMaker" + }, + "transfer": { + "name": "Transfer" + }, + "globalaccelerator": { + "name": "GlobalAccelerator" + }, + "comprehendmedical": { + "name": "ComprehendMedical", + "cors": true + }, + "kinesisanalyticsv2": { + "name": "KinesisAnalyticsV2" + }, + "mediaconnect": { + "name": "MediaConnect" + }, + "fsx": { + "name": "FSx" + }, + "securityhub": { + "name": "SecurityHub" + }, + "appmesh": { + "name": "AppMesh", + "versions": [ + "2018-10-01*" + ] + }, + "licensemanager": { + "prefix": "license-manager", + "name": "LicenseManager" + }, + "kafka": { + "name": "Kafka" + }, + "apigatewaymanagementapi": { + "name": "ApiGatewayManagementApi" + }, + "apigatewayv2": { + "name": "ApiGatewayV2" + }, + "docdb": { + "name": "DocDB" + }, + "backup": { + "name": "Backup" + }, + "worklink": { + "name": "WorkLink" + }, + "textract": { + "name": "Textract" + }, + "managedblockchain": { + "name": "ManagedBlockchain" + }, + "mediapackagevod": { + "prefix": "mediapackage-vod", + "name": "MediaPackageVod" + }, + "groundstation": { + "name": "GroundStation" + }, + "iotthingsgraph": { + "name": "IoTThingsGraph" + }, + "iotevents": { + "name": "IoTEvents" + }, + "ioteventsdata": { + "prefix": "iotevents-data", + "name": "IoTEventsData" + }, + "personalize": { + "name": "Personalize", + "cors": true + }, + "personalizeevents": { + "prefix": "personalize-events", + "name": "PersonalizeEvents", + "cors": true + }, + "personalizeruntime": { + "prefix": "personalize-runtime", + "name": "PersonalizeRuntime", + "cors": true + }, + "applicationinsights": { + "prefix": "application-insights", + "name": "ApplicationInsights" + }, + "servicequotas": { + "prefix": "service-quotas", + "name": "ServiceQuotas" + }, + "ec2instanceconnect": { + "prefix": "ec2-instance-connect", + "name": "EC2InstanceConnect" + }, + "eventbridge": { + "name": "EventBridge" + }, + "lakeformation": { + "name": "LakeFormation" + }, + "forecastservice": { + "prefix": "forecast", + "name": "ForecastService", + "cors": true + }, + "forecastqueryservice": { + "prefix": "forecastquery", + "name": "ForecastQueryService", + "cors": true + }, + "qldb": { + "name": "QLDB" + }, + "qldbsession": { + "prefix": "qldb-session", + "name": "QLDBSession" + }, + "workmailmessageflow": { + "name": "WorkMailMessageFlow" + } +} diff --git a/packages/@aws-cdk/cx-api/lib/cloud-assembly.ts b/packages/@aws-cdk/cx-api/lib/cloud-assembly.ts index eb0355e831f0a..01779b2413ce0 100644 --- a/packages/@aws-cdk/cx-api/lib/cloud-assembly.ts +++ b/packages/@aws-cdk/cx-api/lib/cloud-assembly.ts @@ -215,7 +215,9 @@ export class CloudAssemblyBuilder { * @param missing Missing context information. */ public addMissing(missing: MissingContext) { - this.missing.push(missing); + if (this.missing.every(m => m.key !== missing.key)) { + this.missing.push(missing); + } } /** diff --git a/packages/@aws-cdk/cx-api/lib/context/ami.ts b/packages/@aws-cdk/cx-api/lib/context/ami.ts new file mode 100644 index 0000000000000..c71be19f7b064 --- /dev/null +++ b/packages/@aws-cdk/cx-api/lib/context/ami.ts @@ -0,0 +1,23 @@ +export const AMI_PROVIDER = "ami"; + +/** + * Query to AMI context provider + */ +export interface AmiContextQuery { + /** + * Owners to DescribeImages call + * + * @default - All owners + */ + readonly owners?: string[]; + + /** + * Filters to DescribeImages call + */ + readonly filters: {[key: string]: string[]}; +} + +/** + * Returns just an AMI ID + */ +export type AmiContextResponse = string; \ No newline at end of file diff --git a/packages/@aws-cdk/cx-api/lib/index.ts b/packages/@aws-cdk/cx-api/lib/index.ts index 7dbe4040dc3f8..0b15109012855 100644 --- a/packages/@aws-cdk/cx-api/lib/index.ts +++ b/packages/@aws-cdk/cx-api/lib/index.ts @@ -2,6 +2,7 @@ export * from './cxapi'; export * from './context/hosted-zone'; export * from './context/vpc'; export * from './context/ssm-parameter'; +export * from './context/ami'; export * from './context/availability-zones'; export * from './cloud-artifact'; export * from './cloudformation-artifact'; diff --git a/packages/@aws-cdk/cx-api/lib/versioning.ts b/packages/@aws-cdk/cx-api/lib/versioning.ts index 17dc186fae9a4..1c9509d70ed1c 100644 --- a/packages/@aws-cdk/cx-api/lib/versioning.ts +++ b/packages/@aws-cdk/cx-api/lib/versioning.ts @@ -31,7 +31,7 @@ import { AssemblyManifest } from './cloud-assembly'; * Note that the versions are not compared in a semver way, they are used as * opaque ordered tokens. */ -export const CLOUD_ASSEMBLY_VERSION = '1.16.0'; +export const CLOUD_ASSEMBLY_VERSION = '1.18.0'; /** * Look at the type of response we get and upgrade it to the latest expected version @@ -69,6 +69,11 @@ export function upgradeAssemblyManifest(manifest: AssemblyManifest): AssemblyMan manifest = justUpgradeVersion(manifest, '1.16.0'); } + if (manifest.version === '1.16.0') { + // Added AMI context provider + manifest = justUpgradeVersion(manifest, '1.18.0'); + } + return manifest; } diff --git a/packages/@aws-cdk/cx-api/test/cloud-assembly-builder.test.ts b/packages/@aws-cdk/cx-api/test/cloud-assembly-builder.test.ts index c69de189bfa79..ff4b01c0ff624 100644 --- a/packages/@aws-cdk/cx-api/test/cloud-assembly-builder.test.ts +++ b/packages/@aws-cdk/cx-api/test/cloud-assembly-builder.test.ts @@ -110,3 +110,15 @@ test('cloud assembly builder', () => { test('outdir must be a directory', () => { expect(() => new CloudAssemblyBuilder(__filename)).toThrow('must be a directory'); }); + +test('duplicate missing values with the same key are only reported once', () => { + const outdir = fs.mkdtempSync(path.join(os.tmpdir(), 'cloud-assembly-builder-tests')); + const session = new CloudAssemblyBuilder(outdir); + + session.addMissing({ key: 'foo', provider: 'context-provider', props: { } }); + session.addMissing({ key: 'foo', provider: 'context-provider', props: { } }); + + const assembly = session.buildAssembly(); + + expect(assembly.manifest.missing!.length).toEqual(1); +}); \ No newline at end of file diff --git a/packages/aws-cdk/lib/context-providers/ami.ts b/packages/aws-cdk/lib/context-providers/ami.ts new file mode 100644 index 0000000000000..b9c7f0caae2d2 --- /dev/null +++ b/packages/aws-cdk/lib/context-providers/ami.ts @@ -0,0 +1,54 @@ +import cxapi = require('@aws-cdk/cx-api'); +import { ISDK, Mode } from '../api'; +import { debug, print } from '../logging'; +import { ContextProviderPlugin } from './provider'; + +/** + * Plugin to search AMIs for the current account + */ +export class AmiContextProviderPlugin implements ContextProviderPlugin { + constructor(private readonly aws: ISDK) { + } + + public async getValue(args: cxapi.AmiContextQuery & { region: string, account: string }) { + const region = args.region; + const account = args.account; + + // Normally we'd do this only as 'debug', but searching AMIs typically takes dozens + // of seconds, so be little more verbose about it so users know what is going on. + print(`Searching for AMI in ${account}:${region}`); + debug(`AMI search parameters: ${JSON.stringify(args)}`); + + const ec2 = await this.aws.ec2(account, region, Mode.ForReading); + const response = await ec2.describeImages({ + Owners: args.owners, + Filters: Object.entries(args.filters).map(([key, values]) => ({ + Name: key, + Values: values + })) + }).promise(); + + const images = [...response.Images || []].filter(i => i.ImageId !== undefined); + + if (images.length === 0) { + throw new Error(`No AMI found that matched the search criteria`); + } + + // Return the most recent one + // Note: Date.parse() is not going to respect the timezone of the string, + // but since we only care about the relative values that is okay. + images.sort(descending(i => Date.parse(i.CreationDate || '1970'))); + + debug(`Selected image '${images[0].ImageId}' created at '${images[0].CreationDate}'`); + return images[0].ImageId!; + } +} + +/** + * Make a comparator that sorts in descending order given a sort key extractor + */ +function descending(valueOf: (x: A) => number) { + return (a: A, b: A) => { + return valueOf(b) - valueOf(a); + }; +} diff --git a/packages/aws-cdk/lib/context-providers/index.ts b/packages/aws-cdk/lib/context-providers/index.ts index 708ad476d0e89..84c68a2a2097f 100644 --- a/packages/aws-cdk/lib/context-providers/index.ts +++ b/packages/aws-cdk/lib/context-providers/index.ts @@ -2,6 +2,7 @@ import cxapi = require('@aws-cdk/cx-api'); import { ISDK } from '../api/util/sdk'; import { debug } from '../logging'; import { Context, TRANSIENT_CONTEXT_KEY } from '../settings'; +import { AmiContextProviderPlugin } from './ami'; import { AZContextProviderPlugin } from './availability-zones'; import { HostedZoneContextProviderPlugin } from './hosted-zones'; import { ContextProviderPlugin } from './provider'; @@ -56,4 +57,5 @@ const availableContextProviders: ProviderMap = { [cxapi.SSM_PARAMETER_PROVIDER]: SSMContextProviderPlugin, [cxapi.HOSTED_ZONE_PROVIDER]: HostedZoneContextProviderPlugin, [cxapi.VPC_PROVIDER]: VpcNetworkContextProviderPlugin, + [cxapi.AMI_PROVIDER]: AmiContextProviderPlugin, }; diff --git a/packages/aws-cdk/test/context-providers/test.amis.ts b/packages/aws-cdk/test/context-providers/test.amis.ts new file mode 100644 index 0000000000000..4c1ec906d5626 --- /dev/null +++ b/packages/aws-cdk/test/context-providers/test.amis.ts @@ -0,0 +1,83 @@ +import aws = require('aws-sdk'); +import AWS = require('aws-sdk-mock'); +import nodeunit = require('nodeunit'); +import { ISDK } from '../../lib/api'; +import { AmiContextProviderPlugin } from '../../lib/context-providers/ami'; + +AWS.setSDKInstance(aws); + +const mockSDK: ISDK = { + defaultAccount: () => Promise.resolve('123456789012'), + defaultRegion: () => Promise.resolve('bermuda-triangle-1337'), + cloudFormation: () => { throw new Error('Not Mocked'); }, + ec2: () => Promise.resolve(new aws.EC2()), + ecr: () => { throw new Error('Not Mocked'); }, + route53: () => { throw new Error('Not Mocked'); }, + s3: () => { throw new Error('Not Mocked'); }, + ssm: () => { throw new Error('Not Mocked'); }, +}; + +type AwsCallback = (err: Error | null, val: T) => void; + +export = nodeunit.testCase({ + async 'calls DescribeImages on the request'(test: nodeunit.Test) { + // GIVEN + let request: aws.EC2.DescribeImagesRequest; + AWS.mock('EC2', 'describeImages', (params: aws.EC2.DescribeImagesRequest, cb: AwsCallback) => { + request = params; + return cb(null, { Images: [{ ImageId: 'ami-1234' }] }); + }); + + // WHEN + await new AmiContextProviderPlugin(mockSDK).getValue({ + account: '1234', + region: 'asdf', + owners: ['some-owner'], + filters: { + 'some-filter': ['filtered'] + } + }); + + // THEN + test.deepEqual(request!, { + Owners: ['some-owner'], + Filters: [ + { + Name: 'some-filter', + Values: ['filtered'], + } + ] + } as aws.EC2.DescribeImagesRequest); + + AWS.restore(); + test.done(); + }, + async 'returns the most recent AMI matching the criteria'(test: nodeunit.Test) { + // GIVEN + AWS.mock('EC2', 'describeImages', (_: aws.EC2.DescribeImagesRequest, cb: AwsCallback) => { + return cb(null, { Images: [ + { + ImageId: 'ami-1234', + CreationDate: "2016-06-22T08:39:59.000Z", + }, + { + ImageId: 'ami-5678', + CreationDate: "2019-06-22T08:39:59.000Z", + } + ]}); + }); + + // WHEN + const result = await new AmiContextProviderPlugin(mockSDK).getValue({ + account: '1234', + region: 'asdf', + filters: {} + }); + + // THEN + test.equals(result, 'ami-5678'); + + AWS.restore(); + test.done(); + } +}); \ No newline at end of file diff --git a/tools/cdk-integ-tools/lib/integ-helpers.ts b/tools/cdk-integ-tools/lib/integ-helpers.ts index 2d53c07814a86..b368172fb9473 100644 --- a/tools/cdk-integ-tools/lib/integ-helpers.ts +++ b/tools/cdk-integ-tools/lib/integ-helpers.ts @@ -183,6 +183,8 @@ export const DEFAULT_SYNTH_OPTIONS = { "ssm:account=12345678:parameterName=/aws/service/ami-amazon-linux-latest/amzn-ami-hvm-x86_64-gp2:region=test-region": "ami-1234", "ssm:account=12345678:parameterName=/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2:region=test-region": "ami-1234", "ssm:account=12345678:parameterName=/aws/service/ecs/optimized-ami/amazon-linux/recommended:region=test-region": "{\"image_id\": \"ami-1234\"}", + // tslint:disable-next-line:max-line-length + "ami:account=12345678:filters.image-type.0=machine:filters.name.0=amzn-ami-vpc-nat-*:filters.state.0=available:owners.0=amazon:region=test-region": "ami-1234", "vpc-provider:account=12345678:filter.isDefault=true:region=test-region:returnAsymmetricSubnets=true": { vpcId: "vpc-60900905", subnetGroups: [ From 78a31aa44cbaa04415873168a94e5920b43c47cd Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Thu, 7 Nov 2019 15:55:46 +0100 Subject: [PATCH 2/8] Set CX version to 1.17.0 --- packages/@aws-cdk/cx-api/lib/versioning.ts | 4 ++-- .../cx-api/test/__snapshots__/cloud-assembly.test.js.snap | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/cx-api/lib/versioning.ts b/packages/@aws-cdk/cx-api/lib/versioning.ts index 1c9509d70ed1c..e83382b05d0b9 100644 --- a/packages/@aws-cdk/cx-api/lib/versioning.ts +++ b/packages/@aws-cdk/cx-api/lib/versioning.ts @@ -31,7 +31,7 @@ import { AssemblyManifest } from './cloud-assembly'; * Note that the versions are not compared in a semver way, they are used as * opaque ordered tokens. */ -export const CLOUD_ASSEMBLY_VERSION = '1.18.0'; +export const CLOUD_ASSEMBLY_VERSION = '1.17.0'; /** * Look at the type of response we get and upgrade it to the latest expected version @@ -71,7 +71,7 @@ export function upgradeAssemblyManifest(manifest: AssemblyManifest): AssemblyMan if (manifest.version === '1.16.0') { // Added AMI context provider - manifest = justUpgradeVersion(manifest, '1.18.0'); + manifest = justUpgradeVersion(manifest, '1.17.0'); } return manifest; diff --git a/packages/@aws-cdk/cx-api/test/__snapshots__/cloud-assembly.test.js.snap b/packages/@aws-cdk/cx-api/test/__snapshots__/cloud-assembly.test.js.snap index 3549e8352e60e..46e42bf16f112 100644 --- a/packages/@aws-cdk/cx-api/test/__snapshots__/cloud-assembly.test.js.snap +++ b/packages/@aws-cdk/cx-api/test/__snapshots__/cloud-assembly.test.js.snap @@ -48,7 +48,7 @@ Array [ exports[`empty assembly 1`] = ` Object { - "version": "1.16.0", + "version": "1.17.0", } `; From 4230bec25e0da962382501d5519397e32ac5c43a Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Thu, 7 Nov 2019 16:06:47 +0100 Subject: [PATCH 3/8] Add missing 'readonly's --- packages/@aws-cdk/aws-ec2/lib/machine-image.ts | 10 +++++----- packages/@aws-cdk/aws-ec2/lib/nat.ts | 10 +++++----- packages/@aws-cdk/aws-ec2/lib/vpc.ts | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/@aws-cdk/aws-ec2/lib/machine-image.ts b/packages/@aws-cdk/aws-ec2/lib/machine-image.ts index 85aadaf0362c2..ae757033c78f7 100644 --- a/packages/@aws-cdk/aws-ec2/lib/machine-image.ts +++ b/packages/@aws-cdk/aws-ec2/lib/machine-image.ts @@ -368,14 +368,14 @@ export interface LookupMachineImageProps { /** * Name of the image (may contain wildcards) */ - name: string; + readonly name: string; /** * Owner account IDs or aliases * * @default - All owners */ - owners?: string[]; + readonly owners?: string[]; /** * Additional filters on the AMI @@ -383,19 +383,19 @@ export interface LookupMachineImageProps { * @see https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeImages.html * @default - No additional filters */ - filters?: {[key: string]: string[]}; + readonly filters?: {[key: string]: string[]}; /** * Look for Windows images * * @default false */ - windows?: boolean; + readonly windows?: boolean; /** * Custom userdata for this image * * @default - Empty user data appropriate for the platform type */ - userData?: UserData; + readonly userData?: UserData; } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2/lib/nat.ts b/packages/@aws-cdk/aws-ec2/lib/nat.ts index 8be0039c82235..fc0a4fa358bc1 100644 --- a/packages/@aws-cdk/aws-ec2/lib/nat.ts +++ b/packages/@aws-cdk/aws-ec2/lib/nat.ts @@ -56,19 +56,19 @@ export interface ConfigureNatOptions { /** * The VPC we're configuring NAT for */ - vpc: Vpc; + readonly vpc: Vpc; /** * The public subnets where the NAT providers need to be placed */ - natSubnets: PublicSubnet[]; + readonly natSubnets: PublicSubnet[]; /** * The private subnets that need to route through the NAT providers. * * There may be more private subnets than public subnets with NAT providers. */ - privateSubnets: PrivateSubnet[]; + readonly privateSubnets: PrivateSubnet[]; } /** @@ -95,12 +95,12 @@ export interface NatInstanceProps { * * @default - Latest NAT instance image */ - machineImage?: IMachineImage; + readonly machineImage?: IMachineImage; /** * Instance type of the NAT instance */ - instanceType: InstanceType; + readonly instanceType: InstanceType; /** * Name of SSH keypair to grant access to instance diff --git a/packages/@aws-cdk/aws-ec2/lib/vpc.ts b/packages/@aws-cdk/aws-ec2/lib/vpc.ts index ba43af3fd88c0..79494daadb152 100644 --- a/packages/@aws-cdk/aws-ec2/lib/vpc.ts +++ b/packages/@aws-cdk/aws-ec2/lib/vpc.ts @@ -1426,26 +1426,26 @@ export interface AddRouteOptions { * * @default '0.0.0.0/0' */ - destinationCidrBlock?: string; + readonly destinationCidrBlock?: string; /** * IPv6 range this route applies to * * @default - Uses IPv6 */ - destinationIpv6CidrBlock?: string; + readonly destinationIpv6CidrBlock?: string; /** * What type of router to route this traffic to */ - routerType: RouterType; + readonly routerType: RouterType; /** * The ID of the router * * Can be an instance ID, gateway ID, etc, depending on the router type. */ - routerId: string; + readonly routerId: string; /** * Whether this route will enable internet connectivity @@ -1453,7 +1453,7 @@ export interface AddRouteOptions { * If true, this route will be added before any AWS resources that depend * on internet connectivity in the VPC will be created. */ - enablesInternetConnectivity?: boolean; + readonly enablesInternetConnectivity?: boolean; } /** From a126c912b7c9bede884651752d44934cbf47ca62 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Thu, 7 Nov 2019 16:23:14 +0100 Subject: [PATCH 4/8] Document @default --- packages/@aws-cdk/aws-ec2/lib/vpc.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/@aws-cdk/aws-ec2/lib/vpc.ts b/packages/@aws-cdk/aws-ec2/lib/vpc.ts index 79494daadb152..6167e71ec5967 100644 --- a/packages/@aws-cdk/aws-ec2/lib/vpc.ts +++ b/packages/@aws-cdk/aws-ec2/lib/vpc.ts @@ -1452,6 +1452,8 @@ export interface AddRouteOptions { * * If true, this route will be added before any AWS resources that depend * on internet connectivity in the VPC will be created. + * + * @default false */ readonly enablesInternetConnectivity?: boolean; } From e51ef54768f00684f1ca5233e7512ce8c91f1b13 Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Thu, 7 Nov 2019 23:19:42 +0200 Subject: [PATCH 5/8] Delete sdk-api-metadata.json --- .../lib/sdk-api-metadata.json | 772 ------------------ 1 file changed, 772 deletions(-) delete mode 100644 packages/@aws-cdk/custom-resources/lib/sdk-api-metadata.json diff --git a/packages/@aws-cdk/custom-resources/lib/sdk-api-metadata.json b/packages/@aws-cdk/custom-resources/lib/sdk-api-metadata.json deleted file mode 100644 index 374b098e90dbd..0000000000000 --- a/packages/@aws-cdk/custom-resources/lib/sdk-api-metadata.json +++ /dev/null @@ -1,772 +0,0 @@ -{ - "acm": { - "name": "ACM", - "cors": true - }, - "apigateway": { - "name": "APIGateway", - "cors": true - }, - "applicationautoscaling": { - "prefix": "application-autoscaling", - "name": "ApplicationAutoScaling", - "cors": true - }, - "appstream": { - "name": "AppStream" - }, - "autoscaling": { - "name": "AutoScaling", - "cors": true - }, - "batch": { - "name": "Batch" - }, - "budgets": { - "name": "Budgets" - }, - "clouddirectory": { - "name": "CloudDirectory", - "versions": [ - "2016-05-10*" - ] - }, - "cloudformation": { - "name": "CloudFormation", - "cors": true - }, - "cloudfront": { - "name": "CloudFront", - "versions": [ - "2013-05-12*", - "2013-11-11*", - "2014-05-31*", - "2014-10-21*", - "2014-11-06*", - "2015-04-17*", - "2015-07-27*", - "2015-09-17*", - "2016-01-13*", - "2016-01-28*", - "2016-08-01*", - "2016-08-20*", - "2016-09-07*", - "2016-09-29*", - "2016-11-25*", - "2017-03-25*", - "2017-10-30*", - "2018-06-18*", - "2018-11-05*" - ], - "cors": true - }, - "cloudhsm": { - "name": "CloudHSM", - "cors": true - }, - "cloudsearch": { - "name": "CloudSearch" - }, - "cloudsearchdomain": { - "name": "CloudSearchDomain" - }, - "cloudtrail": { - "name": "CloudTrail", - "cors": true - }, - "cloudwatch": { - "prefix": "monitoring", - "name": "CloudWatch", - "cors": true - }, - "cloudwatchevents": { - "prefix": "events", - "name": "CloudWatchEvents", - "versions": [ - "2014-02-03*" - ], - "cors": true - }, - "cloudwatchlogs": { - "prefix": "logs", - "name": "CloudWatchLogs", - "cors": true - }, - "codebuild": { - "name": "CodeBuild", - "cors": true - }, - "codecommit": { - "name": "CodeCommit", - "cors": true - }, - "codedeploy": { - "name": "CodeDeploy", - "cors": true - }, - "codepipeline": { - "name": "CodePipeline", - "cors": true - }, - "cognitoidentity": { - "prefix": "cognito-identity", - "name": "CognitoIdentity", - "cors": true - }, - "cognitoidentityserviceprovider": { - "prefix": "cognito-idp", - "name": "CognitoIdentityServiceProvider", - "cors": true - }, - "cognitosync": { - "prefix": "cognito-sync", - "name": "CognitoSync", - "cors": true - }, - "configservice": { - "prefix": "config", - "name": "ConfigService", - "cors": true - }, - "cur": { - "name": "CUR", - "cors": true - }, - "datapipeline": { - "name": "DataPipeline" - }, - "devicefarm": { - "name": "DeviceFarm", - "cors": true - }, - "directconnect": { - "name": "DirectConnect", - "cors": true - }, - "directoryservice": { - "prefix": "ds", - "name": "DirectoryService" - }, - "discovery": { - "name": "Discovery" - }, - "dms": { - "name": "DMS" - }, - "dynamodb": { - "name": "DynamoDB", - "cors": true - }, - "dynamodbstreams": { - "prefix": "streams.dynamodb", - "name": "DynamoDBStreams", - "cors": true - }, - "ec2": { - "name": "EC2", - "versions": [ - "2013-06-15*", - "2013-10-15*", - "2014-02-01*", - "2014-05-01*", - "2014-06-15*", - "2014-09-01*", - "2014-10-01*", - "2015-03-01*", - "2015-04-15*", - "2015-10-01*", - "2016-04-01*", - "2016-09-15*" - ], - "cors": true - }, - "ecr": { - "name": "ECR", - "cors": true - }, - "ecs": { - "name": "ECS", - "cors": true - }, - "efs": { - "prefix": "elasticfilesystem", - "name": "EFS", - "cors": true - }, - "elasticache": { - "name": "ElastiCache", - "versions": [ - "2012-11-15*", - "2014-03-24*", - "2014-07-15*", - "2014-09-30*" - ], - "cors": true - }, - "elasticbeanstalk": { - "name": "ElasticBeanstalk", - "cors": true - }, - "elb": { - "prefix": "elasticloadbalancing", - "name": "ELB", - "cors": true - }, - "elbv2": { - "prefix": "elasticloadbalancingv2", - "name": "ELBv2", - "cors": true - }, - "emr": { - "prefix": "elasticmapreduce", - "name": "EMR", - "cors": true - }, - "es": { - "name": "ES" - }, - "elastictranscoder": { - "name": "ElasticTranscoder", - "cors": true - }, - "firehose": { - "name": "Firehose", - "cors": true - }, - "gamelift": { - "name": "GameLift", - "cors": true - }, - "glacier": { - "name": "Glacier" - }, - "health": { - "name": "Health" - }, - "iam": { - "name": "IAM", - "cors": true - }, - "importexport": { - "name": "ImportExport" - }, - "inspector": { - "name": "Inspector", - "versions": [ - "2015-08-18*" - ], - "cors": true - }, - "iot": { - "name": "Iot", - "cors": true - }, - "iotdata": { - "prefix": "iot-data", - "name": "IotData", - "cors": true - }, - "kinesis": { - "name": "Kinesis", - "cors": true - }, - "kinesisanalytics": { - "name": "KinesisAnalytics" - }, - "kms": { - "name": "KMS", - "cors": true - }, - "lambda": { - "name": "Lambda", - "cors": true - }, - "lexruntime": { - "prefix": "runtime.lex", - "name": "LexRuntime", - "cors": true - }, - "lightsail": { - "name": "Lightsail" - }, - "machinelearning": { - "name": "MachineLearning", - "cors": true - }, - "marketplacecommerceanalytics": { - "name": "MarketplaceCommerceAnalytics", - "cors": true - }, - "marketplacemetering": { - "prefix": "meteringmarketplace", - "name": "MarketplaceMetering" - }, - "mturk": { - "prefix": "mturk-requester", - "name": "MTurk", - "cors": true - }, - "mobileanalytics": { - "name": "MobileAnalytics", - "cors": true - }, - "opsworks": { - "name": "OpsWorks", - "cors": true - }, - "opsworkscm": { - "name": "OpsWorksCM" - }, - "organizations": { - "name": "Organizations" - }, - "pinpoint": { - "name": "Pinpoint" - }, - "polly": { - "name": "Polly", - "cors": true - }, - "rds": { - "name": "RDS", - "versions": [ - "2014-09-01*" - ], - "cors": true - }, - "redshift": { - "name": "Redshift", - "cors": true - }, - "rekognition": { - "name": "Rekognition", - "cors": true - }, - "resourcegroupstaggingapi": { - "name": "ResourceGroupsTaggingAPI" - }, - "route53": { - "name": "Route53", - "cors": true - }, - "route53domains": { - "name": "Route53Domains", - "cors": true - }, - "s3": { - "name": "S3", - "dualstackAvailable": true, - "cors": true - }, - "s3control": { - "name": "S3Control", - "dualstackAvailable": true - }, - "servicecatalog": { - "name": "ServiceCatalog", - "cors": true - }, - "ses": { - "prefix": "email", - "name": "SES", - "cors": true - }, - "shield": { - "name": "Shield" - }, - "simpledb": { - "prefix": "sdb", - "name": "SimpleDB" - }, - "sms": { - "name": "SMS" - }, - "snowball": { - "name": "Snowball" - }, - "sns": { - "name": "SNS", - "cors": true - }, - "sqs": { - "name": "SQS", - "cors": true - }, - "ssm": { - "name": "SSM", - "cors": true - }, - "storagegateway": { - "name": "StorageGateway", - "cors": true - }, - "stepfunctions": { - "prefix": "states", - "name": "StepFunctions" - }, - "sts": { - "name": "STS", - "cors": true - }, - "support": { - "name": "Support" - }, - "swf": { - "name": "SWF" - }, - "xray": { - "name": "XRay", - "cors": true - }, - "waf": { - "name": "WAF", - "cors": true - }, - "wafregional": { - "prefix": "waf-regional", - "name": "WAFRegional" - }, - "workdocs": { - "name": "WorkDocs", - "cors": true - }, - "workspaces": { - "name": "WorkSpaces" - }, - "codestar": { - "name": "CodeStar" - }, - "lexmodelbuildingservice": { - "prefix": "lex-models", - "name": "LexModelBuildingService", - "cors": true - }, - "marketplaceentitlementservice": { - "prefix": "entitlement.marketplace", - "name": "MarketplaceEntitlementService" - }, - "athena": { - "name": "Athena" - }, - "greengrass": { - "name": "Greengrass" - }, - "dax": { - "name": "DAX" - }, - "migrationhub": { - "prefix": "AWSMigrationHub", - "name": "MigrationHub" - }, - "cloudhsmv2": { - "name": "CloudHSMV2" - }, - "glue": { - "name": "Glue" - }, - "mobile": { - "name": "Mobile" - }, - "pricing": { - "name": "Pricing", - "cors": true - }, - "costexplorer": { - "prefix": "ce", - "name": "CostExplorer", - "cors": true - }, - "mediaconvert": { - "name": "MediaConvert" - }, - "medialive": { - "name": "MediaLive" - }, - "mediapackage": { - "name": "MediaPackage" - }, - "mediastore": { - "name": "MediaStore" - }, - "mediastoredata": { - "prefix": "mediastore-data", - "name": "MediaStoreData", - "cors": true - }, - "appsync": { - "name": "AppSync" - }, - "guardduty": { - "name": "GuardDuty" - }, - "mq": { - "name": "MQ" - }, - "comprehend": { - "name": "Comprehend", - "cors": true - }, - "iotjobsdataplane": { - "prefix": "iot-jobs-data", - "name": "IoTJobsDataPlane" - }, - "kinesisvideoarchivedmedia": { - "prefix": "kinesis-video-archived-media", - "name": "KinesisVideoArchivedMedia", - "cors": true - }, - "kinesisvideomedia": { - "prefix": "kinesis-video-media", - "name": "KinesisVideoMedia", - "cors": true - }, - "kinesisvideo": { - "name": "KinesisVideo", - "cors": true - }, - "sagemakerruntime": { - "prefix": "runtime.sagemaker", - "name": "SageMakerRuntime" - }, - "sagemaker": { - "name": "SageMaker" - }, - "translate": { - "name": "Translate", - "cors": true - }, - "resourcegroups": { - "prefix": "resource-groups", - "name": "ResourceGroups", - "cors": true - }, - "alexaforbusiness": { - "name": "AlexaForBusiness" - }, - "cloud9": { - "name": "Cloud9" - }, - "serverlessapplicationrepository": { - "prefix": "serverlessrepo", - "name": "ServerlessApplicationRepository" - }, - "servicediscovery": { - "name": "ServiceDiscovery" - }, - "workmail": { - "name": "WorkMail" - }, - "autoscalingplans": { - "prefix": "autoscaling-plans", - "name": "AutoScalingPlans" - }, - "transcribeservice": { - "prefix": "transcribe", - "name": "TranscribeService" - }, - "connect": { - "name": "Connect" - }, - "acmpca": { - "prefix": "acm-pca", - "name": "ACMPCA" - }, - "fms": { - "name": "FMS" - }, - "secretsmanager": { - "name": "SecretsManager", - "cors": true - }, - "iotanalytics": { - "name": "IoTAnalytics", - "cors": true - }, - "iot1clickdevicesservice": { - "prefix": "iot1click-devices", - "name": "IoT1ClickDevicesService" - }, - "iot1clickprojects": { - "prefix": "iot1click-projects", - "name": "IoT1ClickProjects" - }, - "pi": { - "name": "PI" - }, - "neptune": { - "name": "Neptune" - }, - "mediatailor": { - "name": "MediaTailor" - }, - "eks": { - "name": "EKS" - }, - "macie": { - "name": "Macie" - }, - "dlm": { - "name": "DLM" - }, - "signer": { - "name": "Signer" - }, - "chime": { - "name": "Chime" - }, - "pinpointemail": { - "prefix": "pinpoint-email", - "name": "PinpointEmail" - }, - "ram": { - "name": "RAM" - }, - "route53resolver": { - "name": "Route53Resolver" - }, - "pinpointsmsvoice": { - "prefix": "sms-voice", - "name": "PinpointSMSVoice" - }, - "quicksight": { - "name": "QuickSight" - }, - "rdsdataservice": { - "prefix": "rds-data", - "name": "RDSDataService" - }, - "amplify": { - "name": "Amplify" - }, - "datasync": { - "name": "DataSync" - }, - "robomaker": { - "name": "RoboMaker" - }, - "transfer": { - "name": "Transfer" - }, - "globalaccelerator": { - "name": "GlobalAccelerator" - }, - "comprehendmedical": { - "name": "ComprehendMedical", - "cors": true - }, - "kinesisanalyticsv2": { - "name": "KinesisAnalyticsV2" - }, - "mediaconnect": { - "name": "MediaConnect" - }, - "fsx": { - "name": "FSx" - }, - "securityhub": { - "name": "SecurityHub" - }, - "appmesh": { - "name": "AppMesh", - "versions": [ - "2018-10-01*" - ] - }, - "licensemanager": { - "prefix": "license-manager", - "name": "LicenseManager" - }, - "kafka": { - "name": "Kafka" - }, - "apigatewaymanagementapi": { - "name": "ApiGatewayManagementApi" - }, - "apigatewayv2": { - "name": "ApiGatewayV2" - }, - "docdb": { - "name": "DocDB" - }, - "backup": { - "name": "Backup" - }, - "worklink": { - "name": "WorkLink" - }, - "textract": { - "name": "Textract" - }, - "managedblockchain": { - "name": "ManagedBlockchain" - }, - "mediapackagevod": { - "prefix": "mediapackage-vod", - "name": "MediaPackageVod" - }, - "groundstation": { - "name": "GroundStation" - }, - "iotthingsgraph": { - "name": "IoTThingsGraph" - }, - "iotevents": { - "name": "IoTEvents" - }, - "ioteventsdata": { - "prefix": "iotevents-data", - "name": "IoTEventsData" - }, - "personalize": { - "name": "Personalize", - "cors": true - }, - "personalizeevents": { - "prefix": "personalize-events", - "name": "PersonalizeEvents", - "cors": true - }, - "personalizeruntime": { - "prefix": "personalize-runtime", - "name": "PersonalizeRuntime", - "cors": true - }, - "applicationinsights": { - "prefix": "application-insights", - "name": "ApplicationInsights" - }, - "servicequotas": { - "prefix": "service-quotas", - "name": "ServiceQuotas" - }, - "ec2instanceconnect": { - "prefix": "ec2-instance-connect", - "name": "EC2InstanceConnect" - }, - "eventbridge": { - "name": "EventBridge" - }, - "lakeformation": { - "name": "LakeFormation" - }, - "forecastservice": { - "prefix": "forecast", - "name": "ForecastService", - "cors": true - }, - "forecastqueryservice": { - "prefix": "forecastquery", - "name": "ForecastQueryService", - "cors": true - }, - "qldb": { - "name": "QLDB" - }, - "qldbsession": { - "prefix": "qldb-session", - "name": "QLDBSession" - }, - "workmailmessageflow": { - "name": "WorkMailMessageFlow" - } -} From 253aaf87f33d45810585cb2fd738b2fae026176e Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Fri, 8 Nov 2019 13:19:08 +0100 Subject: [PATCH 6/8] Improve docs --- packages/@aws-cdk/aws-ec2/lib/vpc.ts | 2 +- packages/@aws-cdk/cx-api/lib/versioning.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-ec2/lib/vpc.ts b/packages/@aws-cdk/aws-ec2/lib/vpc.ts index 6167e71ec5967..a2d508b444183 100644 --- a/packages/@aws-cdk/aws-ec2/lib/vpc.ts +++ b/packages/@aws-cdk/aws-ec2/lib/vpc.ts @@ -640,7 +640,7 @@ export interface VpcProps { * Select between NAT gateways or NAT instances. NAT gateways * may not be available in all AWS regions. * - * @default - NatProvider.gateway() + * @default NatProvider.gateway() * @experimental */ readonly natGatewayProvider?: NatProvider; diff --git a/packages/@aws-cdk/cx-api/lib/versioning.ts b/packages/@aws-cdk/cx-api/lib/versioning.ts index e83382b05d0b9..93e638e73dd6b 100644 --- a/packages/@aws-cdk/cx-api/lib/versioning.ts +++ b/packages/@aws-cdk/cx-api/lib/versioning.ts @@ -70,7 +70,7 @@ export function upgradeAssemblyManifest(manifest: AssemblyManifest): AssemblyMan } if (manifest.version === '1.16.0') { - // Added AMI context provider + // Added AMI context provider: old assemblies won't reference it. manifest = justUpgradeVersion(manifest, '1.17.0'); } From 3e077aa2ba8b3c3c9b373f5a94c51e021a8f4022 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Mon, 11 Nov 2019 09:40:00 +0100 Subject: [PATCH 7/8] Target integration with upcoming release --- packages/@aws-cdk/cx-api/lib/versioning.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/@aws-cdk/cx-api/lib/versioning.ts b/packages/@aws-cdk/cx-api/lib/versioning.ts index 93e638e73dd6b..090798c17c55a 100644 --- a/packages/@aws-cdk/cx-api/lib/versioning.ts +++ b/packages/@aws-cdk/cx-api/lib/versioning.ts @@ -31,7 +31,7 @@ import { AssemblyManifest } from './cloud-assembly'; * Note that the versions are not compared in a semver way, they are used as * opaque ordered tokens. */ -export const CLOUD_ASSEMBLY_VERSION = '1.17.0'; +export const CLOUD_ASSEMBLY_VERSION = '1.16.0'; /** * Look at the type of response we get and upgrade it to the latest expected version @@ -65,15 +65,12 @@ export function upgradeAssemblyManifest(manifest: AssemblyManifest): AssemblyMan } if (manifest.version === '1.10.0') { - // backwards-compatible changes to the VPC provider + // Two changes: + // * Backwards-compatible changes to the VPC provider + // * Added AMI context provider: old assemblies won't reference it. manifest = justUpgradeVersion(manifest, '1.16.0'); } - if (manifest.version === '1.16.0') { - // Added AMI context provider: old assemblies won't reference it. - manifest = justUpgradeVersion(manifest, '1.17.0'); - } - return manifest; } From 4b973de6dc837d0bb3c176fe57055579890562c0 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Mon, 11 Nov 2019 09:53:17 +0100 Subject: [PATCH 8/8] Fix test --- .../cx-api/test/__snapshots__/cloud-assembly.test.js.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/cx-api/test/__snapshots__/cloud-assembly.test.js.snap b/packages/@aws-cdk/cx-api/test/__snapshots__/cloud-assembly.test.js.snap index 46e42bf16f112..3549e8352e60e 100644 --- a/packages/@aws-cdk/cx-api/test/__snapshots__/cloud-assembly.test.js.snap +++ b/packages/@aws-cdk/cx-api/test/__snapshots__/cloud-assembly.test.js.snap @@ -48,7 +48,7 @@ Array [ exports[`empty assembly 1`] = ` Object { - "version": "1.17.0", + "version": "1.16.0", } `;