Skip to content

Commit

Permalink
Merge branch 'master' into feature/enable-nodejs14-inline
Browse files Browse the repository at this point in the history
  • Loading branch information
mergify[bot] authored Aug 24, 2021
2 parents e981d26 + 9c39bcb commit 9d87307
Show file tree
Hide file tree
Showing 43 changed files with 2,102 additions and 87 deletions.
4 changes: 2 additions & 2 deletions packages/@aws-cdk/aws-apigateway/lib/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export interface IModel {
* }
*
* @see https://docs.amazonaws.cn/en_us/apigateway/latest/developerguide/models-mappings.html#models-mappings-models
* @deprecated You should use @see Model.EMPTY_MODEL
* @deprecated You should use Model.EMPTY_MODEL
*/
export class EmptyModel implements IModel {
public readonly modelId = 'Empty';
Expand All @@ -51,7 +51,7 @@ export class EmptyModel implements IModel {
* "message" : { "type" : "string" }
* }
* }
* @deprecated You should use @see Model.ERROR_MODEL
* @deprecated You should use Model.ERROR_MODEL
*/
export class ErrorModel implements IModel {
public readonly modelId = 'Error';
Expand Down
4 changes: 4 additions & 0 deletions packages/@aws-cdk/aws-apigatewayv2/lib/common/domain-name.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ export class DomainName extends Resource implements IDomainName {
constructor(scope: Construct, id: string, props: DomainNameProps) {
super(scope, id);

if (props.domainName === '') {
throw new Error('empty string for domainName not allowed');
}

const domainNameProps: CfnDomainNameProps = {
domainName: props.domainName,
domainNameConfigurations: [
Expand Down
16 changes: 16 additions & 0 deletions packages/@aws-cdk/aws-apigatewayv2/test/http/domain-name.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,22 @@ describe('DomainName', () => {
});
});

test('throws when domainName is empty string', () => {
// GIVEN
const stack = new Stack();

// WHEN
const t = () => {
new DomainName(stack, 'DomainName', {
domainName: '',
certificate: Certificate.fromCertificateArn(stack, 'cert', certArn),
});
};

// THEN
expect(t).toThrow(/empty string for domainName not allowed/);
});

test('import domain name correctly', () => {
// GIVEN
const stack = new Stack();
Expand Down
4 changes: 2 additions & 2 deletions packages/@aws-cdk/aws-appsync/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ rdsDS.createResolver({
}
`),
responseMappingTemplate: MappingTemplate.fromString(`
$util.rds.toJsonObject($ctx.result)
$utils.toJson($utils.rds.toJsonObject($ctx.result)[0])
`),
});

Expand All @@ -157,7 +157,7 @@ rdsDS.createResolver({
}
`),
responseMappingTemplate: MappingTemplate.fromString(`
$util.rds.toJsonObject($ctx.result)
$utils.toJson($utils.rds.toJsonObject($ctx.result)[1][0])
`),
});
```
Expand Down
2 changes: 1 addition & 1 deletion packages/@aws-cdk/aws-cloudfront/lib/cache-policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ export class CacheHeaderBehavior {
return new CacheHeaderBehavior('whitelist', headers);
}

/** If the no headers will be passed, or an allow list of headers. */
/** If no headers will be passed, or an allow list of headers. */
public readonly behavior: string;
/** The headers for the allow/deny list, if applicable. */
public readonly headers?: string[];
Expand Down
63 changes: 62 additions & 1 deletion packages/@aws-cdk/aws-ec2/lib/vpc-endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -326,9 +326,70 @@ export class InterfaceVpcEndpointAwsService implements IInterfaceVpcEndpointServ
const region = Lazy.uncachedString({
produce: (context) => Stack.of(context.scope).region,
});
this.name = `${prefix || 'com.amazonaws'}.${region}.${name}`;
const defaultEndpointPrefix = Lazy.uncachedString({
produce: (context) => {
const regionName = Stack.of(context.scope).region;
return this.getDefaultEndpointPrefix(name, regionName);
},
});
const defaultEndpointSuffix = Lazy.uncachedString({
produce: (context) => {
const regionName = Stack.of(context.scope).region;
return this.getDefaultEndpointSuffix(name, regionName);
},
});

this.name = `${prefix || defaultEndpointPrefix}.${region}.${name}${defaultEndpointSuffix}`;
this.port = port || 443;
}

/**
* Get the endpoint prefix for the service in the specified region
* because the prefix for some of the services in cn-north-1 and cn-northwest-1 are different
*
* For future maintenance, the vpc endpoint services could be fetched using AWS CLI Commmand:
* aws ec2 describe-vpc-endpoint-services
*/
private getDefaultEndpointPrefix(name: string, region: string) {
const VPC_ENDPOINT_SERVICE_EXCEPTIONS: { [region: string]: string[] } = {
'cn-north-1': ['application-autoscaling', 'athena', 'autoscaling', 'awsconnector', 'cassandra',
'cloudformation', 'codedeploy-commands-secure', 'databrew', 'dms', 'ebs', 'ec2', 'ecr.api', 'ecr.dkr',
'elasticbeanstalk', 'elasticfilesystem', 'elasticfilesystem-fips', 'execute-api', 'imagebuilder',
'iotsitewise.api', 'iotsitewise.data', 'kinesis-streams', 'lambda', 'license-manager', 'monitoring',
'rds', 'redshift', 'redshift-data', 's3', 'sagemaker.api', 'sagemaker.featurestore-runtime',
'sagemaker.runtime', 'servicecatalog', 'sms', 'sqs', 'states', 'sts', 'synthetics', 'transcribe',
'transcribestreaming', 'transfer', 'xray'],
'cn-northwest-1': ['application-autoscaling', 'athena', 'autoscaling', 'awsconnector', 'cassandra',
'cloudformation', 'codedeploy-commands-secure', 'databrew', 'dms', 'ebs', 'ec2', 'ecr.api', 'ecr.dkr',
'elasticbeanstalk', 'elasticfilesystem', 'elasticfilesystem-fips', 'execute-api', 'imagebuilder',
'kinesis-streams', 'lambda', 'license-manager', 'monitoring', 'rds', 'redshift', 'redshift-data', 's3',
'sagemaker.api', 'sagemaker.featurestore-runtime', 'sagemaker.runtime', 'servicecatalog', 'sms', 'sqs',
'states', 'sts', 'synthetics', 'transcribe', 'transcribestreaming', 'transfer', 'workspaces', 'xray'],
};
if (VPC_ENDPOINT_SERVICE_EXCEPTIONS[region]?.includes(name)) {
return 'cn.com.amazonaws';
} else {
return 'com.amazonaws';
}
}

/**
* Get the endpoint suffix for the service in the specified region.
* In cn-north-1 and cn-northwest-1, the vpc endpoint of transcribe is:
* cn.com.amazonaws.cn-north-1.transcribe.cn
* cn.com.amazonaws.cn-northwest-1.transcribe.cn
* so suffix '.cn' should be return in these scenarios.
*
* For future maintenance, the vpc endpoint services could be fetched using AWS CLI Commmand:
* aws ec2 describe-vpc-endpoint-services
*/
private getDefaultEndpointSuffix(name: string, region: string) {
const VPC_ENDPOINT_SERVICE_EXCEPTIONS: { [region: string]: string[] } = {
'cn-north-1': ['transcribe'],
'cn-northwest-1': ['transcribe'],
};
return VPC_ENDPOINT_SERVICE_EXCEPTIONS[region]?.includes(name) ? '.cn' : '';
}
}

/**
Expand Down
119 changes: 119 additions & 0 deletions packages/@aws-cdk/aws-ec2/test/vpc-endpoint.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -607,5 +607,124 @@ nodeunitShim({
}));
test.done();
},
'test vpc interface endpoint with cn.com.amazonaws prefix can be created correctly in cn-north-1'(test: Test) {
//GIVEN
const stack = new Stack(undefined, 'TestStack', { env: { account: '123456789012', region: 'cn-north-1' } });
const vpc = new Vpc(stack, 'VPC');

//WHEN
vpc.addInterfaceEndpoint('ECR Endpoint', {
service: InterfaceVpcEndpointAwsService.ECR,
});

//THEN
expect(stack).to(haveResource('AWS::EC2::VPCEndpoint', {
ServiceName: 'cn.com.amazonaws.cn-north-1.ecr.api',
}));

test.done();
},
'test vpc interface endpoint with cn.com.amazonaws prefix can be created correctly in cn-northwest-1'(test: Test) {
//GIVEN
const stack = new Stack(undefined, 'TestStack', { env: { account: '123456789012', region: 'cn-northwest-1' } });
const vpc = new Vpc(stack, 'VPC');

//WHEN
vpc.addInterfaceEndpoint('Lambda Endpoint', {
service: InterfaceVpcEndpointAwsService.LAMBDA,
});

//THEN
expect(stack).to(haveResource('AWS::EC2::VPCEndpoint', {
ServiceName: 'cn.com.amazonaws.cn-northwest-1.lambda',
}));

test.done();
},
'test vpc interface endpoint without cn.com.amazonaws prefix can be created correctly in cn-north-1'(test: Test) {
//GIVEN
const stack = new Stack(undefined, 'TestStack', { env: { account: '123456789012', region: 'cn-north-1' } });
const vpc = new Vpc(stack, 'VPC');

//WHEN
vpc.addInterfaceEndpoint('ECS Endpoint', {
service: InterfaceVpcEndpointAwsService.ECS,
});

//THEN
expect(stack).to(haveResource('AWS::EC2::VPCEndpoint', {
ServiceName: 'com.amazonaws.cn-north-1.ecs',
}));

test.done();
},
'test vpc interface endpoint without cn.com.amazonaws prefix can be created correctly in cn-northwest-1'(test: Test) {
//GIVEN
const stack = new Stack(undefined, 'TestStack', { env: { account: '123456789012', region: 'cn-northwest-1' } });
const vpc = new Vpc(stack, 'VPC');

//WHEN
vpc.addInterfaceEndpoint('Glue Endpoint', {
service: InterfaceVpcEndpointAwsService.GLUE,
});

//THEN
expect(stack).to(haveResource('AWS::EC2::VPCEndpoint', {
ServiceName: 'com.amazonaws.cn-northwest-1.glue',
}));

test.done();
},
'test vpc interface endpoint for transcribe can be created correctly in non-china regions'(test: Test) {
//GIVEN
const stack = new Stack(undefined, 'TestStack', { env: { account: '123456789012', region: 'us-east-1' } });
const vpc = new Vpc(stack, 'VPC');

//WHEN
vpc.addInterfaceEndpoint('Transcribe Endpoint', {
service: InterfaceVpcEndpointAwsService.TRANSCRIBE,
});

//THEN
expect(stack).to(haveResource('AWS::EC2::VPCEndpoint', {
ServiceName: 'com.amazonaws.us-east-1.transcribe',
}));

test.done();
},
'test vpc interface endpoint for transcribe can be created correctly in cn-north-1'(test: Test) {
//GIVEN
const stack = new Stack(undefined, 'TestStack', { env: { account: '123456789012', region: 'cn-north-1' } });
const vpc = new Vpc(stack, 'VPC');

//WHEN
vpc.addInterfaceEndpoint('Transcribe Endpoint', {
service: InterfaceVpcEndpointAwsService.TRANSCRIBE,
});

//THEN
expect(stack).to(haveResource('AWS::EC2::VPCEndpoint', {
ServiceName: 'cn.com.amazonaws.cn-north-1.transcribe.cn',
}));

test.done();
},
'test vpc interface endpoint for transcribe can be created correctly in cn-northwest-1'(test: Test) {
//GIVEN
const stack = new Stack(undefined, 'TestStack', { env: { account: '123456789012', region: 'cn-northwest-1' } });
const vpc = new Vpc(stack, 'VPC');

//WHEN
vpc.addInterfaceEndpoint('Transcribe Endpoint', {
service: InterfaceVpcEndpointAwsService.TRANSCRIBE,
});

//THEN
expect(stack).to(haveResource('AWS::EC2::VPCEndpoint', {
ServiceName: 'cn.com.amazonaws.cn-northwest-1.transcribe.cn',
}));

test.done();
},
},
});
13 changes: 10 additions & 3 deletions packages/@aws-cdk/aws-ecs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,6 @@ If you omit the property `vpc`, the construct will create a new VPC with two AZs
[Bottlerocket](https://aws.amazon.com/bottlerocket/) is a Linux-based open source operating system that is
purpose-built by AWS for running containers. You can launch Amazon ECS container instances with the Bottlerocket AMI.

> **NOTICE**: The Bottlerocket AMI is in developer preview release for Amazon ECS and is subject to change.
The following example will create a capacity with self-managed Amazon EC2 capacity of 2 `c5.large` Linux instances running with `Bottlerocket` AMI.

The following example adds Bottlerocket capacity to the cluster:
Expand All @@ -164,7 +162,16 @@ cluster.addCapacity('graviton-cluster', {
instanceType: new ec2.InstanceType('c6g.large'),
machineImage: ecs.EcsOptimizedImage.amazonLinux2(ecs.AmiHardwareType.ARM),
});
```

Bottlerocket is also supported:

```ts
cluster.addCapacity('graviton-cluster', {
minCapacity: 2,
instanceType: new ec2.InstanceType('c6g.large'),
machineImage: ecs.MachineImageType.BOTTLEROCKET,
});
```

### Spot Instances
Expand Down Expand Up @@ -854,7 +861,7 @@ cluster.addAsgCapacityProvider(capacityProvider);
const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef');

taskDefinition.addContainer('web', {
image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample',
image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
memoryReservationMiB: 256,
});

Expand Down
19 changes: 17 additions & 2 deletions packages/@aws-cdk/aws-ecs/lib/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,9 @@ export class Cluster extends Resource implements ICluster {
// Do 2-way defaulting here: if the machineImageType is BOTTLEROCKET, pick the right AMI.
// Otherwise, determine the machineImageType from the given AMI.
const machineImage = options.machineImage ??
(options.machineImageType === MachineImageType.BOTTLEROCKET ? new BottleRocketImage() : new EcsOptimizedAmi());
(options.machineImageType === MachineImageType.BOTTLEROCKET ? new BottleRocketImage({
architecture: options.instanceType.architecture,
}) : new EcsOptimizedAmi());

const machineImageType = options.machineImageType ??
(isBottleRocketImage(machineImage) ? MachineImageType.BOTTLEROCKET : MachineImageType.AMAZON_LINUX_2);
Expand Down Expand Up @@ -767,6 +769,13 @@ export interface BottleRocketImageProps {
* @default - BottlerocketEcsVariant.AWS_ECS_1
*/
readonly variant?: BottlerocketEcsVariant;

/**
* The CPU architecture
*
* @default - x86_64
*/
readonly architecture?: ec2.InstanceArchitecture;
}

/**
Expand All @@ -779,14 +788,20 @@ export class BottleRocketImage implements ec2.IMachineImage {
*/
private readonly variant: string;

/**
* Instance architecture
*/
private readonly architecture: ec2.InstanceArchitecture;

/**
* Constructs a new instance of the BottleRocketImage class.
*/
public constructor(props: BottleRocketImageProps = {}) {
this.variant = props.variant ?? BottlerocketEcsVariant.AWS_ECS_1;
this.architecture = props.architecture ?? ec2.InstanceArchitecture.X86_64;

// set the SSM parameter name
this.amiParameterName = `/aws/service/bottlerocket/${this.variant}/x86_64/latest/image_id`;
this.amiParameterName = `/aws/service/bottlerocket/${this.variant}/${this.architecture}/latest/image_id`;
}

/**
Expand Down
31 changes: 30 additions & 1 deletion packages/@aws-cdk/aws-ecs/test/cluster.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1682,7 +1682,36 @@ nodeunitShim({
test.done();
},

'cluster capacity with bottlerocket AMI, by setting the machineImage'(test: Test) {
'correct bottlerocket AMI for ARM64 architecture'(test: Test) {
// GIVEN
const app = new cdk.App();
const stack = new cdk.Stack(app, 'test');

const cluster = new ecs.Cluster(stack, 'EcsCluster');
cluster.addCapacity('bottlerocket-asg', {
instanceType: new ec2.InstanceType('m6g.large'),
machineImageType: ecs.MachineImageType.BOTTLEROCKET,
});

// THEN
expect(stack).to(haveResource('AWS::AutoScaling::LaunchConfiguration', {
ImageId: {
Ref: 'SsmParameterValueawsservicebottlerocketawsecs1arm64latestimageidC96584B6F00A464EAD1953AFF4B05118Parameter',
},
}));

const assembly = app.synth();
const template = assembly.getStackByName(stack.stackName).template;
test.deepEqual(template.Parameters, {
SsmParameterValueawsservicebottlerocketawsecs1arm64latestimageidC96584B6F00A464EAD1953AFF4B05118Parameter: {
Type: 'AWS::SSM::Parameter::Value<String>',
Default: '/aws/service/bottlerocket/aws-ecs-1/arm64/latest/image_id',
},
});
test.done();
},

'throws when machineImage and machineImageType both specified'(test: Test) {
// GIVEN
const app = new cdk.App();
const stack = new cdk.Stack(app, 'test');
Expand Down
Loading

0 comments on commit 9d87307

Please sign in to comment.