Skip to content

Commit

Permalink
feat(codepipeline): add support for a StepFunctions invoke action (#8931
Browse files Browse the repository at this point in the history
)

this feature enables developers using the cdk
to create stepfunction invoke action in their stacks.

verified the changes using unit tests.

Reviewed by: sainsbm


----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
madhumitranrl authored Jul 18, 2020
1 parent e8d0776 commit 499776d
Show file tree
Hide file tree
Showing 7 changed files with 961 additions and 0 deletions.
49 changes: 49 additions & 0 deletions packages/@aws-cdk/aws-codepipeline-actions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -801,3 +801,52 @@ new codepipeline_actions.CodeBuildAction({

See [the AWS documentation](https://docs.aws.amazon.com/codepipeline/latest/userguide/actions-invoke-lambda-function.html)
on how to write a Lambda function invoked from CodePipeline.

### AWS Step Functions

This module contains an Action that allows you to invoke a Step Function in a Pipeline:

```ts
import * as stepfunction from '@aws-cdk/aws-stepfunctions';

const pipeline = new codepipeline.Pipeline(this, 'MyPipeline');
const startState = new stepfunction.Pass(stack, 'StartState');
const simpleStateMachine = new stepfunction.StateMachine(stack, 'SimpleStateMachine', {
definition: startState,
});
const stepFunctionAction = new codepipeline_actions.StepFunctionsInvokeAction({
actionName: 'Invoke',
stateMachine: simpleStateMachine,
stateMachineInput: codepipeline_actions.StateMachineInput.literal({ IsHelloWorldExample: true }),
});
pipeline.addStage({
stageName: 'StepFunctions',
actions: [stepFunctionAction],
});
```

The `StateMachineInput` can be created with one of 2 static factory methods:
`literal`, which takes an arbitrary map as its only argument, or `filePath`:

```ts
import * as stepfunction from '@aws-cdk/aws-stepfunctions';

const pipeline = new codepipeline.Pipeline(this, 'MyPipeline');
const inputArtifact = new codepipeline.Artifact();
const startState = new stepfunction.Pass(stack, 'StartState');
const simpleStateMachine = new stepfunction.StateMachine(stack, 'SimpleStateMachine', {
definition: startState,
});
const stepFunctionAction = new codepipeline_actions.StepFunctionsInvokeAction({
actionName: 'Invoke',
stateMachine: simpleStateMachine,
stateMachineInput: codepipeline_actions.StateMachineInput.filePath(inputArtifact.atPath('assets/input.json')),
});
pipeline.addStage({
stageName: 'StepFunctions',
actions: [stepFunctionAction],
});
```

See [the AWS documentation](https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-StepFunctions.html)
for information on Action structure reference.
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-codepipeline-actions/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ export * from './lambda/invoke-action';
export * from './manual-approval-action';
export * from './s3/deploy-action';
export * from './s3/source-action';
export * from './stepfunctions/invoke-action';
export * from './action'; // for some reason, JSII fails building the module without exporting this class
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import * as codepipeline from '@aws-cdk/aws-codepipeline';
import * as iam from '@aws-cdk/aws-iam';
import * as stepfunction from '@aws-cdk/aws-stepfunctions';
import * as cdk from '@aws-cdk/core';
import { Action } from '../action';

/**
* Represents the input for the StateMachine.
*/
export class StateMachineInput {
/**
* When the input type is FilePath, input artifact and
* filepath must be specified.
*/
public static filePath(inputFile: codepipeline.ArtifactPath): StateMachineInput {
return new StateMachineInput(inputFile.location, inputFile.artifact, 'FilePath');
}

/**
* When the input type is Literal, input value is passed
* directly to the state machine input.
*/
public static literal(object: object): StateMachineInput {
return new StateMachineInput(JSON.stringify(object), undefined, 'Literal');
}

/**
* The optional input Artifact of the Action.
* If InputType is set to FilePath, this artifact is required
* and is used to source the input for the state machine execution.
*
* @default - the Action will not have any inputs
* @see https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-StepFunctions.html#action-reference-StepFunctions-example
*/
public readonly inputArtifact?: codepipeline.Artifact;

/**
* Optional StateMachine InputType
* InputType can be Literal or FilePath
*
* @default - Literal
*/
public readonly inputType?: string;

/**
* When InputType is set to Literal (default), the Input field is used
* directly as the input for the state machine execution.
* Otherwise, the state machine is invoked with an empty JSON object {}.
*
* When InputType is set to FilePath, this field is required.
* An input artifact is also required when InputType is set to FilePath.
*
* @default - none
*/
public readonly input: any;

private constructor(input: any, inputArtifact: codepipeline.Artifact | undefined, inputType: string) {
this.input = input;
this.inputArtifact = inputArtifact;
this.inputType = inputType;
}
}

/**
* Construction properties of the {@link StepFunctionsInvokeAction StepFunction Invoke Action}.
*/
export interface StepFunctionsInvokeActionProps extends codepipeline.CommonAwsActionProps {
/**
* The optional output Artifact of the Action.
*
* @default the Action will not have any outputs
*/
readonly output?: codepipeline.Artifact;

/**
* The state machine to invoke.
*/
readonly stateMachine: stepfunction.IStateMachine;

/**
* Represents the input to the StateMachine.
* This includes input artifact, input type and the statemachine input.
*
* @default - none
*/
readonly stateMachineInput?: StateMachineInput;

/**
* Prefix (optional)
*
* By default, the action execution ID is used as the state machine execution name.
* If a prefix is provided, it is prepended to the action execution ID with a hyphen and
* together used as the state machine execution name.
*
* @default - action execution ID
*/
readonly executionNamePrefix?: string;
}

/**
* StepFunctionInvokeAction that is provided by an AWS CodePipeline.
*/
export class StepFunctionInvokeAction extends Action {
private readonly props: StepFunctionsInvokeActionProps;

constructor(props: StepFunctionsInvokeActionProps) {
super({
...props,
resource: props.stateMachine,
category: codepipeline.ActionCategory.INVOKE,
provider: 'StepFunctions',
artifactBounds: {
minInputs: 0,
maxInputs: 1,
minOutputs: 0,
maxOutputs: 1,
},
inputs: (props.stateMachineInput && props.stateMachineInput.inputArtifact) ? [props.stateMachineInput.inputArtifact] : [],
outputs: (props.output) ? [props.output] : [],
});
this.props = props;
}

protected bound(_scope: cdk.Construct, _stage: codepipeline.IStage, options: codepipeline.ActionBindOptions):
codepipeline.ActionConfig {
// allow pipeline to invoke this step function
options.role.addToPolicy(new iam.PolicyStatement({
actions: ['states:StartExecution', 'states:DescribeStateMachine'],
resources: [this.props.stateMachine.stateMachineArn],
}));

// allow state machine executions to be inspected
options.role.addToPolicy(new iam.PolicyStatement({
actions: ['states:DescribeExecution'],
resources: [cdk.Stack.of(this.props.stateMachine).formatArn({
service: 'states',
resource: 'execution',
resourceName: `${this.props.stateMachine.stateMachineArn}:${this.props.executionNamePrefix ?? ''}*`,
sep: ':',
})],
}));

// allow the Role access to the Bucket, if there are any inputs/outputs
if ((this.actionProperties.inputs ?? []).length > 0) {
options.bucket.grantRead(options.role);
}
if ((this.actionProperties.outputs ?? []).length > 0) {
options.bucket.grantWrite(options.role);
}

return {
configuration: {
StateMachineArn: this.props.stateMachine.stateMachineArn,
Input: this.props.stateMachineInput?.input,
InputType: this.props.stateMachineInput?.inputType,
ExecutionNamePrefix: this.props.executionNamePrefix,
},
};
}
}
2 changes: 2 additions & 0 deletions packages/@aws-cdk/aws-codepipeline-actions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
"@aws-cdk/aws-s3": "0.0.0",
"@aws-cdk/aws-sns": "0.0.0",
"@aws-cdk/aws-sns-subscriptions": "0.0.0",
"@aws-cdk/aws-stepfunctions": "0.0.0",
"@aws-cdk/core": "0.0.0",
"case": "1.6.3",
"constructs": "^3.0.2"
Expand All @@ -109,6 +110,7 @@
"@aws-cdk/aws-s3": "0.0.0",
"@aws-cdk/aws-sns": "0.0.0",
"@aws-cdk/aws-sns-subscriptions": "0.0.0",
"@aws-cdk/aws-stepfunctions": "0.0.0",
"@aws-cdk/core": "0.0.0",
"constructs": "^3.0.2"
},
Expand Down
Loading

0 comments on commit 499776d

Please sign in to comment.