Skip to content

Commit

Permalink
fix(ec2): MachineImage.genericLinux/Windows don't work in environment…
Browse files Browse the repository at this point in the history
…-agnostic stacks (#12546)

Close #8759


----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
wchaws authored Feb 9, 2021
1 parent 6597a09 commit fbe7e89
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 18 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 @@ -538,6 +538,9 @@ examples of things you might want to use:
> `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.
>
> `MachineImage.genericLinux()`, `MachineImage.genericWindows()` will use `CfnMapping` in
> an agnostic stack.
## Special VPC configurations

Expand Down
54 changes: 36 additions & 18 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 { ContextProvider, Stack, Token } from '@aws-cdk/core';
import { ContextProvider, CfnMapping, Aws, Stack, Token } 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 @@ -367,24 +367,33 @@ export interface GenericWindowsImageProps {
* manually specify an AMI map.
*/
export class GenericLinuxImage implements IMachineImage {
constructor(private readonly amiMap: {[region: string]: string}, private readonly props: GenericLinuxImageProps = {}) {
constructor(private readonly amiMap: { [region: string]: string }, private readonly props: GenericLinuxImageProps = {}) {
}

public getImage(scope: Construct): MachineImageConfig {
const userData = this.props.userData ?? UserData.forLinux();
const osType = OperatingSystemType.LINUX;
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');
const mapping: { [k1: string]: { [k2: string]: any } } = {};
for (const [rgn, ami] of Object.entries(this.amiMap)) {
mapping[rgn] = { ami };
}
const amiMap = new CfnMapping(scope, 'AmiMap', { mapping });
return {
imageId: amiMap.findInMap(Aws.REGION, 'ami'),
userData,
osType,
};
}

const ami = region !== 'test-region' ? this.amiMap[region] : 'ami-12345';
if (!ami) {
const imageId = region !== 'test-region' ? this.amiMap[region] : 'ami-12345';
if (!imageId) {
throw new Error(`Unable to find AMI in AMI map: no AMI specified for region '${region}'`);
}

return {
imageId: ami,
userData: this.props.userData ?? UserData.forLinux(),
osType: OperatingSystemType.LINUX,
imageId,
userData,
osType,
};
}
}
Expand All @@ -399,20 +408,29 @@ export class GenericWindowsImage implements IMachineImage {
}

public getImage(scope: Construct): MachineImageConfig {
const userData = this.props.userData ?? UserData.forWindows();
const osType = OperatingSystemType.WINDOWS;
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');
const mapping: { [k1: string]: { [k2: string]: any } } = {};
for (const [rgn, ami] of Object.entries(this.amiMap)) {
mapping[rgn] = { ami };
}
const amiMap = new CfnMapping(scope, 'AmiMap', { mapping });
return {
imageId: amiMap.findInMap(Aws.REGION, 'ami'),
userData,
osType,
};
}

const ami = region !== 'test-region' ? this.amiMap[region] : 'ami-12345';
if (!ami) {
const imageId = region !== 'test-region' ? this.amiMap[region] : 'ami-12345';
if (!imageId) {
throw new Error(`Unable to find AMI in AMI map: no AMI specified for region '${region}'`);
}

return {
imageId: ami,
userData: this.props.userData ?? UserData.forWindows(),
osType: OperatingSystemType.WINDOWS,
imageId,
userData,
osType,
};
}
}
Expand Down
63 changes: 63 additions & 0 deletions 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,43 @@ beforeEach(() => {
});
});

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

// THEN
const details = image.getImage(stack);
expect(details.imageId).toEqual('ami-1234');
expect(details.osType).toEqual(ec2.OperatingSystemType.LINUX);
});

test('can make and use a Linux image in agnostic stack', () => {
// WHEN
app = new App();
stack = new Stack(app, 'Stack');
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 @@ -23,6 +61,31 @@ test('can make and use a Windows image', () => {
expect(details.osType).toEqual(ec2.OperatingSystemType.WINDOWS);
});

test('can make and use a Windows image in agnostic stack', () => {
// WHEN
app = new App();
stack = new Stack(app, 'Stack');
const image = new ec2.GenericWindowsImage({
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.WINDOWS);
});

test('can make and use a Generic SSM image', () => {
// WHEN
const image = new ec2.GenericSSMParameterImage('testParam', ec2.OperatingSystemType.LINUX);
Expand Down

0 comments on commit fbe7e89

Please sign in to comment.