konokenj.cdk-api-mcp-server 0.32.0__py3-none-any.whl → 0.34.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of konokenj.cdk-api-mcp-server might be problematic. Click here for more details.

Files changed (37) hide show
  1. cdk_api_mcp_server/__about__.py +1 -1
  2. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigatewayv2/integ.api-dualstack.ts +3 -4
  3. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigatewayv2/integ.api.ts +2 -4
  4. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigatewayv2/integ.stage.ts +7 -20
  5. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigatewayv2-authorizers/integ.iam.ts +34 -38
  6. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigatewayv2-integrations/integ.sqs.ts +58 -71
  7. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-cloudfront/README.md +292 -1
  8. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-cloudwatch/README.md +13 -0
  9. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-cloudwatch/integ.alarm-and-dashboard.ts +12 -0
  10. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-codebuild/README.md +67 -3
  11. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-codebuild/integ.project-s3-cache.ts +71 -0
  12. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-ecs/integ.availability-zone-rebalancing.ts +14 -4
  13. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-ecs/integ.enable-execute-command.ts +35 -29
  14. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-ecs/integ.exec-command.ts +16 -22
  15. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-ecs/integ.lb-awsvpc-nw.ts +26 -16
  16. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-ecs/integ.pseudo-terminal.ts +18 -8
  17. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-events/integ.api-destination.ts +42 -0
  18. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-events-targets/README.md +7 -2
  19. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-kinesisfirehose/README.md +3 -0
  20. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-kinesisfirehose/integ.s3-bucket.lit.ts +1 -0
  21. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-lambda/integ.lambda-policy-with-token-resolution.ts +46 -0
  22. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-route53-targets/integ.elastic-beanstalk-hostedzoneid.ts +1 -1
  23. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-s3-deployment/integ.bucket-deployment-cross-nested-stack-source.ts +58 -0
  24. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-s3-deployment/integ.bucket-deployment-cross-stack-source.ts +48 -0
  25. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-stepfunctions-tasks/integ.invoke-jsonata.ts +87 -80
  26. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-stepfunctions-tasks/integ.invoke.ts +87 -69
  27. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-stepfunctions-tasks/integ.start-job-run.ts +96 -43
  28. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-synthetics/README.md +40 -0
  29. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-synthetics/integ.canary-resources-to-replicate-tags.ts +36 -0
  30. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-synthetics/integ.canary-retry.ts +32 -0
  31. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/cx-api/FEATURE_FLAGS.md +29 -0
  32. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/pipelines/integ.pipeline-with-customsynthesizer.ts +105 -0
  33. {konokenj_cdk_api_mcp_server-0.32.0.dist-info → konokenj_cdk_api_mcp_server-0.34.0.dist-info}/METADATA +2 -2
  34. {konokenj_cdk_api_mcp_server-0.32.0.dist-info → konokenj_cdk_api_mcp_server-0.34.0.dist-info}/RECORD +37 -29
  35. {konokenj_cdk_api_mcp_server-0.32.0.dist-info → konokenj_cdk_api_mcp_server-0.34.0.dist-info}/WHEEL +0 -0
  36. {konokenj_cdk_api_mcp_server-0.32.0.dist-info → konokenj_cdk_api_mcp_server-0.34.0.dist-info}/entry_points.txt +0 -0
  37. {konokenj_cdk_api_mcp_server-0.32.0.dist-info → konokenj_cdk_api_mcp_server-0.34.0.dist-info}/licenses/LICENSE.txt +0 -0
@@ -0,0 +1,48 @@
1
+ import { App, Stack, StackProps } from 'aws-cdk-lib';
2
+ import { UserPool } from 'aws-cdk-lib/aws-cognito';
3
+ import * as integ from '@aws-cdk/integ-tests-alpha';
4
+ import { ExpectedResult } from '@aws-cdk/integ-tests-alpha';
5
+ import { Construct } from 'constructs';
6
+ import { BucketDeployment, Source } from 'aws-cdk-lib/aws-s3-deployment';
7
+ import { Bucket } from 'aws-cdk-lib/aws-s3';
8
+
9
+ class Stack2 extends Stack {
10
+ userPool: UserPool;
11
+
12
+ constructor (scope: Construct, id: string, props: StackProps = {}) {
13
+ super(scope, id, props);
14
+ this.userPool = new UserPool(this, 'userpool');
15
+ }
16
+ }
17
+
18
+ class Stack1 extends Stack {
19
+ bucket: Bucket;
20
+
21
+ constructor (scope: Construct, id: string, props: { userPool: UserPool }) {
22
+ super(scope, id);
23
+ this.bucket = new Bucket(this, 'bucket');
24
+ new BucketDeployment(this, 'XXXXXXXXXX', {
25
+ destinationBucket: this.bucket,
26
+ sources: [
27
+ Source.data('test.txt', props.userPool.userPoolId),
28
+ ],
29
+ });
30
+ }
31
+ }
32
+
33
+ const app = new App();
34
+ const stack2 = new Stack2(app, 'stack2');
35
+ const stack1 = new Stack1(app, 'stack1', { userPool: stack2.userPool });
36
+
37
+ const integTest = new integ.IntegTest(app, 'integ-cross-stack-source', {
38
+ testCases: [
39
+ stack1,
40
+ ],
41
+ });
42
+
43
+ integTest.assertions.awsApiCall('S3', 'getObject', {
44
+ Bucket: stack1.bucket.bucketName,
45
+ Key: 'test.txt',
46
+ }).expect(ExpectedResult.objectLike({
47
+ Body: stack2.userPool.userPoolId,
48
+ }));
@@ -1,112 +1,119 @@
1
- import * as path from 'path';
2
- import { IntegTest, ExpectedResult } from '@aws-cdk/integ-tests-alpha';
3
- import * as cdk from 'aws-cdk-lib';
4
- import * as apigateway from 'aws-cdk-lib/aws-apigateway';
5
- import * as events from 'aws-cdk-lib/aws-events';
1
+ import { Code, Function } from 'aws-cdk-lib/aws-lambda';
6
2
  import * as sfn from 'aws-cdk-lib/aws-stepfunctions';
7
- import * as lambda from 'aws-cdk-lib/aws-lambda-nodejs';
8
- import * as tasks from 'aws-cdk-lib/aws-stepfunctions-tasks';
9
- import { password, username } from './my-lambda-handler';
3
+ import * as cdk from 'aws-cdk-lib';
4
+ import { LambdaInvoke } from 'aws-cdk-lib/aws-stepfunctions-tasks';
5
+ import { IntegTest, ExpectedResult } from '@aws-cdk/integ-tests-alpha';
6
+ import { STANDARD_NODEJS_RUNTIME } from '../../../config';
10
7
 
11
8
  /*
12
- * Creates an API Gateway instance with a GET method and mock integration,
13
- * secured with basic auth. It then creates a matching Connection and uses it
14
- * in a state machine with a task state to invoke the endpoint.
9
+ * Creates a state machine with a task state to invoke a Lambda function
10
+ * The state machine creates a couple of Lambdas that pass results forward
11
+ * and into a Choice state that validates the output.
12
+ *
13
+ * Stack verification steps:
14
+ * The generated State Machine can be executed from the CLI (or Step Functions console)
15
+ * and runs with an execution status of `Succeeded`.
15
16
  *
16
- * Stack verification steps :
17
- * * aws stepfunctions start-execution --state-machine-arn <deployed state machine arn> : should return execution arn
18
- * * aws stepfunctions describe-execution --execution-arn <execution-arn generated before> : should return status as SUCCEEDED
17
+ * -- aws stepfunctions start-execution --state-machine-arn <state-machine-arn-from-output> provides execution arn
18
+ * -- aws stepfunctions describe-execution --execution-arn <state-machine-arn-from-output> returns a status of `Succeeded`
19
19
  */
20
20
  const app = new cdk.App({
21
21
  postCliContext: {
22
22
  '@aws-cdk/aws-lambda:useCdkManagedLogGroup': false,
23
23
  },
24
24
  });
25
- const stack = new cdk.Stack(app, 'aws-stepfunctions-tasks-http-invoke-integ');
25
+ const stack = new cdk.Stack(app, 'aws-stepfunctions-tasks-lambda-invoke-jsonata-integ');
26
26
 
27
- const authorizerHandler = new lambda.NodejsFunction(stack, 'AuthorizerHandler', {
28
- entry: path.resolve(__dirname, 'my-lambda-handler', 'index.ts'),
29
- handler: 'handler',
27
+ const submitJobLambda = new Function(stack, 'submitJobLambda', {
28
+ code: Code.fromInline(`exports.handler = async (event, context) => {
29
+ return {
30
+ statusCode: '200',
31
+ body: 'hello, world!',
32
+ ...event,
33
+ };
34
+ };`),
35
+ runtime: STANDARD_NODEJS_RUNTIME,
36
+ handler: 'index.handler',
30
37
  });
31
38
 
32
- const authorizer = new apigateway.TokenAuthorizer(stack, 'Authorizer', {
33
- handler: authorizerHandler,
34
- identitySource: 'method.request.header.Authorization',
35
- resultsCacheTtl: cdk.Duration.seconds(0),
36
- });
37
-
38
- const api = new apigateway.RestApi(stack, 'IntegRestApi');
39
-
40
- api.root.addResource('test').addMethod(
41
- 'GET',
42
- new apigateway.MockIntegration({
43
- integrationResponses: [
44
- {
45
- statusCode: '200',
46
- responseTemplates: {
47
- 'application/json': JSON.stringify({ message: 'Hello, tester!' }),
48
- },
49
- },
50
- ],
51
- passthroughBehavior: apigateway.PassthroughBehavior.NEVER,
52
- requestTemplates: {
53
- 'application/json': '{ "statusCode": 200 }',
54
- },
39
+ const submitJob = LambdaInvoke.jsonata(stack, 'Invoke Handler', {
40
+ lambdaFunction: submitJobLambda,
41
+ payload: sfn.TaskInput.fromObject({
42
+ execId: '{% $states.context.Execution.Id %}',
43
+ execInput: '{% $states.context.Execution.Input %}',
44
+ execName: '{% $states.context.Execution.Name %}',
45
+ execRoleArn: '{% $states.context.Execution.RoleArn %}',
46
+ execStartTime: '{% $states.context.Execution.StartTime %}',
47
+ stateEnteredTime: '{% $states.context.State.EnteredTime %}',
48
+ stateName: '{% $states.context.State.Name %}',
49
+ stateRetryCount: '{% $states.context.State.RetryCount %}',
50
+ stateMachineId: '{% $states.context.StateMachine.Id %}',
51
+ stateMachineName: '{% $states.context.StateMachine.Name %}',
55
52
  }),
56
- {
57
- authorizer,
58
- methodResponses: [
59
- {
60
- statusCode: '200',
61
- },
62
- ],
63
- },
64
- );
53
+ outputs: '{% $states.result.Payload %}',
54
+ });
65
55
 
66
- const connection = new events.Connection(stack, 'Connection', {
67
- authorization: events.Authorization.basic(username, cdk.SecretValue.unsafePlainText(password)),
56
+ const checkJobStateLambda = new Function(stack, 'checkJobStateLambda', {
57
+ code: Code.fromInline(`exports.handler = async function(event, context) {
58
+ const expectedFields = [
59
+ 'execId', 'execInput', 'execName', 'execRoleArn',
60
+ 'execStartTime', 'stateEnteredTime', 'stateName',
61
+ 'stateRetryCount', 'stateMachineId', 'stateMachineName',
62
+ ];
63
+ const fieldsAreSet = expectedFields.every(field => event[field] !== undefined);
64
+ return {
65
+ status: event.statusCode === '200' && fieldsAreSet ? 'SUCCEEDED' : 'FAILED'
66
+ };
67
+ };`),
68
+ runtime: STANDARD_NODEJS_RUNTIME,
69
+ handler: 'index.handler',
68
70
  });
69
71
 
70
- const httpInvokeTask = tasks.HttpInvoke.jsonata(stack, 'Invoke HTTP Endpoint', {
71
- apiRoot: api.urlForPath('/'),
72
- apiEndpoint: sfn.TaskInput.fromText('{% $states.input.apiEndpoint %}'),
73
- connection,
74
- method: sfn.TaskInput.fromText('GET'),
72
+ const checkJobState = LambdaInvoke.jsonata(stack, 'Check the job state', {
73
+ lambdaFunction: checkJobStateLambda,
75
74
  outputs: {
76
- ResponseBody: '{% $states.result.ResponseBody %}',
75
+ status: '{% $states.result.Payload.status %}',
77
76
  },
78
77
  });
79
78
 
79
+ const isComplete = sfn.Choice.jsonata(stack, 'Job Complete?');
80
+ const jobFailed = sfn.Fail.jsonata(stack, 'Job Failed', {
81
+ cause: 'Job Failed',
82
+ error: 'Received a status that was not 200',
83
+ });
84
+ const finalStatus = sfn.Pass.jsonata(stack, 'Final step');
85
+
86
+ const chain = sfn.Chain.start(submitJob)
87
+ .next(checkJobState)
88
+ .next(
89
+ isComplete
90
+ .when(sfn.Condition.jsonata("{% $states.input.status = 'FAILED' %}"), jobFailed)
91
+ .when(sfn.Condition.jsonata("{% $states.input.status = 'SUCCEEDED' %}"), finalStatus),
92
+ );
93
+
80
94
  const sm = new sfn.StateMachine(stack, 'StateMachine', {
81
- definition: httpInvokeTask,
95
+ definition: chain,
82
96
  timeout: cdk.Duration.seconds(30),
83
97
  });
84
98
 
85
- const testCase = new IntegTest(app, 'HttpInvokeTest', {
86
- testCases: [stack],
99
+ new cdk.CfnOutput(stack, 'stateMachineArn', {
100
+ value: sm.stateMachineArn,
87
101
  });
88
102
 
89
- // Start an execution
90
- const start = testCase.assertions.awsApiCall('@aws-sdk/client-sfn', 'StartExecution', {
91
- stateMachineArn: sm.stateMachineArn,
92
- input: JSON.stringify({
93
- apiEndpoint: '/test',
94
- }),
103
+ const integ = new IntegTest(app, 'IntegTest', {
104
+ testCases: [stack],
95
105
  });
96
-
97
- // describe the results of the execution
98
- const describe = testCase.assertions.awsApiCall('@aws-sdk/client-sfn', 'DescribeExecution', {
99
- executionArn: start.getAttString('executionArn'),
106
+ const res = integ.assertions.awsApiCall('@aws-sdk/client-sfn', 'StartExecution', {
107
+ stateMachineArn: sm.stateMachineArn,
100
108
  });
101
-
102
- // assert the results
103
- describe.expect(ExpectedResult.objectLike({
109
+ const executionArn = res.getAttString('executionArn');
110
+ integ.assertions.awsApiCall('@aws-sdk/client-sfn', 'DescribeExecution', {
111
+ executionArn,
112
+ }).expect(ExpectedResult.objectLike({
104
113
  status: 'SUCCEEDED',
105
- output: JSON.stringify({
106
- ResponseBody: {
107
- message: 'Hello, tester!',
108
- },
109
- }),
110
- }));
114
+ })).waitForAssertions({
115
+ totalTimeout: cdk.Duration.seconds(10),
116
+ interval: cdk.Duration.seconds(3),
117
+ });
111
118
 
112
119
  app.synth();
@@ -1,101 +1,119 @@
1
- import * as path from 'path';
2
- import { IntegTest, ExpectedResult } from '@aws-cdk/integ-tests-alpha';
3
- import * as cdk from 'aws-cdk-lib';
4
- import * as apigateway from 'aws-cdk-lib/aws-apigateway';
5
- import * as events from 'aws-cdk-lib/aws-events';
1
+ import { Code, Function } from 'aws-cdk-lib/aws-lambda';
6
2
  import * as sfn from 'aws-cdk-lib/aws-stepfunctions';
7
- import * as lambda from 'aws-cdk-lib/aws-lambda-nodejs';
8
- import * as tasks from 'aws-cdk-lib/aws-stepfunctions-tasks';
9
- import { password, username } from './my-lambda-handler';
3
+ import * as cdk from 'aws-cdk-lib';
4
+ import { LambdaInvoke } from 'aws-cdk-lib/aws-stepfunctions-tasks';
5
+ import { IntegTest, ExpectedResult } from '@aws-cdk/integ-tests-alpha';
6
+ import { STANDARD_NODEJS_RUNTIME } from '../../../config';
10
7
 
11
8
  /*
12
- * Creates an API Gateway instance with a GET method and mock integration,
13
- * secured with basic auth. It then creates a matching Connection and uses it
14
- * in a state machine with a task state to invoke the endpoint.
9
+ * Creates a state machine with a task state to invoke a Lambda function
10
+ * The state machine creates a couple of Lambdas that pass results forward
11
+ * and into a Choice state that validates the output.
12
+ *
13
+ * Stack verification steps:
14
+ * The generated State Machine can be executed from the CLI (or Step Functions console)
15
+ * and runs with an execution status of `Succeeded`.
15
16
  *
16
- * Stack verification steps :
17
- * * aws stepfunctions start-execution --state-machine-arn <deployed state machine arn> : should return execution arn
18
- * * aws stepfunctions describe-execution --execution-arn <execution-arn generated before> : should return status as SUCCEEDED
17
+ * -- aws stepfunctions start-execution --state-machine-arn <state-machine-arn-from-output> provides execution arn
18
+ * -- aws stepfunctions describe-execution --execution-arn <state-machine-arn-from-output> returns a status of `Succeeded`
19
19
  */
20
20
  const app = new cdk.App({
21
21
  postCliContext: {
22
22
  '@aws-cdk/aws-lambda:useCdkManagedLogGroup': false,
23
23
  },
24
24
  });
25
- const stack = new cdk.Stack(app, 'aws-stepfunctions-tasks-http-invoke-integ');
25
+ const stack = new cdk.Stack(app, 'aws-stepfunctions-tasks-lambda-invoke-integ');
26
26
 
27
- const authorizerHandler = new lambda.NodejsFunction(stack, 'AuthorizerHandler', {
28
- entry: path.resolve(__dirname, 'my-lambda-handler', 'index.ts'),
29
- handler: 'handler',
27
+ const submitJobLambda = new Function(stack, 'submitJobLambda', {
28
+ code: Code.fromInline(`exports.handler = async (event, context) => {
29
+ return {
30
+ statusCode: '200',
31
+ body: 'hello, world!',
32
+ ...event,
33
+ };
34
+ };`),
35
+ runtime: STANDARD_NODEJS_RUNTIME,
36
+ handler: 'index.handler',
30
37
  });
31
38
 
32
- const authorizer = new apigateway.TokenAuthorizer(stack, 'Authorizer', {
33
- handler: authorizerHandler,
34
- identitySource: 'method.request.header.Authorization',
35
- resultsCacheTtl: cdk.Duration.seconds(0),
39
+ const submitJob = new LambdaInvoke(stack, 'Invoke Handler', {
40
+ lambdaFunction: submitJobLambda,
41
+ payload: sfn.TaskInput.fromObject({
42
+ execId: sfn.JsonPath.executionId,
43
+ execInput: sfn.JsonPath.executionInput,
44
+ execName: sfn.JsonPath.executionName,
45
+ execRoleArn: sfn.JsonPath.executionRoleArn,
46
+ execStartTime: sfn.JsonPath.executionStartTime,
47
+ stateEnteredTime: sfn.JsonPath.stateEnteredTime,
48
+ stateName: sfn.JsonPath.stateName,
49
+ stateRetryCount: sfn.JsonPath.stateRetryCount,
50
+ stateMachineId: sfn.JsonPath.stateMachineId,
51
+ stateMachineName: sfn.JsonPath.stateMachineName,
52
+ }),
53
+ outputPath: '$.Payload',
36
54
  });
37
55
 
38
- const api = new apigateway.RestApi(stack, 'IntegRestApi');
56
+ const checkJobStateLambda = new Function(stack, 'checkJobStateLambda', {
57
+ code: Code.fromInline(`exports.handler = async function(event, context) {
58
+ const expectedFields = [
59
+ 'execId', 'execInput', 'execName', 'execRoleArn',
60
+ 'execStartTime', 'stateEnteredTime', 'stateName',
61
+ 'stateRetryCount', 'stateMachineId', 'stateMachineName',
62
+ ];
63
+ const fieldsAreSet = expectedFields.every(field => event[field] !== undefined);
64
+ return {
65
+ status: event.statusCode === '200' && fieldsAreSet ? 'SUCCEEDED' : 'FAILED'
66
+ };
67
+ };`),
68
+ runtime: STANDARD_NODEJS_RUNTIME,
69
+ handler: 'index.handler',
70
+ });
39
71
 
40
- api.root.addResource('test').addMethod(
41
- 'GET',
42
- new apigateway.MockIntegration({
43
- integrationResponses: [
44
- {
45
- statusCode: '200',
46
- responseTemplates: {
47
- 'application/json': JSON.stringify({ message: 'Hello, tester!' }),
48
- },
49
- },
50
- ],
51
- passthroughBehavior: apigateway.PassthroughBehavior.NEVER,
52
- requestTemplates: {
53
- 'application/json': '{ "statusCode": 200 }',
54
- },
55
- }),
56
- {
57
- authorizer,
58
- methodResponses: [
59
- {
60
- statusCode: '200',
61
- },
62
- ],
72
+ const checkJobState = new LambdaInvoke(stack, 'Check the job state', {
73
+ lambdaFunction: checkJobStateLambda,
74
+ resultSelector: {
75
+ status: sfn.JsonPath.stringAt('$.Payload.status'),
63
76
  },
64
- );
65
-
66
- const connection = new events.Connection(stack, 'Connection', {
67
- authorization: events.Authorization.basic(username, cdk.SecretValue.unsafePlainText(password)),
68
77
  });
69
78
 
70
- const httpInvokeTask = new tasks.HttpInvoke(stack, 'Invoke HTTP Endpoint', {
71
- apiRoot: api.urlForPath('/'),
72
- apiEndpoint: sfn.TaskInput.fromText('/test'),
73
- connection,
74
- method: sfn.TaskInput.fromText('GET'),
79
+ const isComplete = new sfn.Choice(stack, 'Job Complete?');
80
+ const jobFailed = new sfn.Fail(stack, 'Job Failed', {
81
+ cause: 'Job Failed',
82
+ error: 'Received a status that was not 200',
75
83
  });
84
+ const finalStatus = new sfn.Pass(stack, 'Final step');
85
+
86
+ const chain = sfn.Chain.start(submitJob)
87
+ .next(checkJobState)
88
+ .next(
89
+ isComplete
90
+ .when(sfn.Condition.stringEquals('$.status', 'FAILED'), jobFailed)
91
+ .when(sfn.Condition.stringEquals('$.status', 'SUCCEEDED'), finalStatus),
92
+ );
76
93
 
77
94
  const sm = new sfn.StateMachine(stack, 'StateMachine', {
78
- definition: httpInvokeTask,
95
+ definition: chain,
79
96
  timeout: cdk.Duration.seconds(30),
80
97
  });
81
98
 
82
- const testCase = new IntegTest(app, 'HttpInvokeTest', {
83
- testCases: [stack],
99
+ new cdk.CfnOutput(stack, 'stateMachineArn', {
100
+ value: sm.stateMachineArn,
84
101
  });
85
102
 
86
- // Start an execution
87
- const start = testCase.assertions.awsApiCall('StepFunctions', 'startExecution', {
88
- stateMachineArn: sm.stateMachineArn,
103
+ const integ = new IntegTest(app, 'IntegTest', {
104
+ testCases: [stack],
89
105
  });
90
-
91
- // describe the results of the execution
92
- const describe = testCase.assertions.awsApiCall('StepFunctions', 'describeExecution', {
93
- executionArn: start.getAttString('executionArn'),
106
+ const res = integ.assertions.awsApiCall('StepFunctions', 'startExecution', {
107
+ stateMachineArn: sm.stateMachineArn,
94
108
  });
95
-
96
- // assert the results
97
- describe.expect(ExpectedResult.objectLike({
109
+ const executionArn = res.getAttString('executionArn');
110
+ integ.assertions.awsApiCall('StepFunctions', 'describeExecution', {
111
+ executionArn,
112
+ }).expect(ExpectedResult.objectLike({
98
113
  status: 'SUCCEEDED',
99
- }));
114
+ })).waitForAssertions({
115
+ totalTimeout: cdk.Duration.seconds(10),
116
+ interval: cdk.Duration.seconds(3),
117
+ });
100
118
 
101
119
  app.synth();
@@ -1,69 +1,122 @@
1
- import * as path from 'path';
2
- import * as glue from 'aws-cdk-lib/aws-glue';
1
+ import * as ec2 from 'aws-cdk-lib/aws-ec2';
2
+ import * as eks from 'aws-cdk-lib/aws-eks';
3
+ import { AwsAuthMapping } from 'aws-cdk-lib/aws-eks';
3
4
  import * as iam from 'aws-cdk-lib/aws-iam';
4
- import * as assets from 'aws-cdk-lib/aws-s3-assets';
5
5
  import * as sfn from 'aws-cdk-lib/aws-stepfunctions';
6
6
  import * as cdk from 'aws-cdk-lib';
7
- import { GlueStartJobRun } from 'aws-cdk-lib/aws-stepfunctions-tasks';
8
- import { IntegTest } from '@aws-cdk/integ-tests-alpha';
7
+ import { Aws } from 'aws-cdk-lib';
8
+ import * as integ from '@aws-cdk/integ-tests-alpha';
9
+ import { KubectlV31Layer } from '@aws-cdk/lambda-layer-kubectl-v31';
10
+ import { EmrContainersStartJobRun, ReleaseLabel, VirtualClusterInput } from 'aws-cdk-lib/aws-stepfunctions-tasks';
11
+ import { EC2_RESTRICT_DEFAULT_SECURITY_GROUP } from 'aws-cdk-lib/cx-api';
9
12
 
10
- /*
13
+ /**
11
14
  * Stack verification steps:
12
- * * aws stepfunctions start-execution --state-machine-arn <deployed state machine arn>
13
- * * aws stepfunctions describe-execution --execution-arn <execution arn created above>
14
- * The "describe-execution" call should eventually return status "SUCCEEDED".
15
- * NOTE: It will take up to 15 minutes for the step function to complete due to the cold start time
16
- * for AWS Glue, which as of 02/2020, is around 10-15 minutes.
15
+ * Everything in the link below must be setup before running the state machine.
16
+ * @see https://docs.aws.amazon.com/emr/latest/EMR-on-EKS-DevelopmentGuide/setting-up-enable-IAM.html
17
+ * aws stepfunctions start-execution --state-machine-arn <deployed state machine arn> : should return execution arn
18
+ * aws stepfunctions describe-execution --execution-arn <exection-arn generated before> : should return status as SUCCEEDED
17
19
  */
18
20
 
19
- const app = new cdk.App();
20
- const stack = new cdk.Stack(app, 'aws-stepfunctions-integ');
21
+ const app = new cdk.App({
22
+ postCliContext: {
23
+ '@aws-cdk/aws-lambda:useCdkManagedLogGroup': false,
24
+ '@aws-cdk/aws-lambda:createNewPoliciesWithAddToRolePolicy': false,
25
+ },
26
+ });
27
+ const stack = new cdk.Stack(app, 'aws-stepfunctions-tasks-emr-containers-start-job-run');
28
+ stack.node.setContext(EC2_RESTRICT_DEFAULT_SECURITY_GROUP, false);
29
+
30
+ const eksCluster = new eks.Cluster(stack, 'integration-test-eks-cluster', {
31
+ version: eks.KubernetesVersion.V1_30,
32
+ defaultCapacity: 3,
33
+ defaultCapacityInstance: ec2.InstanceType.of(ec2.InstanceClass.M5, ec2.InstanceSize.XLARGE),
34
+ kubectlLayer: new KubectlV31Layer(stack, 'KubectlLayer'),
35
+ });
21
36
 
22
- const codeAsset = new assets.Asset(stack, 'Glue Job Script', {
23
- path: path.join(__dirname, 'my-glue-script/job.py'),
37
+ const virtualCluster = new cdk.CfnResource(stack, 'Virtual Cluster', {
38
+ type: 'AWS::EMRContainers::VirtualCluster',
39
+ properties: {
40
+ ContainerProvider: {
41
+ Id: eksCluster.clusterName,
42
+ Info: {
43
+ EksInfo: {
44
+ Namespace: 'default',
45
+ },
46
+ },
47
+ Type: 'EKS',
48
+ },
49
+ Name: 'Virtual-Cluster-Name',
50
+ },
24
51
  });
25
52
 
26
- const jobRole = new iam.Role(stack, 'Glue Job Role', {
27
- assumedBy: new iam.ServicePrincipal('glue'),
28
- managedPolicies: [
29
- iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSGlueServiceRole'),
53
+ const emrRole = eksCluster.addManifest('emrRole', {
54
+ apiVersion: 'rbac.authorization.k8s.io/v1',
55
+ kind: 'Role',
56
+ metadata: { name: 'emr-containers', namespace: 'default' },
57
+ rules: [
58
+ { apiGroups: [''], resources: ['namespaces'], verbs: ['get'] },
59
+ { apiGroups: [''], resources: ['serviceaccounts', 'services', 'configmaps', 'events', 'pods', 'pods/log'], verbs: ['get', 'list', 'watch', 'describe', 'create', 'edit', 'delete', 'deletecollection', 'annotate', 'patch', 'label'] },
60
+ { apiGroups: [''], resources: ['secrets'], verbs: ['create', 'patch', 'delete', 'watch'] },
61
+ { apiGroups: ['apps'], resources: ['statefulsets', 'deployments'], verbs: ['get', 'list', 'watch', 'describe', 'create', 'edit', 'delete', 'annotate', 'patch', 'label'] },
62
+ { apiGroups: ['batch'], resources: ['jobs'], verbs: ['get', 'list', 'watch', 'describe', 'create', 'edit', 'delete', 'annotate', 'patch', 'label'] },
63
+ { apiGroups: ['extensions'], resources: ['ingresses'], verbs: ['get', 'list', 'watch', 'describe', 'create', 'edit', 'delete', 'annotate', 'patch', 'label'] },
64
+ { apiGroups: ['rbac.authorization.k8s.io'], resources: ['roles', 'rolebindings'], verbs: ['get', 'list', 'watch', 'describe', 'create', 'edit', 'delete', 'deletecollection', 'annotate', 'patch', 'label'] },
30
65
  ],
31
66
  });
32
- codeAsset.grantRead(jobRole);
33
67
 
34
- const job = new glue.CfnJob(stack, 'Glue Job', {
35
- name: 'My Glue Job',
36
- glueVersion: '1.0',
37
- command: {
38
- name: 'glueetl',
39
- pythonVersion: '3',
40
- scriptLocation: `s3://${codeAsset.s3BucketName}/${codeAsset.s3ObjectKey}`,
41
- },
42
- role: jobRole.roleArn,
68
+ const emrRoleBind = eksCluster.addManifest('emrRoleBind', {
69
+ apiVersion: 'rbac.authorization.k8s.io/v1',
70
+ kind: 'RoleBinding',
71
+ metadata: { name: 'emr-containers', namespace: 'default' },
72
+ subjects: [{ kind: 'User', name: 'emr-containers', apiGroup: 'rbac.authorization.k8s.io' }],
73
+ roleRef: { kind: 'Role', name: 'emr-containers', apiGroup: 'rbac.authorization.k8s.io' },
43
74
  });
44
75
 
45
- const jobTask = new GlueStartJobRun(stack, 'Glue Job Task', {
46
- glueJobName: job.name!,
47
- integrationPattern: sfn.IntegrationPattern.RUN_JOB,
48
- arguments: sfn.TaskInput.fromObject({
49
- '--enable-metrics': 'true',
50
- }),
76
+ emrRoleBind.node.addDependency(emrRole);
77
+
78
+ const emrServiceRole = iam.Role.fromRoleArn(stack, 'emrServiceRole', 'arn:aws:iam::'+Aws.ACCOUNT_ID+':role/AWSServiceRoleForAmazonEMRContainers');
79
+ const authMapping: AwsAuthMapping = { groups: [], username: 'emr-containers' };
80
+ eksCluster.awsAuth.addRoleMapping(emrServiceRole, authMapping);
81
+
82
+ virtualCluster.node.addDependency(emrRoleBind);
83
+ virtualCluster.node.addDependency(eksCluster.awsAuth);
84
+
85
+ const startJobRunJob = new EmrContainersStartJobRun(stack, 'Start a Job Run', {
86
+ virtualCluster: VirtualClusterInput.fromVirtualClusterId(virtualCluster.getAtt('Id').toString()),
87
+ releaseLabel: ReleaseLabel.EMR_6_2_0,
88
+ jobName: 'EMR-Containers-Job',
89
+ jobDriver: {
90
+ sparkSubmitJobDriver: {
91
+ entryPoint: sfn.TaskInput.fromText('local:///usr/lib/spark/examples/src/main/python/pi.py'),
92
+ entryPointArguments: sfn.TaskInput.fromObject(['2']),
93
+ sparkSubmitParameters: '--conf spark.driver.memory=512M --conf spark.kubernetes.driver.request.cores=0.2 --conf spark.kubernetes.executor.request.cores=0.2 --conf spark.sql.shuffle.partitions=60 --conf spark.dynamicAllocation.enabled=false',
94
+ },
95
+ },
51
96
  });
52
97
 
53
- const startTask = new sfn.Pass(stack, 'Start Task');
54
- const endTask = new sfn.Pass(stack, 'End Task');
98
+ const chain = sfn.Chain.start(startJobRunJob);
55
99
 
56
- const stateMachine = new sfn.StateMachine(stack, 'State Machine', {
57
- definition: sfn.Chain.start(startTask).next(jobTask).next(endTask),
100
+ const sm = new sfn.StateMachine(stack, 'StateMachine', {
101
+ definition: chain,
102
+ timeout: cdk.Duration.seconds(1000),
58
103
  });
59
104
 
60
- new cdk.CfnOutput(stack, 'State Machine ARN Output', {
61
- value: stateMachine.stateMachineArn,
105
+ new cdk.CfnOutput(stack, 'stateMachineArn', {
106
+ value: sm.stateMachineArn,
62
107
  });
63
108
 
64
- new IntegTest(app, 'AwsSfnIntegTest', {
109
+ new integ.IntegTest(app, 'aws-stepfunctions-tasks-emr-containers-start-job-run-integ', {
65
110
  testCases: [stack],
66
- diffAssets: true,
111
+ // Test includes assets that are updated weekly. If not disabled, the upgrade PR will fail.
112
+ diffAssets: false,
113
+ cdkCommandOptions: {
114
+ deploy: {
115
+ args: {
116
+ rollback: true,
117
+ },
118
+ },
119
+ },
67
120
  });
68
121
 
69
122
  app.synth();