Skip to content

Commit

Permalink
update example app flag description, correct subnet ID code, and gene…
Browse files Browse the repository at this point in the history
…ral cleanup
  • Loading branch information
jericht committed Sep 9, 2021
1 parent 91994f6 commit c07ff41
Show file tree
Hide file tree
Showing 13 changed files with 224 additions and 149 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def main():
sep_props = sep_stack.SEPStackProps(
docker_recipes_stage_path=os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir, 'stage'),
worker_machine_image=MachineImage.generic_linux(config.deadline_client_linux_ami_map),
deadline_resource_tracker_exists=config.deadline_resource_tracker_exists,
deadline_resource_tracker_exists=config.create_resource_tracker_role,
)
service = sep_stack.SEPStack(app, 'SEPStack', props=sep_props, env=env)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ def __init__(self):
# should match the one used for staging the render queue and usage based licensing recipes.
self.deadline_client_linux_ami_map: Mapping[str, str] = {'us-west-2': 'ami-0c8431fc72742c110'}

# Whether the DeadlineResourceTracker stack and supporting resources already exist in the account/region you are deploying to.
# Whether the DeadlineResourceTrackerAccessRole IAM role required by Deadline's Resource Tracker should be created in this CDK app.
#
# If this is false, resources required by the Deadline Resource Tracker will be deployed into your account.
# If this is true, these resources will be skipped.
self.deadline_resource_tracker_exists: bool = False
# If you have previously used this same AWS account with either Deadline's AWS Portal feature or Spot Event Plugin and had used the
# Deadline Resource Tracker, then you likely have this IAM role in your account already unless you have removed it.
#
# Note: Deadline's Resource Tracker only supports being used by a single Deadline Repository per AWS account.
self.create_resource_tracker_role: bool = True


config: AppConfig = AppConfig()
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ class SEPStackProps(StackProps):
docker_recipes_stage_path: str
# The IMachineImage to use for Workers (needs Deadline Client installed).
worker_machine_image: IMachineImage
# Whether the DeadlineResourceTracker stack and supporting resources already exist or not.
deadline_resource_tracker_exists: bool
# Whether the DeadlineResourceTrackerAccessRole IAM role required by Deadline's Resource Tracker should be created in this CDK app.
create_resource_tracker_role: bool


class SEPStack(Stack):
Expand Down Expand Up @@ -161,7 +161,7 @@ def __init__(self, scope: Construct, stack_id: str, *, props: SEPStackProps, **k
),
)

if not props.deadline_resource_tracker_exists:
if props.create_resource_tracker_role:
# Creates the Resource Tracker Access role. This role is required to exist in your account so the resource tracker will work properly
Role(
self,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,5 @@ new SEPStack(app, 'SEPStack', {
env,
dockerRecipesStagePath: path.join(__dirname, '..', pkg.config.stage_path), // Stage directory in config is relative, make it absolute
workerMachineImage: MachineImage.genericLinux(config.deadlineClientLinuxAmiMap),
deadlineResourceTrackerExists: config.deadlineResourceTrackerExists,
createResourceTrackerRole: config.createResourceTrackerRole,
});
10 changes: 5 additions & 5 deletions examples/deadline/All-In-AWS-Infrastructure-SEP/ts/bin/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ class AppConfig {
public readonly deadlineClientLinuxAmiMap: Record<string, string> = {['us-west-2']: 'ami-0c8431fc72742c110'};

/**
* Whether the DeadlineResourceTracker stack and supporting resources already exist in the account/region you are deploying to.
* Whether the DeadlineResourceTrackerAccessRole IAM role required by Deadline's Resource Tracker should be created in this CDK app.
*
* If this is false, resources required by the Deadline Resource Tracker will be deployed into your account.
* If this is true, these resources will be skipped.
* If you have previously used this same AWS account with either Deadline's AWS Portal feature or Spot Event Plugin and had used the
* Deadline Resource Tracker, then you likely have this IAM role in your account already unless you have removed it.
*
* @default false
* Note: Deadline's Resource Tracker only supports being used by a single Deadline Repository per AWS account.
*/
public readonly deadlineResourceTrackerExists: boolean = false;
public readonly createResourceTrackerRole: boolean = true;
}

export const config = new AppConfig();
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export interface SEPStackProps extends StackProps {
/**
* Whether the DeadlineResourceTracker stack and supporting resources already exist or not.
*/
readonly deadlineResourceTrackerExists: boolean;
readonly createResourceTrackerRole: boolean;
}

export class SEPStack extends Stack {
Expand Down Expand Up @@ -131,7 +131,7 @@ export class SEPStack extends Stack {
trafficEncryption,
});

if (!props.deadlineResourceTrackerExists) {
if (props.createResourceTrackerRole) {
// Creates the Resource Tracker Access role. This role is required to exist in your account so the resource tracker will work properly
new Role(this, 'ResourceTrackerRole', {
assumedBy: new ServicePrincipal('lambda.amazonaws.com'),
Expand Down
48 changes: 2 additions & 46 deletions packages/aws-rfdk/lib/deadline/lib/configure-spot-event-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import * as path from 'path';

import {
IVpc,
LaunchTemplate,
SubnetSelection,
SubnetType,
} from '@aws-cdk/aws-ec2';
Expand All @@ -23,15 +22,12 @@ import {
} from '@aws-cdk/aws-lambda';
import { RetentionDays } from '@aws-cdk/aws-logs';
import {
Aspects,
Construct,
CustomResource,
Duration,
IResolvable,
Lazy,
Stack,
Tag,
Tags,
} from '@aws-cdk/core';
import {
PluginSettings,
Expand Down Expand Up @@ -518,55 +514,15 @@ export class ConfigureSpotEventPlugin extends Construct {

/**
* Construct Spot Fleet Configurations from the provided fleet.
* Each congiguration is a mapping between one Deadline Group and one Spot Fleet Request Configuration.
* Each configuration is a mapping between one Deadline Group and one Spot Fleet Request Configuration.
*/
private generateSpotFleetRequestConfig(fleet: SpotEventPluginFleet): SpotFleetRequestConfiguration[] {
const { subnetIds } = fleet.subnets;
const subnetId = subnetIds.join(',');

const spotFleetRequestTagsToken = this.tagSpecifications(fleet, SpotFleetResourceType.SPOT_FLEET_REQUEST);

const launchTemplates: LaunchTemplate[] = fleet.instanceTypes.map((instanceType, idx) => {
const launchTemplate = new LaunchTemplate(this, `${fleet.node.id}${instanceType}LaunchTemplate${idx}`, {
blockDevices: fleet.blockDevices,
role: fleet.fleetInstanceRole,
machineImage: fleet.machineImage,
keyName: fleet.keyName,
securityGroup: fleet.securityGroups[0],
userData: fleet.userData,
instanceType,
});
if (fleet.securityGroups.length > 1) {
launchTemplate.connections.addSecurityGroup(...fleet.securityGroups.slice(1));
}

// Add tags that are added via the TagManager API
/* istanbul ignore if */
if (fleet.tags.hasTags()) {
const tags = fleet.tags.renderTags() as {[key: string]: string}[];
tags.forEach(tag => Tags.of(launchTemplate).add(tag.Key, tag.Value));
}

// Add tags that are added via the Tags API which uses Aspects and is a layer above the TagManager API
const tagAspects = Aspects.of(fleet).aspects.filter(aspect => aspect instanceof Tag) as Tag[];
tagAspects.forEach(tagAspect => Tags.of(launchTemplate).add(tagAspect.key, tagAspect.value));

return launchTemplate;
});

const spotFleetRequestProps: SpotFleetRequestProps = {
AllocationStrategy: fleet.allocationStrategy,
IamFleetRole: fleet.fleetRole.roleArn,
LaunchTemplateConfigs: launchTemplates.map(launchTemplate => {
return {
LaunchTemplateSpecification: {
Version: '$Latest',
LaunchTemplateId: launchTemplate.launchTemplateId,
LaunchTemplateName: launchTemplate.launchTemplateName,
},
Overrides: [{ SubnetId: subnetId }],
};
}),
LaunchTemplateConfigs: fleet._launchTemplateConfigs,
ReplaceUnhealthyInstances: true,
// In order to work with Deadline, the 'Target Capacity' of the Spot fleet Request is
// the maximum number of Workers that Deadline will start.
Expand Down
68 changes: 68 additions & 0 deletions packages/aws-rfdk/lib/deadline/lib/spot-event-plugin-fleet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
InstanceType,
ISecurityGroup,
IVpc,
LaunchTemplate,
LaunchTemplateSpecialVersions,
OperatingSystemType,
Port,
SecurityGroup,
Expand All @@ -37,6 +39,7 @@ import {
Expiration,
Stack,
TagManager,
Tags,
TagType,
} from '@aws-cdk/core';
import {
Expand All @@ -46,6 +49,9 @@ import {
import {
tagConstruct,
} from '../../core/lib/runtime-info';
import {
LaunchTemplateConfig,
} from '../../lambdas/nodejs/configure-spot-event-plugin';
import {
IRenderQueue,
} from './render-queue';
Expand Down Expand Up @@ -214,6 +220,13 @@ export interface SpotEventPluginFleetProps {
* @default: Not used.
*/
readonly userDataProvider?: IInstanceUserDataProvider;

/**
* Whether the instances in the Spot Fleet should be tracked by Deadline Resource Tracker.
*
* @default true
*/
readonly trackInstancesWithResourceTracker?: boolean;
}

/**
Expand Down Expand Up @@ -270,6 +283,7 @@ export interface ISpotEventPluginFleet extends IConnectable, IScriptHost, IGrant
* - An Amazon CloudWatch log group that contains the Deadline Worker, Deadline Launcher, and instance-startup logs for the instances
* in the fleet.
* - A security Group if security groups are not provided.
* - An EC2 Launch Template for the Spot Fleet.
*
* Security Considerations
* ------------------------
Expand Down Expand Up @@ -417,6 +431,16 @@ export class SpotEventPluginFleet extends Construct implements ISpotEventPluginF
*/
public readonly blockDevices?: BlockDevice[];

/**
* The Launch Template for this Spot Fleet. This launch template does not specify an instance type or subnet.
*/
public readonly launchTemplate: LaunchTemplate;

/**
* @internal
*/
public readonly _launchTemplateConfigs: any[];

constructor(scope: Construct, id: string, props: SpotEventPluginFleetProps) {
super(scope, id);

Expand Down Expand Up @@ -488,6 +512,9 @@ export class SpotEventPluginFleet extends Construct implements ISpotEventPluginF

// Tag deployed resources with RFDK meta-data
tagConstruct(this);

this.launchTemplate = this.createLaunchTemplate(props.trackInstancesWithResourceTracker ?? true);
this._launchTemplateConfigs = this.createLaunchTemplateConfigs();
}

/**
Expand All @@ -504,6 +531,47 @@ export class SpotEventPluginFleet extends Construct implements ISpotEventPluginF
other.connections.allowTo(this.connections, this.remoteControlPorts, 'Worker remote command listening port');
}

private createLaunchTemplate(resourceTrackerEnabled: boolean): LaunchTemplate {
const launchTemplate = new LaunchTemplate(this, 'LaunchTemplate', {
blockDevices: this.blockDevices,
role: this.fleetInstanceRole,
machineImage: this.machineImage,
keyName: this.keyName,
securityGroup: this.securityGroups[0],
userData: this.userData,
});
if (this.securityGroups.length > 1) {
launchTemplate.connections.addSecurityGroup(...this.securityGroups.slice(1));
}

Tags.of(launchTemplate).add(resourceTrackerEnabled ? 'DeadlineTrackedAWSResource' : 'DeadlineResourceTracker', 'SpotEventPlugin');

return launchTemplate;
}

private createLaunchTemplateConfigs(): LaunchTemplateConfig[] {
const launchTemplateConfigs: LaunchTemplateConfig[] = [];

// Create a launch template config for each instance type + subnet pair
this.instanceTypes.forEach(instanceType => {
this.subnets.subnetIds.forEach(subnetId => {
launchTemplateConfigs.push({
LaunchTemplateSpecification: {
Version: LaunchTemplateSpecialVersions.LATEST_VERSION,
LaunchTemplateId: this.launchTemplate.launchTemplateId,
LaunchTemplateName: this.launchTemplate.launchTemplateName,
},
Overrides: [{
InstanceType: instanceType.toString(),
SubnetId: subnetId,
}],
});
});
});

return launchTemplateConfigs;
}

private validateProps(props: SpotEventPluginFleetProps): void {
this.validateFleetInstanceRole(props.fleetInstanceRole);
this.validateInstanceTypes(props.instanceTypes);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import {
InstanceClass,
InstanceSize,
InstanceType,
LaunchTemplate,
SecurityGroup,
Vpc,
} from '@aws-cdk/aws-ec2';
Expand Down Expand Up @@ -232,28 +231,8 @@ describe('ConfigureSpotEventPlugin', () => {
{
LaunchTemplateSpecification: {
Version: '$Latest',
LaunchTemplateId: {
Ref: 'ConfigureSpotEventPluginSpotFleett2smallLaunchTemplate0EB718EA5',
},
LaunchTemplateId: stack.resolve(fleet.launchTemplate.launchTemplateId),
},
Overrides: [
{
SubnetId: {
'Fn::Join': [
'',
[
{
Ref: 'VpcPrivateSubnet1Subnet536B997A',
},
',',
{
Ref: 'VpcPrivateSubnet2Subnet3788AAA1',
},
],
],
},
},
],
},
],
TagSpecifications: arrayWith(
Expand Down Expand Up @@ -428,36 +407,6 @@ describe('ConfigureSpotEventPlugin', () => {
})));
});

test('creates LaunchTemplates for each fleet instance type', () => {
// WHEN
const sep = new ConfigureSpotEventPlugin(stack, 'ConfigureSpotEventPlugin', {
vpc,
renderQueue: renderQueue,
spotFleets: [
fleet,
],
});

// THEN
fleet.instanceTypes.forEach((instanceType, idx) => {
const launchTemplate = sep.node.tryFindChild(`${fleet.node.id}${instanceType}LaunchTemplate${idx}`) as LaunchTemplate;
expect(launchTemplate).toBeDefined();
cdkExpect(stack).to(haveResourceLike('AWS::IAM::InstanceProfile', {
Roles: [stack.resolve(launchTemplate.role?.roleName)],
}));
cdkExpect(stack).to(haveResourceLike('AWS::EC2::LaunchTemplate', {
LaunchTemplateData: objectLike({
ImageId: fleet.machineImage.getImage(stack).imageId,
SecurityGroupIds: fleet.securityGroups.map(sg => stack.resolve(sg.securityGroupId)),
UserData: {
'Fn::Base64': stack.resolve(fleet.userData.render()),
},
InstanceType: instanceType.toString(),
}),
}));
});
});

test('adds multiple fleet security groups to launch template', () => {
// GIVEN
const securityGroups = [
Expand All @@ -484,7 +433,7 @@ describe('ConfigureSpotEventPlugin', () => {
// THEN
cdkExpect(stack).to(haveResourceLike('AWS::EC2::LaunchTemplate', {
LaunchTemplateData: objectLike({
SecurityGroupIds: fleet2.securityGroups.map(sg => stack.resolve(sg.securityGroupId)),
SecurityGroupIds: securityGroups.map(sg => stack.resolve(sg.securityGroupId)),
}),
}));
});
Expand Down
Loading

0 comments on commit c07ff41

Please sign in to comment.