Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ecs): ECS optimized Windows images #3376

Merged
merged 15 commits into from
Jul 25, 2019
Merged
4 changes: 2 additions & 2 deletions packages/@aws-cdk/aws-ecs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,9 @@ cluster.addCapacity('DefaultAutoScalingGroupCapacity', {
const autoScalingGroup = new autoscaling.AutoScalingGroup(this, 'ASG', {
vpc,
instanceType: new ec2.InstanceType('t2.xlarge'),
machineImage: new EcsOptimizedAmi(),
machineImage: EcsOptimizedImage.amazonLinux(),
// Or use Amazon ECS-Optimized Amazon Linux 2 AMI
// machineImage: new EcsOptimizedAmi({ generation: ec2.AmazonLinuxGeneration.AmazonLinux2 }),
// machineImage: EcsOptimizedImage.amazonLinux2(),
desiredCapacity: 3,
// ... other options here ...
});
Expand Down
122 changes: 115 additions & 7 deletions packages/@aws-cdk/aws-ecs/lib/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import ec2 = require('@aws-cdk/aws-ec2');
import iam = require('@aws-cdk/aws-iam');
import cloudmap = require('@aws-cdk/aws-servicediscovery');
import ssm = require('@aws-cdk/aws-ssm');
import { Construct, Duration, IResource, Resource, Stack } from '@aws-cdk/core';
import { InstanceDrainHook } from './drain-hook/instance-drain-hook';
import { CfnCluster } from './ecs.generated';
import {Construct, Duration, IResource, Resource, Stack} from '@aws-cdk/core';
import {InstanceDrainHook} from './drain-hook/instance-drain-hook';
import {CfnCluster} from './ecs.generated';

/**
* The properties used to define an ECS cluster.
Expand Down Expand Up @@ -231,8 +231,23 @@ export class Cluster extends Resource implements ICluster {
}
}

/**
* ECS-optimized Windows version list
*/
export enum WindowsOptimizedVersion {
SERVER_2019 = '2019',
SERVER_2016 = '2016',
}

/*
* TODO:v2.0.0
* * remove `export` keyword
* * remove @depracted
*/
/**
* The properties that define which ECS-optimized AMI is used.
*
* @deprecated see {@link EcsOptimizedImage}
*/
export interface EcsOptimizedAmiProps {
/**
Expand All @@ -242,6 +257,13 @@ export interface EcsOptimizedAmiProps {
*/
readonly generation?: ec2.AmazonLinuxGeneration;

/**
* The Windows Server version to use.
*
* @default none, uses Linux generation
*/
readonly windowsVersion?: WindowsOptimizedVersion;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally, the generation and windowsVersion properties would be merged, but I didn't think it was worth the refactoring and breaking change.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can @deprecate generation in favor of amazonLinuxGeneration (still support generation for backwards compat and in the next MV we will remove)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts? Maybe in a subsequent PR? At least update #3398 with this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I didn't see your previous comment. I can add it to this PR

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we just replace it with an image union construct, to have the definition enforce the mutual exclusion?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So what's up with this? Didn't we say this is gone?


/**
* The ECS-optimized AMI variant to use.
*
Expand All @@ -250,11 +272,17 @@ export interface EcsOptimizedAmiProps {
readonly hardwareType?: AmiHardwareType;
}

/*
* TODO:v2.0.0 remove EcsOptimizedAmi
*/
/**
* Construct a Linux machine image from the latest ECS Optimized AMI published in SSM
* Construct a Linux or Windows machine image from the latest ECS Optimized AMI published in SSM
*
* @deprecated see {@link EcsOptimizedImage#amazonLinux}, {@link EcsOptimizedImage#amazonLinux} and {@link EcsOptimizedImage#windows}
*/
export class EcsOptimizedAmi implements ec2.IMachineImage {
private readonly generation: ec2.AmazonLinuxGeneration;
private readonly generation?: ec2.AmazonLinuxGeneration;
private readonly windowsVersion?: WindowsOptimizedVersion;
private readonly hwType: AmiHardwareType;

private readonly amiParameterName: string;
Expand All @@ -267,9 +295,17 @@ export class EcsOptimizedAmi implements ec2.IMachineImage {
if (props && props.generation) { // generation defined in the props object
if (props.generation === ec2.AmazonLinuxGeneration.AMAZON_LINUX && this.hwType !== AmiHardwareType.STANDARD) {
throw new Error(`Amazon Linux does not support special hardware type. Use Amazon Linux 2 instead`);
} else if (props.windowsVersion) {
throw new Error('"windowsVersion" and Linux image "generation" cannot be both set');
} else {
this.generation = props.generation;
}
} else if (props && props.windowsVersion) {
if (this.hwType !== AmiHardwareType.STANDARD) {
throw new Error('Windows Server does not support special hardware type');
} else {
this.windowsVersion = props.windowsVersion;
}
} else { // generation not defined in props object
// always default to Amazon Linux v2 regardless of HW
this.generation = ec2.AmazonLinuxGeneration.AMAZON_LINUX_2;
Expand All @@ -279,6 +315,7 @@ export class EcsOptimizedAmi implements ec2.IMachineImage {
this.amiParameterName = "/aws/service/ecs/optimized-ami/"
+ ( this.generation === ec2.AmazonLinuxGeneration.AMAZON_LINUX ? "amazon-linux/" : "" )
+ ( this.generation === ec2.AmazonLinuxGeneration.AMAZON_LINUX_2 ? "amazon-linux-2/" : "" )
+ ( this.windowsVersion ? `windows_server/${this.windowsVersion}/english/full/` : "" )
+ ( this.hwType === AmiHardwareType.GPU ? "gpu/" : "" )
+ ( this.hwType === AmiHardwareType.ARM ? "arm64/" : "" )
+ "recommended/image_id";
Expand All @@ -291,7 +328,78 @@ export class EcsOptimizedAmi implements ec2.IMachineImage {
const ami = ssm.StringParameter.valueForStringParameter(scope, this.amiParameterName);
return {
imageId: ami,
osType: ec2.OperatingSystemType.LINUX
osType: this.windowsVersion ? ec2.OperatingSystemType.WINDOWS : ec2.OperatingSystemType.LINUX
};
}
}

/**
* Construct a Linux or Windows machine image from the latest ECS Optimized AMI published in SSM
*/
export class EcsOptimizedImage implements ec2.IMachineImage {
/**
* Construct an Amazon Linux 2 image from the latest ECS Optimized AMI published in SSM
*
* @param hardwareType ECS-optimized AMI variant to use
*/
public static amazonLinux2(hardwareType = AmiHardwareType.STANDARD): EcsOptimizedImage {
return new EcsOptimizedImage({generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2, hardwareType});
}

/**
* Construct an Amazon Linux AMI image from the latest ECS Optimized AMI published in SSM
*/
public static amazonLinux(): EcsOptimizedImage {
return new EcsOptimizedImage({generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX});
}

/**
* Construct a Windows image from the latest ECS Optimized AMI published in SSM
*
* @param windowsVersion Windows Version to use
*/
public static windows(windowsVersion: WindowsOptimizedVersion): EcsOptimizedImage {
return new EcsOptimizedImage({windowsVersion});
}

private readonly generation?: ec2.AmazonLinuxGeneration;
private readonly windowsVersion?: WindowsOptimizedVersion;
private readonly hwType?: AmiHardwareType;

private readonly amiParameterName: string;

/**
* Constructs a new instance of the EcsOptimizedAmi class.
*/
private constructor(props: EcsOptimizedAmiProps) {
this.hwType = props && props.hardwareType;

if (props.windowsVersion) {
this.windowsVersion = props.windowsVersion;
} else if (props.generation) {
this.generation = props.generation;
} else {
throw new Error('This error should never be thrown');
}

// set the SSM parameter name
this.amiParameterName = "/aws/service/ecs/optimized-ami/"
+ ( this.generation === ec2.AmazonLinuxGeneration.AMAZON_LINUX ? "amazon-linux/" : "" )
+ ( this.generation === ec2.AmazonLinuxGeneration.AMAZON_LINUX_2 ? "amazon-linux-2/" : "" )
+ ( this.windowsVersion ? `windows_server/${this.windowsVersion}/english/full/` : "" )
+ ( this.hwType === AmiHardwareType.GPU ? "gpu/" : "" )
+ ( this.hwType === AmiHardwareType.ARM ? "arm64/" : "" )
+ "recommended/image_id";
}

/**
* Return the correct image
*/
public getImage(scope: Construct): ec2.MachineImageConfig {
const ami = ssm.StringParameter.valueForStringParameter(scope, this.amiParameterName);
return {
imageId: ami,
osType: this.windowsVersion ? ec2.OperatingSystemType.WINDOWS : ec2.OperatingSystemType.LINUX
};
}
}
Expand Down Expand Up @@ -510,7 +618,7 @@ export interface CloudMapNamespaceOptions {
export enum AmiHardwareType {

/**
* Use the Amazon ECS-optimized Amazon Linux 2 AMI.
* Use the standard Amazon ECS-optimized AMI.
*/
STANDARD = 'Standard',

Expand Down
Loading