aws-rfdk
Version:
Package for core render farm constructs
742 lines • 89.5 kB
JavaScript
"use strict";
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
Object.defineProperty(exports, "__esModule", { value: true });
const aws_cdk_lib_1 = require("aws-cdk-lib");
const assertions_1 = require("aws-cdk-lib/assertions");
const aws_ec2_1 = require("aws-cdk-lib/aws-ec2");
const aws_ecs_1 = require("aws-cdk-lib/aws-ecs");
const aws_iam_1 = require("aws-cdk-lib/aws-iam");
const aws_route53_1 = require("aws-cdk-lib/aws-route53");
const core_1 = require("../../core");
const runtime_info_1 = require("../../core/lib/runtime-info");
const lib_1 = require("../lib");
const spot_event_plugin_fleet_1 = require("../lib/spot-event-plugin-fleet");
const test_helper_1 = require("./test-helper");
describe('ConfigureSpotEventPlugin', () => {
let stack;
let vpc;
let region;
let renderQueue;
let version;
let app;
let fleet;
let groupName;
const workerMachineImage = new aws_ec2_1.GenericWindowsImage({
'us-east-1': 'ami-any',
});
beforeEach(() => {
region = 'us-east-1';
app = new aws_cdk_lib_1.App();
stack = new aws_cdk_lib_1.Stack(app, 'stack', {
env: {
region,
},
});
vpc = new aws_ec2_1.Vpc(stack, 'Vpc');
version = new lib_1.VersionQuery(stack, 'Version');
renderQueue = new lib_1.RenderQueue(stack, 'RQ', {
vpc,
images: { remoteConnectionServer: aws_ecs_1.ContainerImage.fromAsset(__dirname) },
repository: new lib_1.Repository(stack, 'Repository', {
vpc,
version,
secretsManagementSettings: { enabled: false },
}),
trafficEncryption: { externalTLS: { enabled: false } },
version,
});
groupName = 'group_name1';
fleet = new spot_event_plugin_fleet_1.SpotEventPluginFleet(stack, 'SpotFleet', {
vpc,
renderQueue: renderQueue,
deadlineGroups: [
groupName,
],
instanceTypes: [
aws_ec2_1.InstanceType.of(aws_ec2_1.InstanceClass.T2, aws_ec2_1.InstanceSize.SMALL),
],
workerMachineImage,
maxCapacity: 1,
});
});
describe('creates a custom resource', () => {
test('with default spot event plugin properties', () => {
// WHEN
new lib_1.ConfigureSpotEventPlugin(stack, 'ConfigureSpotEventPlugin', {
vpc,
renderQueue: renderQueue,
spotFleets: [
fleet,
],
});
// THEN
assertions_1.Template.fromStack(stack).hasResourceProperties('Custom::RFDK_ConfigureSpotEventPlugin', assertions_1.Match.objectLike({
spotPluginConfigurations: assertions_1.Match.objectLike({
AWSInstanceStatus: 'Disabled',
DeleteInterruptedSlaves: false,
DeleteTerminatedSlaves: false,
IdleShutdown: 10,
Logging: 'Standard',
PreJobTaskMode: 'Conservative',
Region: aws_cdk_lib_1.Stack.of(renderQueue).region,
ResourceTracker: true,
StaggerInstances: 50,
State: 'Global Enabled',
StrictHardCap: false,
}),
}));
});
test('with custom spot event plugin properties', () => {
// GIVEN
const configuration = {
awsInstanceStatus: lib_1.SpotEventPluginDisplayInstanceStatus.EXTRA_INFO_0,
deleteEC2SpotInterruptedWorkers: true,
deleteSEPTerminatedWorkers: true,
idleShutdown: aws_cdk_lib_1.Duration.minutes(20),
loggingLevel: lib_1.SpotEventPluginLoggingLevel.VERBOSE,
preJobTaskMode: lib_1.SpotEventPluginPreJobTaskMode.NORMAL,
region: 'us-west-2',
enableResourceTracker: false,
maximumInstancesStartedPerCycle: 10,
state: lib_1.SpotEventPluginState.DISABLED,
strictHardCap: true,
};
// WHEN
new lib_1.ConfigureSpotEventPlugin(stack, 'ConfigureSpotEventPlugin', {
vpc,
renderQueue: renderQueue,
spotFleets: [
fleet,
],
configuration,
});
// THEN
assertions_1.Template.fromStack(stack).hasResourceProperties('Custom::RFDK_ConfigureSpotEventPlugin', assertions_1.Match.objectLike({
spotPluginConfigurations: assertions_1.Match.objectLike({
AWSInstanceStatus: 'ExtraInfo0',
DeleteInterruptedSlaves: true,
DeleteTerminatedSlaves: true,
IdleShutdown: 20,
Logging: 'Verbose',
PreJobTaskMode: 'Normal',
Region: 'us-west-2',
ResourceTracker: false,
StaggerInstances: 10,
State: 'Disabled',
StrictHardCap: true,
}),
}));
});
test('without spot fleets', () => {
// WHEN
new lib_1.ConfigureSpotEventPlugin(stack, 'ConfigureSpotEventPlugin', {
vpc,
renderQueue: renderQueue,
});
// THEN
assertions_1.Template.fromStack(stack).hasResourceProperties('Custom::RFDK_ConfigureSpotEventPlugin', {
spotFleetRequestConfigurations: assertions_1.Match.absent(),
});
});
test('provides RQ connection parameters to custom resource', () => {
// WHEN
new lib_1.ConfigureSpotEventPlugin(stack, 'ConfigureSpotEventPlugin', {
vpc,
renderQueue: renderQueue,
spotFleets: [
fleet,
],
});
// THEN
assertions_1.Template.fromStack(stack).hasResourceProperties('Custom::RFDK_ConfigureSpotEventPlugin', assertions_1.Match.objectLike({
connection: assertions_1.Match.objectLike({
hostname: stack.resolve(renderQueue.endpoint.hostname),
port: stack.resolve(renderQueue.endpoint.portAsString()),
protocol: stack.resolve(renderQueue.endpoint.applicationProtocol.toString()),
}),
}));
});
test('with default spot fleet request configuration', () => {
// WHEN
new lib_1.ConfigureSpotEventPlugin(stack, 'ConfigureSpotEventPlugin', {
vpc,
renderQueue: renderQueue,
spotFleets: [
fleet,
],
});
const rfdkTag = (0, runtime_info_1.tagFields)(fleet);
// THEN
assertions_1.Template.fromStack(stack).hasResourceProperties('Custom::RFDK_ConfigureSpotEventPlugin', {
spotFleetRequestConfigurations: {
[groupName]: {
AllocationStrategy: 'lowestPrice',
IamFleetRole: stack.resolve(fleet.fleetRole.roleArn),
LaunchTemplateConfigs: assertions_1.Match.arrayWith([
assertions_1.Match.objectLike({
LaunchTemplateSpecification: {
Version: stack.resolve(fleet.launchTemplate.versionNumber),
LaunchTemplateId: stack.resolve(fleet.launchTemplate.launchTemplateId),
},
}),
]),
TagSpecifications: assertions_1.Match.arrayWith([
assertions_1.Match.objectLike({
ResourceType: 'spot-fleet-request',
Tags: assertions_1.Match.arrayWith([
{
Key: rfdkTag.name,
Value: rfdkTag.value,
},
]),
}),
]),
},
},
});
});
test('adds policies to the render queue', () => {
// WHEN
new lib_1.ConfigureSpotEventPlugin(stack, 'ConfigureSpotEventPlugin', {
vpc,
renderQueue: renderQueue,
spotFleets: [
fleet,
],
});
// THEN
(0, test_helper_1.resourcePropertiesCountIs)(stack, 'AWS::IAM::Role', {
ManagedPolicyArns: assertions_1.Match.arrayWith([
stack.resolve(aws_iam_1.ManagedPolicy.fromAwsManagedPolicyName('AWSThinkboxDeadlineSpotEventPluginAdminPolicy').managedPolicyArn),
stack.resolve(aws_iam_1.ManagedPolicy.fromAwsManagedPolicyName('AWSThinkboxDeadlineResourceTrackerAdminPolicy').managedPolicyArn),
]),
}, 1);
assertions_1.Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', {
PolicyDocument: {
Statement: [
{
Action: 'iam:PassRole',
Condition: {
StringLike: {
'iam:PassedToService': 'ec2.amazonaws.com',
},
},
Effect: 'Allow',
Resource: [
stack.resolve(fleet.fleetRole.roleArn),
stack.resolve(fleet.fleetInstanceRole.roleArn),
],
},
{
Action: 'ec2:CreateTags',
Effect: 'Allow',
Resource: [
'arn:aws:ec2:*:*:spot-fleet-request/*',
'arn:aws:ec2:*:*:volume/*',
],
},
],
},
Roles: [{
Ref: 'RQRCSTaskTaskRole00DC9B43',
}],
});
});
test('adds resource tracker policy even if rt disabled', () => {
// WHEN
new lib_1.ConfigureSpotEventPlugin(stack, 'ConfigureSpotEventPlugin', {
vpc,
renderQueue: renderQueue,
spotFleets: [
fleet,
],
configuration: {
enableResourceTracker: false,
},
});
// THEN
assertions_1.Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', {
ManagedPolicyArns: assertions_1.Match.arrayWith([
stack.resolve(aws_iam_1.ManagedPolicy.fromAwsManagedPolicyName('AWSThinkboxDeadlineResourceTrackerAdminPolicy').managedPolicyArn),
]),
});
});
test.each([
undefined,
[],
])('without spot fleet', (noFleets) => {
// WHEN
new lib_1.ConfigureSpotEventPlugin(stack, 'ConfigureSpotEventPlugin', {
vpc,
renderQueue: renderQueue,
spotFleets: noFleets,
});
// THEN
assertions_1.Template.fromStack(stack).hasResourceProperties('Custom::RFDK_ConfigureSpotEventPlugin', assertions_1.Match.objectLike({
spotFleetRequestConfigurations: assertions_1.Match.absent(),
}));
assertions_1.Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', assertions_1.Match.not({
ManagedPolicyArns: assertions_1.Match.arrayWith([
stack.resolve(aws_iam_1.ManagedPolicy.fromAwsManagedPolicyName('AWSThinkboxDeadlineSpotEventPluginAdminPolicy').managedPolicyArn),
stack.resolve(aws_iam_1.ManagedPolicy.fromAwsManagedPolicyName('AWSThinkboxDeadlineResourceTrackerAdminPolicy').managedPolicyArn),
]),
}));
assertions_1.Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', assertions_1.Match.not({
PolicyDocument: {
Statement: [
{
Action: 'iam:PassRole',
Condition: {
StringLike: {
'iam:PassedToService': 'ec2.amazonaws.com',
},
},
Effect: 'Allow',
Resource: [
stack.resolve(fleet.fleetRole.roleArn),
stack.resolve(fleet.fleetInstanceRole.roleArn),
],
},
{
Action: 'ec2:CreateTags',
Effect: 'Allow',
Resource: 'arn:aws:ec2:*:*:spot-fleet-request/*',
},
],
},
Roles: [{
Ref: 'RQRCSTaskTaskRole00DC9B43',
}],
}));
});
test('fleet with validUntil', () => {
// GIVEN
const validUntil = aws_cdk_lib_1.Expiration.atDate(new Date(2022, 11, 17));
const fleetWithCustomProps = new spot_event_plugin_fleet_1.SpotEventPluginFleet(stack, 'SpotEventPluginFleet', {
vpc,
renderQueue,
deadlineGroups: [
groupName,
],
instanceTypes: [
aws_ec2_1.InstanceType.of(aws_ec2_1.InstanceClass.T3, aws_ec2_1.InstanceSize.LARGE),
],
workerMachineImage,
maxCapacity: 1,
validUntil,
});
// WHEN
new lib_1.ConfigureSpotEventPlugin(stack, 'ConfigureSpotEventPlugin', {
vpc,
renderQueue: renderQueue,
spotFleets: [
fleetWithCustomProps,
],
});
// THEN
assertions_1.Template.fromStack(stack).hasResourceProperties('Custom::RFDK_ConfigureSpotEventPlugin', assertions_1.Match.objectLike({
spotFleetRequestConfigurations: assertions_1.Match.objectLike({
[groupName]: assertions_1.Match.objectLike({
ValidUntil: validUntil.date.toISOString(),
}),
}),
}));
});
test('fleet with context', () => {
// GIVEN
const context = 'context-abcdef';
const fleetWithCustomProps = new spot_event_plugin_fleet_1.SpotEventPluginFleet(stack, 'SpotEventPluginFleet', {
vpc,
renderQueue,
deadlineGroups: [
groupName,
],
instanceTypes: [
aws_ec2_1.InstanceType.of(aws_ec2_1.InstanceClass.T3, aws_ec2_1.InstanceSize.LARGE),
],
workerMachineImage,
maxCapacity: 1,
context,
});
// WHEN
new lib_1.ConfigureSpotEventPlugin(stack, 'ConfigureSpotEventPlugin', {
vpc,
renderQueue: renderQueue,
spotFleets: [
fleetWithCustomProps,
],
});
// THEN
assertions_1.Template.fromStack(stack).hasResourceProperties('Custom::RFDK_ConfigureSpotEventPlugin', assertions_1.Match.objectLike({
spotFleetRequestConfigurations: assertions_1.Match.objectLike({
[groupName]: assertions_1.Match.objectLike({
Context: context,
}),
}),
}));
});
});
test('only one object allowed per render queue', () => {
// GIVEN
new lib_1.ConfigureSpotEventPlugin(stack, 'ConfigureSpotEventPlugin', {
vpc,
renderQueue: renderQueue,
spotFleets: [
fleet,
],
});
// WHEN
function createConfigureSpotEventPlugin() {
new lib_1.ConfigureSpotEventPlugin(stack, 'ConfigureSpotEventPlugin2', {
vpc,
renderQueue: renderQueue,
spotFleets: [
fleet,
],
});
}
// THEN
expect(createConfigureSpotEventPlugin).toThrow(/Only one ConfigureSpotEventPlugin construct is allowed per render queue./);
});
test('can create multiple objects with different render queues', () => {
// GIVEN
const renderQueue2 = new lib_1.RenderQueue(stack, 'RQ2', {
vpc,
images: { remoteConnectionServer: aws_ecs_1.ContainerImage.fromAsset(__dirname) },
repository: new lib_1.Repository(stack, 'Repository2', {
vpc,
version,
secretsManagementSettings: { enabled: false },
}),
trafficEncryption: { externalTLS: { enabled: false } },
version,
});
// WHEN
new lib_1.ConfigureSpotEventPlugin(stack, 'ConfigureSpotEventPlugin', {
vpc,
renderQueue: renderQueue,
spotFleets: [
fleet,
],
});
new lib_1.ConfigureSpotEventPlugin(stack, 'ConfigureSpotEventPlugin2', {
vpc,
renderQueue: renderQueue2,
spotFleets: [
fleet,
],
});
// THEN
assertions_1.Template.fromStack(stack).resourceCountIs('Custom::RFDK_ConfigureSpotEventPlugin', 2);
});
test('throws with not supported render queue', () => {
// GIVEN
const invalidRenderQueue = {};
// WHEN
function createConfigureSpotEventPlugin() {
new lib_1.ConfigureSpotEventPlugin(stack, 'ConfigureSpotEventPlugin2', {
vpc,
renderQueue: invalidRenderQueue,
spotFleets: [
fleet,
],
});
}
// THEN
expect(createConfigureSpotEventPlugin).toThrow(/The provided render queue is not an instance of RenderQueue class. Some functionality is not supported./);
});
test('tagSpecifications returns undefined if fleet does not have tags', () => {
// GIVEN
const mockFleet = {
tags: {
hasTags: jest.fn().mockReturnValue(false),
},
};
const mockedFleet = mockFleet;
const config = new lib_1.ConfigureSpotEventPlugin(stack, 'ConfigureSpotEventPlugin', {
vpc,
renderQueue: renderQueue,
spotFleets: [
fleet,
],
});
// WHEN
// eslint-disable-next-line dot-notation
const result = stack.resolve(config['tagSpecifications'](mockedFleet, lib_1.SpotFleetResourceType.INSTANCE));
// THEN
expect(result).toBeUndefined();
});
describe('with TLS', () => {
let renderQueueWithTls;
let caCert;
beforeEach(() => {
const host = 'renderqueue';
const zoneName = 'deadline-test.internal';
caCert = new core_1.X509CertificatePem(stack, 'RootCA', {
subject: {
cn: 'SampleRootCA',
},
});
renderQueueWithTls = new lib_1.RenderQueue(stack, 'RQ with TLS', {
vpc,
images: { remoteConnectionServer: aws_ecs_1.ContainerImage.fromAsset(__dirname) },
repository: new lib_1.Repository(stack, 'Repository2', {
vpc,
version,
}),
version,
hostname: {
zone: new aws_route53_1.PrivateHostedZone(stack, 'DnsZone', {
vpc,
zoneName: zoneName,
}),
hostname: host,
},
trafficEncryption: {
externalTLS: {
rfdkCertificate: new core_1.X509CertificatePem(stack, 'RQCert', {
subject: {
cn: `${host}.${zoneName}`,
},
signingCertificate: caCert,
}),
},
},
});
});
test('Lambda role can get the ca secret', () => {
// WHEN
new lib_1.ConfigureSpotEventPlugin(stack, 'ConfigureSpotEventPlugin', {
vpc,
renderQueue: renderQueueWithTls,
spotFleets: [
fleet,
],
});
// THEN
assertions_1.Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', {
PolicyDocument: {
Statement: [
{
Action: [
'secretsmanager:GetSecretValue',
'secretsmanager:DescribeSecret',
],
Effect: 'Allow',
Resource: stack.resolve(renderQueueWithTls.certChain.secretArn),
},
],
},
Roles: [
{
Ref: 'ConfigureSpotEventPluginConfiguratorServiceRole341B4735',
},
],
});
});
test('creates a custom resource with connection', () => {
// WHEN
new lib_1.ConfigureSpotEventPlugin(stack, 'ConfigureSpotEventPlugin', {
vpc,
renderQueue: renderQueueWithTls,
spotFleets: [
fleet,
],
});
// THEN
assertions_1.Template.fromStack(stack).hasResourceProperties('Custom::RFDK_ConfigureSpotEventPlugin', assertions_1.Match.objectLike({
connection: assertions_1.Match.objectLike({
hostname: stack.resolve(renderQueueWithTls.endpoint.hostname),
port: stack.resolve(renderQueueWithTls.endpoint.portAsString()),
protocol: stack.resolve(renderQueueWithTls.endpoint.applicationProtocol.toString()),
caCertificateArn: stack.resolve(renderQueueWithTls.certChain.secretArn),
}),
}));
});
});
test('throws with the same group name', () => {
// WHEN
function createConfigureSpotEventPlugin() {
const duplicateFleet = new spot_event_plugin_fleet_1.SpotEventPluginFleet(stack, 'DuplicateSpotFleet', {
vpc,
renderQueue,
workerMachineImage: fleet.machineImage,
instanceTypes: fleet.instanceTypes,
maxCapacity: fleet.maxCapacity,
deadlineGroups: fleet.deadlineGroups,
});
new lib_1.ConfigureSpotEventPlugin(stack, 'ConfigureSpotEventPlugin', {
vpc,
renderQueue: renderQueue,
spotFleets: [
fleet,
duplicateFleet,
],
});
}
// THEN
expect(createConfigureSpotEventPlugin).toThrow(`Bad Group Name: ${groupName}. Group names in Spot Fleet Request Configurations should be unique.`);
});
test('uses selected subnets', () => {
// WHEN
new lib_1.ConfigureSpotEventPlugin(stack, 'ConfigureSpotEventPlugin', {
vpc,
vpcSubnets: { subnets: [vpc.privateSubnets[0]] },
renderQueue: renderQueue,
spotFleets: [
fleet,
],
});
// THEN
assertions_1.Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', {
Handler: 'configure-spot-event-plugin.configureSEP',
VpcConfig: {
SubnetIds: [
stack.resolve(vpc.privateSubnets[0].subnetId),
],
},
});
});
describe('throws with wrong deadline version', () => {
test.each([
['10.1.9'],
['10.1.10'],
])('%s', (versionString) => {
// GIVEN
const newStack = new aws_cdk_lib_1.Stack(app, 'NewStack');
version = new lib_1.VersionQuery(newStack, 'OldVersion', {
version: versionString,
});
renderQueue = new lib_1.RenderQueue(newStack, 'OldRenderQueue', {
vpc,
images: { remoteConnectionServer: aws_ecs_1.ContainerImage.fromAsset(__dirname) },
repository: new lib_1.Repository(newStack, 'Repository', {
vpc,
version,
secretsManagementSettings: { enabled: false },
}),
trafficEncryption: { externalTLS: { enabled: false } },
version,
});
// WHEN
function createConfigureSpotEventPlugin() {
new lib_1.ConfigureSpotEventPlugin(stack, 'ConfigureSpotEventPlugin', {
vpc,
renderQueue: renderQueue,
spotFleets: [
fleet,
],
});
}
// THEN
expect(createConfigureSpotEventPlugin).toThrow(`Minimum supported Deadline version for ConfigureSpotEventPlugin is 10.1.12.0. Received: ${versionString}.`);
});
});
test('does not throw with min deadline version', () => {
// GIVEN
const versionString = '10.1.12';
const newStack = new aws_cdk_lib_1.Stack(app, 'NewStack');
version = new lib_1.VersionQuery(newStack, 'OldVersion', {
version: versionString,
});
renderQueue = new lib_1.RenderQueue(newStack, 'OldRenderQueue', {
vpc,
images: { remoteConnectionServer: aws_ecs_1.ContainerImage.fromAsset(__dirname) },
repository: new lib_1.Repository(newStack, 'Repository', {
vpc,
version,
secretsManagementSettings: { enabled: false },
}),
trafficEncryption: { externalTLS: { enabled: false } },
version,
});
// WHEN
function createConfigureSpotEventPlugin() {
new lib_1.ConfigureSpotEventPlugin(newStack, 'ConfigureSpotEventPlugin', {
vpc,
renderQueue: renderQueue,
spotFleets: [
fleet,
],
});
}
// THEN
expect(createConfigureSpotEventPlugin).not.toThrow();
});
describe('secrets management enabled', () => {
beforeEach(() => {
region = 'us-east-1';
app = new aws_cdk_lib_1.App();
stack = new aws_cdk_lib_1.Stack(app, 'stack', {
env: {
region,
},
});
vpc = new aws_ec2_1.Vpc(stack, 'Vpc');
version = new lib_1.VersionQuery(stack, 'Version');
renderQueue = new lib_1.RenderQueue(stack, 'RQ', {
vpc,
images: { remoteConnectionServer: aws_ecs_1.ContainerImage.fromAsset(__dirname) },
repository: new lib_1.Repository(stack, 'Repository', {
vpc,
version,
}),
version,
});
groupName = 'group_name1';
});
test('a fleet without vpcSubnets specified => warns about dedicated subnets', () => {
// GIVEN
fleet = new spot_event_plugin_fleet_1.SpotEventPluginFleet(stack, 'SpotFleet', {
vpc,
renderQueue: renderQueue,
deadlineGroups: [
groupName,
],
instanceTypes: [
aws_ec2_1.InstanceType.of(aws_ec2_1.InstanceClass.T2, aws_ec2_1.InstanceSize.SMALL),
],
workerMachineImage,
maxCapacity: 1,
});
// WHEN
new lib_1.ConfigureSpotEventPlugin(stack, 'ConfigureSpotEventPlugin', {
renderQueue,
vpc,
spotFleets: [fleet],
});
// THEN
assertions_1.Annotations.fromStack(stack).hasWarning(`/${fleet.node.path}`, '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');
});
test('a fleet with vpcSubnets specified => does not warn about dedicated subnets', () => {
// GIVEN
fleet = new spot_event_plugin_fleet_1.SpotEventPluginFleet(stack, 'SpotFleetWithSubnets', {
vpc,
vpcSubnets: {
subnetType: aws_ec2_1.SubnetType.PRIVATE_WITH_EGRESS,
},
renderQueue: renderQueue,
deadlineGroups: [
groupName,
],
instanceTypes: [
aws_ec2_1.InstanceType.of(aws_ec2_1.InstanceClass.T2, aws_ec2_1.InstanceSize.SMALL),
],
workerMachineImage,
maxCapacity: 1,
});
// WHEN
new lib_1.ConfigureSpotEventPlugin(stack, 'ConfigureSpotEventPlugin', {
renderQueue,
vpc,
spotFleets: [fleet],
});
// THEN
assertions_1.Annotations.fromStack(stack).hasNoWarning(`/${fleet.node.path}`, assertions_1.Match.stringLikeRegexp('.*dedicated subnet.*'));
});
});
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlndXJlLXNwb3QtZXZlbnQtcGx1Z2luLnRlc3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjb25maWd1cmUtc3BvdC1ldmVudC1wbHVnaW4udGVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztHQUdHOztBQUVILDZDQUtxQjtBQUNyQix1REFJZ0M7QUFDaEMsaURBTzZCO0FBQzdCLGlEQUU2QjtBQUM3QixpREFBb0Q7QUFDcEQseURBQTREO0FBQzVELHFDQUFnRDtBQUNoRCw4REFBd0Q7QUFDeEQsZ0NBYWdCO0FBQ2hCLDRFQUV3QztBQUN4QywrQ0FBMEQ7QUFHMUQsUUFBUSxDQUFDLDBCQUEwQixFQUFFLEdBQUcsRUFBRTtJQUN4QyxJQUFJLEtBQVksQ0FBQztJQUNqQixJQUFJLEdBQVEsQ0FBQztJQUNiLElBQUksTUFBYyxDQUFDO0lBQ25CLElBQUksV0FBeUIsQ0FBQztJQUM5QixJQUFJLE9BQWlCLENBQUM7SUFDdEIsSUFBSSxHQUFRLENBQUM7SUFDYixJQUFJLEtBQTJCLENBQUM7SUFDaEMsSUFBSSxTQUFpQixDQUFDO0lBQ3RCLE1BQU0sa0JBQWtCLEdBQUcsSUFBSSw2QkFBbUIsQ0FBQztRQUNqRCxXQUFXLEVBQUUsU0FBUztLQUN2QixDQUFDLENBQUM7SUFFSCxVQUFVLENBQUMsR0FBRyxFQUFFO1FBQ2QsTUFBTSxHQUFHLFdBQVcsQ0FBQztRQUNyQixHQUFHLEdBQUcsSUFBSSxpQkFBRyxFQUFFLENBQUM7UUFDaEIsS0FBSyxHQUFHLElBQUksbUJBQUssQ0FBQyxHQUFHLEVBQUUsT0FBTyxFQUFFO1lBQzlCLEdBQUcsRUFBRTtnQkFDSCxNQUFNO2FBQ1A7U0FDRixDQUFDLENBQUM7UUFDSCxHQUFHLEdBQUcsSUFBSSxhQUFHLENBQUMsS0FBSyxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBRTVCLE9BQU8sR0FBRyxJQUFJLGtCQUFZLENBQUMsS0FBSyxFQUFFLFNBQVMsQ0FBQyxDQUFDO1FBRTdDLFdBQVcsR0FB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