Skip to content

Commit

Permalink
feat(@aws-cdk/aws-ec2): make GenericMachineImage use CfnMapping in an…
Browse files Browse the repository at this point in the history
… agnostic stack (#8759)
  • Loading branch information
wchaws committed Jan 17, 2021
1 parent ea67c54 commit c322545
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 27 deletions.
3 changes: 3 additions & 0 deletions packages/@aws-cdk/aws-ec2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,9 @@ examples of things you might want to use:
> [Runtime Context](https://docs.aws.amazon.com/cdk/latest/guide/context.html) in the CDK
> developer guide.
> NOTE: `MachineImage.genericLinux()`, `MachineImage.genericWindows()` will use `CfnMapping` in
> an agnostic stack.
## Special VPC configurations

### VPN connections to a VPC
Expand Down
56 changes: 31 additions & 25 deletions packages/@aws-cdk/aws-ec2/lib/machine-image.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as ssm from '@aws-cdk/aws-ssm';
import * as cxschema from '@aws-cdk/cloud-assembly-schema';
import { Construct, ContextProvider, Stack, Token } from '@aws-cdk/core';
import { Construct, ContextProvider, CfnMapping, Aws } from '@aws-cdk/core';
import * as cxapi from '@aws-cdk/cx-api';
import { UserData } from './user-data';
import { WindowsVersion } from './windows-versions';
Expand Down Expand Up @@ -363,22 +363,25 @@ export interface GenericWindowsImageProps {
* manually specify an AMI map.
*/
export class GenericLinuxImage implements IMachineImage {
constructor(private readonly amiMap: {[region: string]: string}, private readonly props: GenericLinuxImageProps = {}) {
}

public getImage(scope: Construct): MachineImageConfig {
const region = Stack.of(scope).region;
if (Token.isUnresolved(region)) {
throw new Error('Unable to determine AMI from AMI map since stack is region-agnostic');
}
private mapping: { [k1: string]: { [k2: string]: any } } = {};

const ami = region !== 'test-region' ? this.amiMap[region] : 'ami-12345';
if (!ami) {
throw new Error(`Unable to find AMI in AMI map: no AMI specified for region '${region}'`);
/**
* A Linux image where you specify the AMI ID for every region
*
* @param amiMap For every region where you are deploying the stack,
* specify the AMI ID for that region.
* @param props Customize the image by supplying additional props
*/
constructor(readonly amiMap: { [region: string]: string }, private readonly props: GenericLinuxImageProps = {}) {
for (const [region, ami] of Object.entries(amiMap)) {
this.mapping[region] = { ami };
}
}

public getImage(scope: Construct): MachineImageConfig {
const amiMap = new CfnMapping(scope, 'AmiMap', { mapping: this.mapping });
return {
imageId: ami,
imageId: amiMap.findInMap(Aws.REGION, 'ami'),
userData: this.props.userData ?? UserData.forLinux(),
osType: OperatingSystemType.LINUX,
};
Expand All @@ -391,22 +394,25 @@ export class GenericLinuxImage implements IMachineImage {
* Allows you to create a generic Windows EC2 , manually specify an AMI map.
*/
export class GenericWindowsImage implements IMachineImage {
constructor(private readonly amiMap: {[region: string]: string}, private readonly props: GenericWindowsImageProps = {}) {
}

public getImage(scope: Construct): MachineImageConfig {
const region = Stack.of(scope).region;
if (Token.isUnresolved(region)) {
throw new Error('Unable to determine AMI from AMI map since stack is region-agnostic');
}
private mapping: { [k1: string]: { [k2: string]: any } } = {};

const ami = region !== 'test-region' ? this.amiMap[region] : 'ami-12345';
if (!ami) {
throw new Error(`Unable to find AMI in AMI map: no AMI specified for region '${region}'`);
/**
* A Windows image where you specify the AMI ID for every region
*
* @param amiMap For every region where you are deploying the stack,
* specify the AMI ID for that region.
* @param props Customize the image by supplying additional props
*/
constructor(readonly amiMap: {[region: string]: string}, private readonly props: GenericWindowsImageProps = {}) {
for (const [region, ami] of Object.entries(amiMap)) {
this.mapping[region] = { ami };
}
}

public getImage(scope: Construct): MachineImageConfig {
const amiMap = new CfnMapping(scope, 'AmiMap', { mapping: this.mapping });
return {
imageId: ami,
imageId: amiMap.findInMap(Aws.REGION, 'ami'),
userData: this.props.userData ?? UserData.forWindows(),
osType: OperatingSystemType.WINDOWS,
};
Expand Down
37 changes: 36 additions & 1 deletion packages/@aws-cdk/aws-ec2/test/machine-image.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { expect as cdkExpect, matchTemplate, MatchStyle } from '@aws-cdk/assert';
import { App, Stack } from '@aws-cdk/core';
import * as ec2 from '../lib';

Expand All @@ -11,6 +12,29 @@ beforeEach(() => {
});
});

test('can make and use a Linux image', () => {
// WHEN
const image = new ec2.GenericLinuxImage({
testregion: 'ami-1234',
});

// THEN
const details = image.getImage(stack);
const expected = {
Mappings: {
AmiMap: {
testregion: {
ami: 'ami-1234',
},
},
},
};

cdkExpect(stack).to(matchTemplate(expected, MatchStyle.EXACT));
expect(stack.resolve(details.imageId)).toEqual({ 'Fn::FindInMap': ['AmiMap', { Ref: 'AWS::Region' }, 'ami'] });
expect(details.osType).toEqual(ec2.OperatingSystemType.LINUX);
});

test('can make and use a Windows image', () => {
// WHEN
const image = new ec2.GenericWindowsImage({
Expand All @@ -19,7 +43,18 @@ test('can make and use a Windows image', () => {

// THEN
const details = image.getImage(stack);
expect(details.imageId).toEqual('ami-1234');
const expected = {
Mappings: {
AmiMap: {
testregion: {
ami: 'ami-1234',
},
},
},
};

cdkExpect(stack).to(matchTemplate(expected, MatchStyle.EXACT));
expect(stack.resolve(details.imageId)).toEqual({ 'Fn::FindInMap': ['AmiMap', { Ref: 'AWS::Region' }, 'ami'] });
expect(details.osType).toEqual(ec2.OperatingSystemType.WINDOWS);
});

Expand Down
2 changes: 1 addition & 1 deletion packages/@aws-cdk/aws-ec2/test/vpc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -895,7 +895,7 @@ nodeunitShim({
// THEN
expect(stack).to(countResources('AWS::EC2::Instance', 3));
expect(stack).to(haveResource('AWS::EC2::Instance', {
ImageId: 'ami-1',
ImageId: { 'Fn::FindInMap': ['TheVPCPublicSubnet1NatInstanceAmiMap7BC63154', { Ref: 'AWS::Region' }, 'ami'] },
InstanceType: 'q86.mega',
SourceDestCheck: false,
}));
Expand Down

0 comments on commit c322545

Please sign in to comment.