-
Notifications
You must be signed in to change notification settings - Fork 4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(stepfunctions-tasks): start a nested state machine execution as …
…a construct (#8178) This class is the replacement for the previous `StartExecution` class. There are a few differences: 1. the `stateMachine` parameter has been moved into props. Rationale: alignment with constructs. 2. the resource ARN that's generated in the Amazon States language uses `sync:2`. This returns an output of JSON instead of a string. Rationale: alignment with Step Functions team recommendation. 3. The `input` parameter has been changed to be of type `sfn.TaskInput` Rationale: previous type precluded the ability to assign state input to this parameter. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
- Loading branch information
Showing
7 changed files
with
586 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
129 changes: 129 additions & 0 deletions
129
packages/@aws-cdk/aws-stepfunctions-tasks/lib/stepfunctions/start-execution.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
import * as iam from '@aws-cdk/aws-iam'; | ||
import * as sfn from '@aws-cdk/aws-stepfunctions'; | ||
import { Construct, Stack } from '@aws-cdk/core'; | ||
import { integrationResourceArn, validatePatternSupported } from '../private/task-utils'; | ||
|
||
/** | ||
* Properties for StartExecution | ||
*/ | ||
export interface StepFunctionsStartExecutionProps extends sfn.TaskStateBaseProps { | ||
/** | ||
* The Step Functions state machine to start the execution on. | ||
*/ | ||
readonly stateMachine: sfn.IStateMachine; | ||
|
||
/** | ||
* The JSON input for the execution, same as that of StartExecution. | ||
* | ||
* @see https://docs.aws.amazon.com/step-functions/latest/apireference/API_StartExecution.html | ||
* | ||
* @default - The state input (JSON path '$') | ||
*/ | ||
readonly input?: sfn.TaskInput; | ||
|
||
/** | ||
* The name of the execution, same as that of StartExecution. | ||
* | ||
* @see https://docs.aws.amazon.com/step-functions/latest/apireference/API_StartExecution.html | ||
* | ||
* @default - None | ||
*/ | ||
readonly name?: string; | ||
} | ||
|
||
/** | ||
* A Step Functions Task to call StartExecution on another state machine. | ||
* | ||
* It supports three service integration patterns: FIRE_AND_FORGET, SYNC and WAIT_FOR_TASK_TOKEN. | ||
*/ | ||
export class StepFunctionsStartExecution extends sfn.TaskStateBase { | ||
private static readonly SUPPORTED_INTEGRATION_PATTERNS = [ | ||
sfn.IntegrationPattern.REQUEST_RESPONSE, | ||
sfn.IntegrationPattern.RUN_JOB, | ||
sfn.IntegrationPattern.WAIT_FOR_TASK_TOKEN, | ||
]; | ||
|
||
protected readonly taskMetrics?: sfn.TaskMetricsConfig; | ||
protected readonly taskPolicies?: iam.PolicyStatement[]; | ||
|
||
private readonly integrationPattern: sfn.IntegrationPattern; | ||
|
||
constructor(scope: Construct, id: string, private readonly props: StepFunctionsStartExecutionProps) { | ||
super(scope, id, props); | ||
|
||
this.integrationPattern = props.integrationPattern || sfn.IntegrationPattern.REQUEST_RESPONSE; | ||
validatePatternSupported(this.integrationPattern, StepFunctionsStartExecution.SUPPORTED_INTEGRATION_PATTERNS); | ||
|
||
if (this.integrationPattern === sfn.IntegrationPattern.WAIT_FOR_TASK_TOKEN && !sfn.FieldUtils.containsTaskToken(props.input)) { | ||
throw new Error('Task Token is required in `input` for callback. Use Context.taskToken to set the token.'); | ||
} | ||
|
||
this.taskPolicies = this.createScopedAccessPolicy(); | ||
} | ||
|
||
protected renderTask(): any { | ||
// suffix of ':2' indicates that the output of the nested state machine should be JSON | ||
// suffix is only applicable when waiting for a nested state machine to complete (RUN_JOB) | ||
// https://docs.aws.amazon.com/step-functions/latest/dg/connect-stepfunctions.html | ||
const suffix = this.integrationPattern === sfn.IntegrationPattern.RUN_JOB ? ':2' : ''; | ||
return { | ||
Resource: `${integrationResourceArn('states', 'startExecution', this.integrationPattern)}${suffix}`, | ||
Parameters: sfn.FieldUtils.renderObject({ | ||
Input: this.props.input ? this.props.input.value : sfn.TaskInput.fromDataAt('$').value, | ||
StateMachineArn: this.props.stateMachine.stateMachineArn, | ||
Name: this.props.name, | ||
}), | ||
}; | ||
} | ||
|
||
/** | ||
* As StateMachineArn is extracted automatically from the state machine object included in the constructor, | ||
* | ||
* the scoped access policy should be generated accordingly. | ||
* | ||
* This means the action of StartExecution should be restricted on the given state machine, instead of being granted to all the resources (*). | ||
*/ | ||
private createScopedAccessPolicy(): iam.PolicyStatement[] { | ||
const stack = Stack.of(this); | ||
|
||
const policyStatements = [ | ||
new iam.PolicyStatement({ | ||
actions: ['states:StartExecution'], | ||
resources: [this.props.stateMachine.stateMachineArn], | ||
}), | ||
]; | ||
|
||
// Step Functions use Cloud Watch managed rules to deal with synchronous tasks. | ||
if (this.integrationPattern === sfn.IntegrationPattern.RUN_JOB) { | ||
policyStatements.push( | ||
new iam.PolicyStatement({ | ||
actions: ['states:DescribeExecution', 'states:StopExecution'], | ||
// https://docs.aws.amazon.com/step-functions/latest/dg/concept-create-iam-advanced.html#concept-create-iam-advanced-execution | ||
resources: [ | ||
stack.formatArn({ | ||
service: 'states', | ||
resource: 'execution', | ||
sep: ':', | ||
resourceName: `${stack.parseArn(this.props.stateMachine.stateMachineArn, ':').resourceName}*`, | ||
}), | ||
], | ||
}), | ||
); | ||
|
||
policyStatements.push( | ||
new iam.PolicyStatement({ | ||
actions: ['events:PutTargets', 'events:PutRule', 'events:DescribeRule'], | ||
resources: [ | ||
stack.formatArn({ | ||
service: 'events', | ||
resource: 'rule', | ||
resourceName: 'StepFunctionsGetEventsForStepFunctionsExecutionRule', | ||
}), | ||
], | ||
}), | ||
); | ||
} | ||
|
||
return policyStatements; | ||
} | ||
} |
187 changes: 187 additions & 0 deletions
187
...s/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/integ.start-execution.expected.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
{ | ||
"Resources": { | ||
"ChildRole1E3E0EF5": { | ||
"Type": "AWS::IAM::Role", | ||
"Properties": { | ||
"AssumeRolePolicyDocument": { | ||
"Statement": [ | ||
{ | ||
"Action": "sts:AssumeRole", | ||
"Effect": "Allow", | ||
"Principal": { | ||
"Service": { | ||
"Fn::Join": [ | ||
"", | ||
[ | ||
"states.", | ||
{ | ||
"Ref": "AWS::Region" | ||
}, | ||
".amazonaws.com" | ||
] | ||
] | ||
} | ||
} | ||
} | ||
], | ||
"Version": "2012-10-17" | ||
} | ||
} | ||
}, | ||
"ChildDAB30558": { | ||
"Type": "AWS::StepFunctions::StateMachine", | ||
"Properties": { | ||
"DefinitionString": "{\"StartAt\":\"Pass\",\"States\":{\"Pass\":{\"Type\":\"Pass\",\"End\":true}}}", | ||
"RoleArn": { | ||
"Fn::GetAtt": ["ChildRole1E3E0EF5", "Arn"] | ||
} | ||
}, | ||
"DependsOn": ["ChildRole1E3E0EF5"] | ||
}, | ||
"ParentRole5F0C366C": { | ||
"Type": "AWS::IAM::Role", | ||
"Properties": { | ||
"AssumeRolePolicyDocument": { | ||
"Statement": [ | ||
{ | ||
"Action": "sts:AssumeRole", | ||
"Effect": "Allow", | ||
"Principal": { | ||
"Service": { | ||
"Fn::Join": [ | ||
"", | ||
[ | ||
"states.", | ||
{ | ||
"Ref": "AWS::Region" | ||
}, | ||
".amazonaws.com" | ||
] | ||
] | ||
} | ||
} | ||
} | ||
], | ||
"Version": "2012-10-17" | ||
} | ||
} | ||
}, | ||
"ParentRoleDefaultPolicy9BDC56DC": { | ||
"Type": "AWS::IAM::Policy", | ||
"Properties": { | ||
"PolicyDocument": { | ||
"Statement": [ | ||
{ | ||
"Action": "states:StartExecution", | ||
"Effect": "Allow", | ||
"Resource": { | ||
"Ref": "ChildDAB30558" | ||
} | ||
}, | ||
{ | ||
"Action": ["states:DescribeExecution", "states:StopExecution"], | ||
"Effect": "Allow", | ||
"Resource": { | ||
"Fn::Join": [ | ||
"", | ||
[ | ||
"arn:", | ||
{ | ||
"Ref": "AWS::Partition" | ||
}, | ||
":states:", | ||
{ | ||
"Ref": "AWS::Region" | ||
}, | ||
":", | ||
{ | ||
"Ref": "AWS::AccountId" | ||
}, | ||
":execution:", | ||
{ | ||
"Fn::Select": [ | ||
6, | ||
{ | ||
"Fn::Split": [ | ||
":", | ||
{ | ||
"Ref": "ChildDAB30558" | ||
} | ||
] | ||
} | ||
] | ||
}, | ||
"*" | ||
] | ||
] | ||
} | ||
}, | ||
{ | ||
"Action": ["events:PutTargets", "events:PutRule", "events:DescribeRule"], | ||
"Effect": "Allow", | ||
"Resource": { | ||
"Fn::Join": [ | ||
"", | ||
[ | ||
"arn:", | ||
{ | ||
"Ref": "AWS::Partition" | ||
}, | ||
":events:", | ||
{ | ||
"Ref": "AWS::Region" | ||
}, | ||
":", | ||
{ | ||
"Ref": "AWS::AccountId" | ||
}, | ||
":rule/StepFunctionsGetEventsForStepFunctionsExecutionRule" | ||
] | ||
] | ||
} | ||
} | ||
], | ||
"Version": "2012-10-17" | ||
}, | ||
"PolicyName": "ParentRoleDefaultPolicy9BDC56DC", | ||
"Roles": [ | ||
{ | ||
"Ref": "ParentRole5F0C366C" | ||
} | ||
] | ||
} | ||
}, | ||
"Parent8B210403": { | ||
"Type": "AWS::StepFunctions::StateMachine", | ||
"Properties": { | ||
"DefinitionString": { | ||
"Fn::Join": [ | ||
"", | ||
[ | ||
"{\"StartAt\":\"Task\",\"States\":{\"Task\":{\"End\":true,\"Type\":\"Task\",\"Resource\":\"arn:", | ||
{ | ||
"Ref": "AWS::Partition" | ||
}, | ||
":states:::states:startExecution.sync:2\",\"Parameters\":{\"Input\":{\"hello.$\":\"$.hello\"},\"StateMachineArn\":\"", | ||
{ | ||
"Ref": "ChildDAB30558" | ||
}, | ||
"\"}}}}" | ||
] | ||
] | ||
}, | ||
"RoleArn": { | ||
"Fn::GetAtt": ["ParentRole5F0C366C", "Arn"] | ||
} | ||
}, | ||
"DependsOn": ["ParentRoleDefaultPolicy9BDC56DC", "ParentRole5F0C366C"] | ||
} | ||
}, | ||
"Outputs": { | ||
"StateMachineARN": { | ||
"Value": { | ||
"Ref": "Parent8B210403" | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.