Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(stepfunctions-tasks): add timeout parameter for EmrCreateCluster #28532

Merged
merged 10 commits into from
Jan 2, 2024
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as sfn from 'aws-cdk-lib/aws-stepfunctions';
import { App, Stack } from 'aws-cdk-lib';
import { App, Duration, Stack } from 'aws-cdk-lib';
import { IntegTest } from '@aws-cdk/integ-tests-alpha';
import { EmrCreateCluster } from 'aws-cdk-lib/aws-stepfunctions-tasks';

Expand All @@ -20,7 +20,7 @@ const step = new EmrCreateCluster(stack, 'EmrCreateCluster', {
spotSpecification: {
allocationStrategy: EmrCreateCluster.SpotAllocationStrategy.CAPACITY_OPTIMIZED,
timeoutAction: EmrCreateCluster.SpotTimeoutAction.TERMINATE_CLUSTER,
timeoutDurationMinutes: 60,
timeout: Duration.minutes(60),
},
},
name: 'Master',
Expand Down
2 changes: 1 addition & 1 deletion packages/aws-cdk-lib/aws-stepfunctions-tasks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -634,7 +634,7 @@ new tasks.EmrCreateCluster(this, 'SpotSpecification', {
spotSpecification: {
allocationStrategy: tasks.EmrCreateCluster.SpotAllocationStrategy.CAPACITY_OPTIMIZED,
timeoutAction: tasks.EmrCreateCluster.SpotTimeoutAction.TERMINATE_CLUSTER,
timeoutDurationMinutes: 60,
timeout: Duration.minutes(5),
},
},
}],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -732,9 +732,26 @@ export namespace EmrCreateCluster {
/**
* The spot provisioning timeout period in minutes.
*
* The value must be between 5 and 1440.
* The value must be between 5 and 1440 minutes.
*
* You must specify one of `timeout` and `timeoutDurationMinutes`.
*
* @default - The value in `timeout` is used
*
* @deprecated - Use `timeout`.
*/
readonly timeoutDurationMinutes?: number;

/**
* The spot provisioning timeout period in minutes.
*
* The value must be between 5 and 1440 minutes.
*
* You must specify one of `timeout` and `timeoutDurationMinutes`.
*
* @default - The value in `timeoutDurationMinutes` is used
*/
readonly timeoutDurationMinutes: number;
readonly timeout?: cdk.Duration;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,14 +150,20 @@ function SpotProvisioningSpecificationPropertyToJson(property?: EmrCreateCluster
if (!property) {
return undefined;
}
if (!cdk.Token.isUnresolved(property.timeoutDurationMinutes) && (property.timeoutDurationMinutes < 5 || property.timeoutDurationMinutes > 1440)) {
throw new Error(`timeoutDurationMinutes must be between 5 and 1440, got ${property.timeoutDurationMinutes}`);

if ((property.timeout && property.timeoutDurationMinutes) || (!property.timeout && !property.timeoutDurationMinutes)) {
throw new Error('one of timeout and timeoutDurationMinutes must be specified');
}
const timeout = property.timeout?.toMinutes() ?? property.timeoutDurationMinutes;
if (timeout !== undefined && !cdk.Token.isUnresolved(timeout) && (timeout < 5 || timeout > 1440)) {
throw new Error(`timeout must be between 5 and 1440 minutes, got ${timeout}`);
kaizencc marked this conversation as resolved.
Show resolved Hide resolved
}

return {
AllocationStrategy: cdk.stringToCloudFormation(property.allocationStrategy),
BlockDurationMinutes: cdk.numberToCloudFormation(property.blockDurationMinutes),
TimeoutAction: cdk.stringToCloudFormation(property.timeoutAction?.valueOf()),
TimeoutDurationMinutes: cdk.numberToCloudFormation(property.timeoutDurationMinutes),
TimeoutDurationMinutes: cdk.numberToCloudFormation(property.timeout?.toMinutes() || property.timeoutDurationMinutes),
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -921,7 +921,7 @@ test.each([
allocationStrategy: strategy,
blockDurationMinutes: 1,
timeoutAction: EmrCreateCluster.SpotTimeoutAction.TERMINATE_CLUSTER,
timeoutDurationMinutes: 5,
timeout: cdk.Duration.minutes(5),
},
},
name: 'Main',
Expand Down Expand Up @@ -1033,7 +1033,7 @@ test('Create Cluster with InstanceFleet for Spot instances', () => {
spotSpecification: {
blockDurationMinutes: 1,
timeoutAction: EmrCreateCluster.SpotTimeoutAction.TERMINATE_CLUSTER,
timeoutDurationMinutes: 5,
timeout: cdk.Duration.minutes(5),
},
},
name: 'Main',
Expand Down Expand Up @@ -1219,7 +1219,63 @@ test('Create Cluster with InstanceFleet for On-Demand instances', () => {
});
});

test('Throws if timeoutDurationMinutes for Spot instances is less than 5', () => {
test('Throws if timeout for Spot instances is less than 5 minutes', () => {
// GIVEN
const task = new EmrCreateCluster(stack, 'Task', {
instances: {
instanceFleets: [{
instanceFleetType: EmrCreateCluster.InstanceRoleType.MASTER,
launchSpecifications: {
spotSpecification: {
timeoutAction: EmrCreateCluster.SpotTimeoutAction.TERMINATE_CLUSTER,
timeout: cdk.Duration.minutes(4),
},
},
name: 'Main',
targetSpotCapacity: 1,
}],
},
clusterRole,
name: 'Cluster',
serviceRole,
integrationPattern: sfn.IntegrationPattern.REQUEST_RESPONSE,
});

// THEN
expect(() => {
task.toStateJson();
}).toThrow(/timeout must be between 5 and 1440 minutes, got 4/);
});

test('Throws if timeout for Spot instances is greater than 1440 minutes', () => {
// GIVEN
const task = new EmrCreateCluster(stack, 'Task', {
instances: {
instanceFleets: [{
instanceFleetType: EmrCreateCluster.InstanceRoleType.MASTER,
launchSpecifications: {
spotSpecification: {
timeoutAction: EmrCreateCluster.SpotTimeoutAction.TERMINATE_CLUSTER,
timeout: cdk.Duration.minutes(1441),
},
},
name: 'Main',
targetSpotCapacity: 1,
}],
},
clusterRole,
name: 'Cluster',
serviceRole,
integrationPattern: sfn.IntegrationPattern.REQUEST_RESPONSE,
});

// THEN
expect(() => {
task.toStateJson();
}).toThrow(/timeout must be between 5 and 1440 minutes, got 1441/);
kaizencc marked this conversation as resolved.
Show resolved Hide resolved
});

test('Throws if timeoutDurationMinutes for Spot instances is less than 5 minutes', () => {
// GIVEN
const task = new EmrCreateCluster(stack, 'Task', {
instances: {
Expand All @@ -1244,10 +1300,10 @@ test('Throws if timeoutDurationMinutes for Spot instances is less than 5', () =>
// THEN
expect(() => {
task.toStateJson();
}).toThrow(/timeoutDurationMinutes must be between 5 and 1440, got 4/);
}).toThrow(/timeout must be between 5 and 1440 minutes, got 4/);
kaizencc marked this conversation as resolved.
Show resolved Hide resolved
});

test('Throws if timeoutDurationMinutes for Spot instances is greater than 1440', () => {
test('Throws if timeoutDurationMinutes for Spot instances is greater than 1440 minutes', () => {
// GIVEN
const task = new EmrCreateCluster(stack, 'Task', {
instances: {
Expand All @@ -1272,7 +1328,63 @@ test('Throws if timeoutDurationMinutes for Spot instances is greater than 1440',
// THEN
expect(() => {
task.toStateJson();
}).toThrow(/timeoutDurationMinutes must be between 5 and 1440, got 1441/);
}).toThrow(/timeout must be between 5 and 1440 minutes, got 1441/);
kaizencc marked this conversation as resolved.
Show resolved Hide resolved
});

test('Throws if neither timeout nor timeoutDurationMinutes is specified', () => {
// GIVEN
const task = new EmrCreateCluster(stack, 'Task', {
instances: {
instanceFleets: [{
instanceFleetType: EmrCreateCluster.InstanceRoleType.MASTER,
launchSpecifications: {
spotSpecification: {
timeoutAction: EmrCreateCluster.SpotTimeoutAction.TERMINATE_CLUSTER,
},
},
name: 'Main',
targetSpotCapacity: 1,
}],
},
clusterRole,
name: 'Cluster',
serviceRole,
integrationPattern: sfn.IntegrationPattern.REQUEST_RESPONSE,
});

// THEN
expect(() => {
task.toStateJson();
}).toThrow(/one of timeout and timeoutDurationMinutes must be specified/);
});

test('Throws if both timeout and timeoutDurationMinutes are specified', () => {
// WHEN
const task = new EmrCreateCluster(stack, 'Task', {
instances: {
instanceFleets: [{
instanceFleetType: EmrCreateCluster.InstanceRoleType.MASTER,
launchSpecifications: {
spotSpecification: {
timeoutAction: EmrCreateCluster.SpotTimeoutAction.TERMINATE_CLUSTER,
timeout: cdk.Duration.minutes(5),
timeoutDurationMinutes: 10,
},
},
name: 'Main',
targetSpotCapacity: 1,
}],
},
clusterRole,
name: 'Cluster',
serviceRole,
integrationPattern: sfn.IntegrationPattern.REQUEST_RESPONSE,
});

// THEN
expect(() => {
task.toStateJson();
}).toThrow(/one of timeout and timeoutDurationMinutes must be specified/);
});

test('Throws if both bidPrice and bidPriceAsPercentageOfOnDemandPrice are specified', () => {
Expand Down