in packages/aws-rfdk/lib/deadline/lib/configure-spot-event-plugin.ts [377:533]
constructor(scope: Construct, id: string, props: ConfigureSpotEventPluginProps) {
super(scope, id);
if (ConfigureSpotEventPlugin.uniqueRenderQueues.has(props.renderQueue)) {
throw new Error('Only one ConfigureSpotEventPlugin construct is allowed per render queue.');
}
else {
ConfigureSpotEventPlugin.uniqueRenderQueues.add(props.renderQueue);
}
if (props.renderQueue instanceof RenderQueue) {
// We do not check the patch version, so it's set to 0.
const minimumVersion: Version = new Version([10, 1, 12, 0]);
if (props.renderQueue.version.isLessThan(minimumVersion)) {
throw new Error(`Minimum supported Deadline version for ${this.constructor.name} is ` +
`${minimumVersion.versionString}. ` +
`Received: ${props.renderQueue.version.versionString}.`);
}
if (props.spotFleets && props.spotFleets.length !== 0) {
// Always add Resource Tracker admin policy, even if props.configuration?.enableResourceTracker is false.
// This improves usability, as customers won't need to add this policy manually, if they
// enable Resource Tracker later in the Spot Event Plugin configuration (e.g., using Deadline Monitor and not RFDK).
props.renderQueue.addSEPPolicies(true);
const fleetRoles = props.spotFleets.map(sf => sf.fleetRole.roleArn);
const fleetInstanceRoles = props.spotFleets.map(sf => sf.fleetInstanceRole.roleArn);
new Policy(this, 'SpotEventPluginPolicy', {
statements: [
new PolicyStatement({
actions: [
'iam:PassRole',
],
resources: [...fleetRoles, ...fleetInstanceRoles],
conditions: {
StringLike: {
'iam:PassedToService': 'ec2.amazonaws.com',
},
},
}),
new PolicyStatement({
actions: [
'ec2:CreateTags',
],
resources: [
'arn:aws:ec2:*:*:spot-fleet-request/*',
'arn:aws:ec2:*:*:volume/*',
],
}),
],
roles: [
props.renderQueue.grantPrincipal as Role,
],
});
}
}
else {
throw new Error('The provided render queue is not an instance of RenderQueue class. Some functionality is not supported.');
}
const region = Construct.isConstruct(props.renderQueue) ? Stack.of(props.renderQueue).region : Stack.of(this).region;
const timeoutMins = 15;
const configurator = new LambdaFunction(this, 'Configurator', {
vpc: props.vpc,
vpcSubnets: props.vpcSubnets ?? { subnetType: SubnetType.PRIVATE_WITH_EGRESS },
description: `Used by a ConfigureSpotEventPlugin ${this.node.addr} to perform configuration of Deadline Spot Event Plugin`,
code: Code.fromAsset(path.join(__dirname, '..', '..', 'lambdas', 'nodejs'), {
}),
environment: {
DEBUG: 'false',
LAMBDA_TIMEOUT_MINS: timeoutMins.toString(),
},
runtime: Runtime.NODEJS_18_X,
handler: 'configure-spot-event-plugin.configureSEP',
timeout: Duration.minutes(timeoutMins),
logRetention: RetentionDays.ONE_WEEK,
});
configurator.connections.allowToDefaultPort(props.renderQueue);
props.renderQueue.certChain?.grantRead(configurator.grantPrincipal);
const pluginConfig: PluginSettings = {
AWSInstanceStatus: props.configuration?.awsInstanceStatus ?? SpotEventPluginDisplayInstanceStatus.DISABLED,
DeleteInterruptedSlaves: props.configuration?.deleteEC2SpotInterruptedWorkers ?? false,
DeleteTerminatedSlaves: props.configuration?.deleteSEPTerminatedWorkers ?? false,
IdleShutdown: props.configuration?.idleShutdown?.toMinutes({integral: true}) ?? 10,
Logging: props.configuration?.loggingLevel ?? SpotEventPluginLoggingLevel.STANDARD,
PreJobTaskMode: props.configuration?.preJobTaskMode ?? SpotEventPluginPreJobTaskMode.CONSERVATIVE,
Region: props.configuration?.region ?? region,
ResourceTracker: props.configuration?.enableResourceTracker ?? true,
StaggerInstances: props.configuration?.maximumInstancesStartedPerCycle ?? 50,
State: props.configuration?.state ?? SpotEventPluginState.GLOBAL_ENABLED,
StrictHardCap: props.configuration?.strictHardCap ?? false,
};
const spotFleetRequestConfigs = this.mergeSpotFleetRequestConfigs(props.spotFleets);
const deadlineGroups = Array.from(new Set(props.spotFleets?.map(fleet => fleet.deadlineGroups).reduce((p, c) => p.concat(c), [])));
const deadlinePools = Array.from(new Set(props.spotFleets?.map(fleet => fleet.deadlinePools).reduce((p, c) => p?.concat(c ?? []), [])));
const properties: SEPConfiguratorResourceProps = {
connection: {
hostname: props.renderQueue.endpoint.hostname,
port: props.renderQueue.endpoint.portAsString(),
protocol: props.renderQueue.endpoint.applicationProtocol,
caCertificateArn: props.renderQueue.certChain?.secretArn,
},
spotFleetRequestConfigurations: spotFleetRequestConfigs,
spotPluginConfigurations: pluginConfig,
deadlineGroups,
deadlinePools,
};
const resource = new CustomResource(this, 'Default', {
serviceToken: configurator.functionArn,
resourceType: 'Custom::RFDK_ConfigureSpotEventPlugin',
properties,
});
// Prevents a race during a stack-update.
resource.node.addDependency(configurator.role!);
// We need to add this dependency to avoid failures while deleting a Custom Resource:
// 'Custom Resource failed to stabilize in expected time. If you are using the Python cfn-response module,
// you may need to update your Lambda function code so that CloudFormation can attach the updated version.'.
// This happens, because Route Table Associations are deleted before the Custom Resource and we
// don't get a response from 'doDelete()'.
// Ideally, we would only want to add dependency on 'internetConnectivityEstablished' as shown below,
// but it seems that CDK misses dependencies on Route Table Associations in that case:
// const { internetConnectivityEstablished } = props.vpc.selectSubnets(props.vpcSubnets);
// resource.node.addDependency(internetConnectivityEstablished);
resource.node.addDependency(props.vpc);
// /* istanbul ignore next */
// Add a dependency on the render queue to ensure that
// it is running before we try to send requests to it.
resource.node.addDependency(props.renderQueue);
if (props.spotFleets && props.renderQueue.repository.secretsManagementSettings.enabled) {
props.spotFleets.forEach(spotFleet => {
if (spotFleet.defaultSubnets) {
Annotations.of(spotFleet).addWarning(
'Deadline Secrets Management is enabled on the Repository and VPC subnets have not been supplied. Using dedicated subnets is recommended. See https://github.com/aws/aws-rfdk/blobs/release/packages/aws-rfdk/lib/deadline/README.md#using-dedicated-subnets-for-deadline-components',
);
}
props.renderQueue.configureSecretsManagementAutoRegistration({
dependent: resource,
role: SecretsManagementRole.CLIENT,
registrationStatus: SecretsManagementRegistrationStatus.REGISTERED,
vpc: props.vpc,
vpcSubnets: spotFleet.subnets,
});
});
}
this.node.defaultChild = resource;
}