Skip to content

Commit

Permalink
chore(ecs): allow selecting Bottlerocket via machineImage (aws#16038)
Browse files Browse the repository at this point in the history
As introduced in aws#10097, Bottlerocket had to be explicitly (and only) selected
via setting `machineImageType`, which would pick an appropriate
`machineImage`.

Setting `machineImage` to `new BottleRocketImage()` would not be
sufficient, since the feature also requires configuring additional
UserData commands which are only added if `machineImageType` was set.

This method of configuration does not allow customization of the AMI,
such as introduced in aws#16021.

Instead, we reverse the logic: `machineImageType` may still be necessary
to autoconfigure UserData if we can't know what the machineImage is
(for example in case of a preconfigured AutoScalingGroup), but otherwise
is derived from what `machineImage` is being used.

We allow configuring both fields at the same time for the case when the
autodetection fails.


----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
rix0rrr authored and david-doyle-as24 committed Sep 7, 2021
1 parent 693cf23 commit 26b811f
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 26 deletions.
14 changes: 6 additions & 8 deletions packages/@aws-cdk/aws-ecs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,15 +141,13 @@ purpose-built by AWS for running containers. You can launch Amazon ECS container
The following example will create a capacity with self-managed Amazon EC2 capacity of 2 `c5.large` Linux instances running with `Bottlerocket` AMI.

Note that you must specify either a `machineImage` or `machineImageType`, at least one, not both.

The following example adds Bottlerocket capacity to the cluster:

```ts
cluster.addCapacity('bottlerocket-asg', {
minCapacity: 2,
instanceType: new ec2.InstanceType('c5.large'),
machineImageType: ecs.MachineImageType.BOTTLEROCKET,
machineImage: new ecs.BottleRocketImage(),
});
```

Expand Down Expand Up @@ -214,7 +212,7 @@ some supporting containers which are used to support the main container,
doings things like upload logs or metrics to monitoring services.

To run a task or service with Amazon EC2 launch type, use the `Ec2TaskDefinition`. For AWS Fargate tasks/services, use the
`FargateTaskDefinition`. For AWS ECS Anywhere use the `ExternalTaskDefinition`. These classes
`FargateTaskDefinition`. For AWS ECS Anywhere use the `ExternalTaskDefinition`. These classes
provide simplified APIs that only contain properties relevant for each specific launch type.

For a `FargateTaskDefinition`, specify the task size (`memoryLimitMiB` and `cpu`):
Expand Down Expand Up @@ -736,7 +734,7 @@ const service = new ecs.Ec2Service(stack, 'Service', {
});
```

With `bridge` or `host` network modes, only `SRV` DNS record types are supported.
With `bridge` or `host` network modes, only `SRV` DNS record types are supported.
By default, `SRV` DNS record types will target the default container and default
port. However, you may target a different container and port on the same ECS task:

Expand Down Expand Up @@ -893,7 +891,7 @@ const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', {
To enable using the inference accelerators in the containers, add `inferenceAcceleratorResources`
field and set it to a list of device names used for the inference accelerators. Each value in the
list should match a `DeviceName` for an `InferenceAccelerator` specified in the task definition.
list should match a `DeviceName` for an `InferenceAccelerator` specified in the task definition.
```ts
const inferenceAcceleratorResources = ['device1'];
Expand Down Expand Up @@ -927,11 +925,11 @@ const service = new ecs.Ec2Service(stack, 'Service', {
You can enable sending logs of your execute session commands to a CloudWatch log group or S3 bucket by configuring
the `executeCommandConfiguration` property for your cluster. The default configuration will send the
logs to the CloudWatch Logs using the `awslogs` log driver that is configured in your task definition. Please note,
when using your own `logConfiguration` the log group or S3 Bucket specified must already be created.
when using your own `logConfiguration` the log group or S3 Bucket specified must already be created.
To encrypt data using your own KMS Customer Key (CMK), you must create a CMK and provide the key in the `kmsKey` field
of the `executeCommandConfiguration`. To use this key for encrypting CloudWatch log data or S3 bucket, make sure to associate the key
to these resources on creation.
to these resources on creation.
```ts
const kmsKey = new kms.Key(stack, 'KmsKey');
Expand Down
31 changes: 22 additions & 9 deletions packages/@aws-cdk/aws-ecs/lib/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,12 +294,13 @@ export class Cluster extends Resource implements ICluster {
* @deprecated Use {@link Cluster.addAsgCapacityProvider} instead.
*/
public addCapacity(id: string, options: AddCapacityOptions): autoscaling.AutoScalingGroup {
if (options.machineImage && options.machineImageType) {
throw new Error('You can only specify either machineImage or machineImageType, not both.');
}
// 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());

const machineImage = options.machineImage ?? options.machineImageType === MachineImageType.BOTTLEROCKET ?
new BottleRocketImage() : new EcsOptimizedAmi();
const machineImageType = options.machineImageType ??
(isBottleRocketImage(machineImage) ? MachineImageType.BOTTLEROCKET : MachineImageType.AMAZON_LINUX_2);

const autoScalingGroup = new autoscaling.AutoScalingGroup(this, id, {
vpc: this.vpc,
Expand All @@ -309,7 +310,7 @@ export class Cluster extends Resource implements ICluster {
});

this.addAutoScalingGroup(autoScalingGroup, {
machineImageType: options.machineImageType,
machineImageType: machineImageType,
...options,
});

Expand Down Expand Up @@ -1016,11 +1017,18 @@ export interface AddAutoScalingGroupCapacityOptions {
*/
readonly topicEncryptionKey?: kms.IKey;


/**
* Specify the machine image type.
* What type of machine image this is
*
* Depending on the setting, different UserData will automatically be added
* to the `AutoScalingGroup` to configure it properly for use with ECS.
*
* @default MachineImageType.AMAZON_LINUX_2
* If you create an `AutoScalingGroup` yourself and are adding it via
* `addAutoScalingGroup()`, you must specify this value. If you are adding an
* `autoScalingGroup` via `addCapacity`, this value will be determined
* from the `machineImage` you pass.
*
* @default - Automatically determined from `machineImage`, if available, otherwise `MachineImageType.AMAZON_LINUX_2`.
*/
readonly machineImageType?: MachineImageType;
}
Expand Down Expand Up @@ -1356,3 +1364,8 @@ class MaybeCreateCapacityProviderAssociations implements IAspect {
}
}
}


function isBottleRocketImage(image: ec2.IMachineImage) {
return image instanceof BottleRocketImage;
}
32 changes: 23 additions & 9 deletions packages/@aws-cdk/aws-ecs/test/cluster.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1589,7 +1589,7 @@ nodeunitShim({
test.done();
},

'cluster capacity with bottlerocket AMI'(test: Test) {
'cluster capacity with bottlerocket AMI, by setting machineImageType'(test: Test) {
// GIVEN
const app = new cdk.App();
const stack = new cdk.Stack(app, 'test');
Expand Down Expand Up @@ -1682,20 +1682,34 @@ nodeunitShim({
test.done();
},

'throws when machineImage and machineImageType both specified'(test: Test) {
'cluster capacity with bottlerocket AMI, by setting the machineImage'(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('c5.large'),
machineImage: new ecs.BottleRocketImage(),
});

// THEN
test.throws(() => {
cluster.addCapacity('bottlerocket-asg', {
instanceType: new ec2.InstanceType('c5.large'),
machineImageType: ecs.MachineImageType.BOTTLEROCKET,
machineImage: new ecs.EcsOptimizedAmi(),
});
}, /You can only specify either machineImage or machineImageType, not both./);
expect(stack).to(haveResourceLike('AWS::AutoScaling::LaunchConfiguration', {
UserData: {
'Fn::Base64': {
'Fn::Join': [
'',
[
'\n[settings.ecs]\ncluster = "',
{
Ref: 'EcsCluster97242B84',
},
'"',
],
],
},
},
}));
test.done();
},

Expand Down

0 comments on commit 26b811f

Please sign in to comment.