From 9e21803b31e5df82f2abc7f6e7c7074a09ad7ed6 Mon Sep 17 00:00:00 2001 From: kazuho cryer-shinozuka Date: Tue, 30 Jan 2024 07:56:47 +0900 Subject: [PATCH 1/7] feat(sqs): support for permission settings for dead letter source queues (#28745) This PR allows for the configuration of constraints on source queues that can designate this queue as their dead letter queue. ```ts declare const sourceQueue: sqs.IQueue; // Only the sourceQueue can specify this queue as the dead-letter queue. const queue1 = new sqs.Queue(this, 'Queue1', { redriveAllowPolicy: { sourceQueues: [sourceQueue], } }); // No source queues can specify this queue as the dead-letter queue. const queue2 = new sqs.Queue(this, 'Queue2', { redriveAllowPolicy: { redrivePermission: sqs.RedrivePermission.DENY_ALL, } }); ``` Closes #19766. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- ...efaultTestDeployAssert659366A6.assets.json | 19 ++ ...aultTestDeployAssert659366A6.template.json | 36 ++++ .../aws-cdk-sqs.assets.json | 19 ++ .../aws-cdk-sqs.template.json | 72 +++++++ .../cdk.out | 1 + .../integ.json | 12 ++ .../manifest.json | 125 ++++++++++++ .../tree.json | 185 ++++++++++++++++++ .../test/integ.sqs-source-queue-permission.ts | 24 +++ packages/aws-cdk-lib/aws-sqs/README.md | 24 +++ packages/aws-cdk-lib/aws-sqs/lib/queue.ts | 78 ++++++++ packages/aws-cdk-lib/aws-sqs/test/sqs.test.ts | 122 ++++++++++++ 12 files changed, 717 insertions(+) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/SqsTestDefaultTestDeployAssert659366A6.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/SqsTestDefaultTestDeployAssert659366A6.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/aws-cdk-sqs.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/aws-cdk-sqs.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/tree.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.ts diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/SqsTestDefaultTestDeployAssert659366A6.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/SqsTestDefaultTestDeployAssert659366A6.assets.json new file mode 100644 index 0000000000000..6d7ba276e8984 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/SqsTestDefaultTestDeployAssert659366A6.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "SqsTestDefaultTestDeployAssert659366A6.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/SqsTestDefaultTestDeployAssert659366A6.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/SqsTestDefaultTestDeployAssert659366A6.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/SqsTestDefaultTestDeployAssert659366A6.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/aws-cdk-sqs.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/aws-cdk-sqs.assets.json new file mode 100644 index 0000000000000..588e3fca7e14a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/aws-cdk-sqs.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "d72236aa3f4906a9f550a7a28798879c3ad8fe6a6d47dd38a1d3e3565256f769": { + "source": { + "path": "aws-cdk-sqs.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "d72236aa3f4906a9f550a7a28798879c3ad8fe6a6d47dd38a1d3e3565256f769.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/aws-cdk-sqs.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/aws-cdk-sqs.template.json new file mode 100644 index 0000000000000..a116bcb48f618 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/aws-cdk-sqs.template.json @@ -0,0 +1,72 @@ +{ + "Resources": { + "SourceQueue1F4BBA4BB": { + "Type": "AWS::SQS::Queue", + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "SourceQueue22481CB5A": { + "Type": "AWS::SQS::Queue", + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "DeadLetterQueue9F481546": { + "Type": "AWS::SQS::Queue", + "Properties": { + "RedriveAllowPolicy": { + "redrivePermission": "byQueue", + "sourceQueueArns": [ + { + "Fn::GetAtt": [ + "SourceQueue1F4BBA4BB", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "SourceQueue22481CB5A", + "Arn" + ] + } + ] + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/integ.json new file mode 100644 index 0000000000000..544e3721ce789 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "36.0.0", + "testCases": { + "SqsTest/DefaultTest": { + "stacks": [ + "aws-cdk-sqs" + ], + "assertionStack": "SqsTest/DefaultTest/DeployAssert", + "assertionStackName": "SqsTestDefaultTestDeployAssert659366A6" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/manifest.json new file mode 100644 index 0000000000000..86395236fd481 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/manifest.json @@ -0,0 +1,125 @@ +{ + "version": "36.0.0", + "artifacts": { + "aws-cdk-sqs.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-cdk-sqs.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-cdk-sqs": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-cdk-sqs.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/d72236aa3f4906a9f550a7a28798879c3ad8fe6a6d47dd38a1d3e3565256f769.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-cdk-sqs.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "aws-cdk-sqs.assets" + ], + "metadata": { + "/aws-cdk-sqs/SourceQueue1/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "SourceQueue1F4BBA4BB" + } + ], + "/aws-cdk-sqs/SourceQueue2/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "SourceQueue22481CB5A" + } + ], + "/aws-cdk-sqs/DeadLetterQueue/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "DeadLetterQueue9F481546" + } + ], + "/aws-cdk-sqs/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-cdk-sqs/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-cdk-sqs" + }, + "SqsTestDefaultTestDeployAssert659366A6.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "SqsTestDefaultTestDeployAssert659366A6.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "SqsTestDefaultTestDeployAssert659366A6": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "SqsTestDefaultTestDeployAssert659366A6.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "SqsTestDefaultTestDeployAssert659366A6.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "SqsTestDefaultTestDeployAssert659366A6.assets" + ], + "metadata": { + "/SqsTest/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/SqsTest/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "SqsTest/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/tree.json new file mode 100644 index 0000000000000..3cf1e989a0a30 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.js.snapshot/tree.json @@ -0,0 +1,185 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "aws-cdk-sqs": { + "id": "aws-cdk-sqs", + "path": "aws-cdk-sqs", + "children": { + "SourceQueue1": { + "id": "SourceQueue1", + "path": "aws-cdk-sqs/SourceQueue1", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-sqs/SourceQueue1/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SQS::Queue", + "aws:cdk:cloudformation:props": {} + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_sqs.CfnQueue", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_sqs.Queue", + "version": "0.0.0" + } + }, + "SourceQueue2": { + "id": "SourceQueue2", + "path": "aws-cdk-sqs/SourceQueue2", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-sqs/SourceQueue2/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SQS::Queue", + "aws:cdk:cloudformation:props": {} + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_sqs.CfnQueue", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_sqs.Queue", + "version": "0.0.0" + } + }, + "DeadLetterQueue": { + "id": "DeadLetterQueue", + "path": "aws-cdk-sqs/DeadLetterQueue", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-sqs/DeadLetterQueue/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SQS::Queue", + "aws:cdk:cloudformation:props": { + "redriveAllowPolicy": { + "redrivePermission": "byQueue", + "sourceQueueArns": [ + { + "Fn::GetAtt": [ + "SourceQueue1F4BBA4BB", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "SourceQueue22481CB5A", + "Arn" + ] + } + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_sqs.CfnQueue", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_sqs.Queue", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "aws-cdk-sqs/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "aws-cdk-sqs/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "SqsTest": { + "id": "SqsTest", + "path": "SqsTest", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "SqsTest/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "SqsTest/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "SqsTest/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "SqsTest/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "SqsTest/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.ts new file mode 100644 index 0000000000000..e6079ba21f67e --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-sqs/test/integ.sqs-source-queue-permission.ts @@ -0,0 +1,24 @@ + +import { App, Stack } from 'aws-cdk-lib'; +import * as integ from '@aws-cdk/integ-tests-alpha'; +import { Queue, RedrivePermission } from 'aws-cdk-lib/aws-sqs'; + +const app = new App(); + +const stack = new Stack(app, 'aws-cdk-sqs'); + +const sourceQueue1 = new Queue(stack, 'SourceQueue1'); +const sourceQueue2 = new Queue(stack, 'SourceQueue2'); + +new Queue(stack, 'DeadLetterQueue', { + redriveAllowPolicy: { + sourceQueues: [sourceQueue1, sourceQueue2], + redrivePermission: RedrivePermission.BY_QUEUE, + }, +}); + +new integ.IntegTest(app, 'SqsTest', { + testCases: [stack], +}); + +app.synth(); diff --git a/packages/aws-cdk-lib/aws-sqs/README.md b/packages/aws-cdk-lib/aws-sqs/README.md index d910cd3d96b80..b7f4f8e409972 100644 --- a/packages/aws-cdk-lib/aws-sqs/README.md +++ b/packages/aws-cdk-lib/aws-sqs/README.md @@ -84,3 +84,27 @@ the SQS manual. Note that FIFO queues are not available in all AWS regions. A queue can be made a FIFO queue by either setting `fifo: true`, giving it a name which ends in `".fifo"`, or by enabling a FIFO specific feature such as: content-based deduplication, deduplication scope or fifo throughput limit. + +## Dead letter source queues permission + +You can configure the permission settings for queues that can designate the created queue as their dead-letter queue using the `redriveAllowPolicy` attribute. + +By default, all queues within the same account and region are permitted as source queues. + +```ts +declare const sourceQueue: sqs.IQueue; + +// Only the sourceQueue can specify this queue as the dead-letter queue. +const queue1 = new sqs.Queue(this, 'Queue2', { + redriveAllowPolicy: { + sourceQueues: [sourceQueue], + } +}); + +// No source queues can specify this queue as the dead-letter queue. +const queue2 = new sqs.Queue(this, 'Queue', { + redriveAllowPolicy: { + redrivePermission: sqs.RedrivePermission.DENY_ALL, + } +}); +``` diff --git a/packages/aws-cdk-lib/aws-sqs/lib/queue.ts b/packages/aws-cdk-lib/aws-sqs/lib/queue.ts index a122045571937..be742bf64c045 100644 --- a/packages/aws-cdk-lib/aws-sqs/lib/queue.ts +++ b/packages/aws-cdk-lib/aws-sqs/lib/queue.ts @@ -178,6 +178,14 @@ export interface QueueProps { * @default false */ readonly enforceSSL?: boolean; + + /** + * The string that includes the parameters for the permissions for the dead-letter queue + * redrive permission and which source queues can specify dead-letter queues. + * + * @default - All source queues can designate this queue as their dead-letter queue. + */ + readonly redriveAllowPolicy?: RedriveAllowPolicy; } /** @@ -195,6 +203,34 @@ export interface DeadLetterQueue { readonly maxReceiveCount: number; } +/** + * Permission settings for the dead letter source queue + */ +export interface RedriveAllowPolicy { + /** + * Permission settings for source queues that can designate this queue as their dead-letter queue. + * + * @default - `RedrivePermission.BY_QUEUE` if `sourceQueues` is specified,`RedrivePermission.ALLOW_ALL` otherwise. + */ + readonly redrivePermission?: RedrivePermission; + + /** + * Source queues that can designate this queue as their dead-letter queue. + * + * When `redrivePermission` is set to `RedrivePermission.BY_QUEUE`, this parameter is required. + * + * You can specify up to 10 source queues. + * To allow more than 10 source queues to specify dead-letter queues, set the `redrivePermission` to + * `RedrivePermission.ALLOW_ALL`. + * + * When `redrivePermission` is either `RedrivePermission.ALLOW_ALL` or `RedrivePermission.DENY_ALL`, + * this parameter cannot be set. + * + * @default - Required when `redrivePermission` is `RedrivePermission.BY_QUEUE`, cannot be defined otherwise. + */ + readonly sourceQueues?: IQueue[]; +} + /** * What kind of deduplication scope to apply */ @@ -223,6 +259,24 @@ export enum FifoThroughputLimit { PER_MESSAGE_GROUP_ID = 'perMessageGroupId', } +/** + * The permission type that defines which source queues can specify the current queue as the dead-letter queue + */ +export enum RedrivePermission { + /** + * Any source queues in this AWS account in the same Region can specify this queue as the dead-letter queue + */ + ALLOW_ALL = 'allowAll', + /** + * No source queues can specify this queue as the dead-letter queue + */ + DENY_ALL = 'denyAll', + /** + * Only queues specified by the `sourceQueueArns` parameter can specify this queue as the dead-letter queue + */ + BY_QUEUE = 'byQueue', +} + /** * A new Amazon SQS queue */ @@ -331,6 +385,20 @@ export class Queue extends QueueBase { validateProps(props); + if (props.redriveAllowPolicy) { + const { redrivePermission, sourceQueues } = props.redriveAllowPolicy; + if (redrivePermission === RedrivePermission.BY_QUEUE) { + if (!sourceQueues || sourceQueues.length === 0) { + throw new Error('At least one source queue must be specified when RedrivePermission is set to \'byQueue\''); + } + if (sourceQueues && sourceQueues.length > 10) { + throw new Error('Up to 10 sourceQueues can be specified. Set RedrivePermission to \'allowAll\' to specify more'); + } + } else if (redrivePermission && sourceQueues) { + throw new Error('sourceQueues cannot be configured when RedrivePermission is set to \'allowAll\' or \'denyAll\''); + } + } + const redrivePolicy = props.deadLetterQueue ? { deadLetterTargetArn: props.deadLetterQueue.queue.queueArn, @@ -338,6 +406,15 @@ export class Queue extends QueueBase { } : undefined; + // When `redriveAllowPolicy` is provided, `redrivePermission` defaults to allow all queues (`ALLOW_ALL`); + const redriveAllowPolicy = props.redriveAllowPolicy ? { + redrivePermission: props.redriveAllowPolicy.redrivePermission + // When `sourceQueues` is provided in `redriveAllowPolicy`, `redrivePermission` defaults to allow specified queues (`BY_QUEUE`); + // otherwise, it defaults to allow all queues (`ALLOW_ALL`). + ?? props.redriveAllowPolicy.sourceQueues ? RedrivePermission.BY_QUEUE : RedrivePermission.ALLOW_ALL, + sourceQueueArns: props.redriveAllowPolicy.sourceQueues?.map(q => q.queueArn), + } : undefined; + const { encryptionMasterKey, encryptionProps, encryptionType } = _determineEncryptionProps.call(this); const fifoProps = this.determineFifoProps(props); @@ -348,6 +425,7 @@ export class Queue extends QueueBase { ...fifoProps, ...encryptionProps, redrivePolicy, + redriveAllowPolicy, delaySeconds: props.deliveryDelay && props.deliveryDelay.toSeconds(), maximumMessageSize: props.maxMessageSizeBytes, messageRetentionPeriod: props.retentionPeriod && props.retentionPeriod.toSeconds(), diff --git a/packages/aws-cdk-lib/aws-sqs/test/sqs.test.ts b/packages/aws-cdk-lib/aws-sqs/test/sqs.test.ts index 0c0492b26f28b..956c8a77cdd7a 100644 --- a/packages/aws-cdk-lib/aws-sqs/test/sqs.test.ts +++ b/packages/aws-cdk-lib/aws-sqs/test/sqs.test.ts @@ -730,6 +730,128 @@ test('fails if queue policy has no IAM principals', () => { expect(() => app.synth()).toThrow(/A PolicyStatement used in a resource-based policy must specify at least one IAM principal/); }); +describe('redriveAllowPolicy', () => { + test('Default settings for the dead letter source queue permission', () => { + const stack = new Stack(); + new sqs.Queue(stack, 'Queue', { + redriveAllowPolicy: {}, + }); + + Template.fromStack(stack).templateMatches({ + 'Resources': { + 'Queue4A7E3555': { + 'Type': 'AWS::SQS::Queue', + 'Properties': { + 'RedriveAllowPolicy': { + 'redrivePermission': 'allowAll', + }, + }, + 'UpdateReplacePolicy': 'Delete', + 'DeletionPolicy': 'Delete', + }, + }, + }); + }); + + test('explicit specification of dead letter source queues', () => { + const stack = new Stack(); + const sourceQueue1 = new sqs.Queue(stack, 'SourceQueue1'); + const sourceQueue2 = new sqs.Queue(stack, 'SourceQueue2'); + new sqs.Queue(stack, 'Queue', { redriveAllowPolicy: { sourceQueues: [sourceQueue1, sourceQueue2] } }); + + Template.fromStack(stack).templateMatches({ + 'Resources': { + 'SourceQueue1F4BBA4BB': { + 'Type': 'AWS::SQS::Queue', + 'UpdateReplacePolicy': 'Delete', + 'DeletionPolicy': 'Delete', + }, + 'SourceQueue22481CB5A': { + 'Type': 'AWS::SQS::Queue', + 'UpdateReplacePolicy': 'Delete', + 'DeletionPolicy': 'Delete', + }, + 'Queue4A7E3555': { + 'Type': 'AWS::SQS::Queue', + 'Properties': { + 'RedriveAllowPolicy': { + 'redrivePermission': 'byQueue', + 'sourceQueueArns': [ + { + 'Fn::GetAtt': [ + 'SourceQueue1F4BBA4BB', + 'Arn', + ], + }, + { + 'Fn::GetAtt': [ + 'SourceQueue22481CB5A', + 'Arn', + ], + }, + ], + }, + }, + 'UpdateReplacePolicy': 'Delete', + 'DeletionPolicy': 'Delete', + }, + }, + }); + }); + + test('throw if sourceQueues is not specified when redrivePermission is byQueue', () => { + const stack = new Stack(); + expect(() => { + new sqs.Queue(stack, 'Queue', { + redriveAllowPolicy: { + redrivePermission: sqs.RedrivePermission.BY_QUEUE, + }, + }); + }).toThrow(/At least one source queue must be specified when RedrivePermission is set to 'byQueue'/); + }); + + test('throw if dead letter source queues are specified with allowAll permission', () => { + const stack = new Stack(); + const sourceQueue1 = new sqs.Queue(stack, 'SourceQueue1'); + expect(() => { + new sqs.Queue(stack, 'Queue', { + redriveAllowPolicy: { + sourceQueues: [sourceQueue1], + redrivePermission: sqs.RedrivePermission.ALLOW_ALL, + }, + }); + }).toThrow(/sourceQueues cannot be configured when RedrivePermission is set to 'allowAll' or 'denyAll'/); + }); + + test('throw if souceQueues length is greater than 10', () => { + const stack = new Stack(); + const sourceQueues: sqs.IQueue[] = []; + for (let i = 0; i < 11; i++) { + sourceQueues.push(new sqs.Queue(stack, `SourceQueue${i}`)); + } + expect(() => { + new sqs.Queue(stack, 'Queue', { + redriveAllowPolicy: { + sourceQueues, + redrivePermission: sqs.RedrivePermission.BY_QUEUE, + }, + }); + }).toThrow(/Up to 10 sourceQueues can be specified. Set RedrivePermission to 'allowAll' to specify more/); + }); + + test('throw if sourceQueues is blank array when redrivePermission is byQueue', () => { + const stack = new Stack(); + expect(() => { + new sqs.Queue(stack, 'Queue', { + redriveAllowPolicy: { + sourceQueues: [], + redrivePermission: sqs.RedrivePermission.BY_QUEUE, + }, + }); + }).toThrow(/At least one source queue must be specified when RedrivePermission is set to 'byQueue'/); + }); +}); + function testGrant(action: (q: sqs.Queue, principal: iam.IPrincipal) => void, ...expectedActions: string[]) { const stack = new Stack(); const queue = new sqs.Queue(stack, 'MyQueue'); From 534794c900025b174825f82feb00671305555c0a Mon Sep 17 00:00:00 2001 From: Michael Sambol Date: Mon, 29 Jan 2024 15:43:24 -0800 Subject: [PATCH 2/7] feat(cognito): validate oidc provider name (#28802) Closes #28667. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-cognito/lib/user-pool-idps/oidc.ts | 9 +++++---- .../aws-cognito/test/user-pool-idps/oidc.test.ts | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/packages/aws-cdk-lib/aws-cognito/lib/user-pool-idps/oidc.ts b/packages/aws-cdk-lib/aws-cognito/lib/user-pool-idps/oidc.ts index 3daaf11afccdd..d3018d4584514 100644 --- a/packages/aws-cdk-lib/aws-cognito/lib/user-pool-idps/oidc.ts +++ b/packages/aws-cdk-lib/aws-cognito/lib/user-pool-idps/oidc.ts @@ -107,10 +107,6 @@ export class UserPoolIdentityProviderOidc extends UserPoolIdentityProviderBase { constructor(scope: Construct, id: string, props: UserPoolIdentityProviderOidcProps) { super(scope, id, props); - if (props.name && !Token.isUnresolved(props.name) && (props.name.length < 3 || props.name.length > 32)) { - throw new Error(`Expected provider name to be between 3 and 32 characters, received ${props.name} (${props.name.length} characters)`); - } - const scopes = props.scopes ?? ['openid']; const resource = new CfnUserPoolIdentityProvider(this, 'Resource', { @@ -140,6 +136,11 @@ export class UserPoolIdentityProviderOidc extends UserPoolIdentityProviderBase { if (!Token.isUnresolved(name) && (name.length < 3 || name.length > 32)) { throw new Error(`Expected provider name to be between 3 and 32 characters, received ${name} (${name.length} characters)`); } + // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cognito-userpoolidentityprovider.html#cfn-cognito-userpoolidentityprovider-providername + // u is for unicode + if (!name.match(/^[^_\p{Z}][\p{L}\p{M}\p{S}\p{N}\p{P}][^_\p{Z}]+$/u)) { + throw new Error(`Expected provider name must match [^_\p{Z}][\p{L}\p{M}\p{S}\p{N}\p{P}][^_\p{Z}]+, received ${name}`); + } return name; } diff --git a/packages/aws-cdk-lib/aws-cognito/test/user-pool-idps/oidc.test.ts b/packages/aws-cdk-lib/aws-cognito/test/user-pool-idps/oidc.test.ts index e3b13cda4e0f7..9e37c7a02e411 100644 --- a/packages/aws-cdk-lib/aws-cognito/test/user-pool-idps/oidc.test.ts +++ b/packages/aws-cdk-lib/aws-cognito/test/user-pool-idps/oidc.test.ts @@ -173,6 +173,22 @@ describe('UserPoolIdentityProvider', () => { })).toThrow(/Expected provider name to be between 3 and 32 characters/); }); + test('throws with provider name that doesn\'t match pattern', () => { + // GIVEN + const stack = new Stack(); + const pool = new UserPool(stack, 'userpool'); + const name = ' thisisabadname'; + + // THEN + expect(() => new UserPoolIdentityProviderOidc(stack, 'userpoolidp', { + userPool: pool, + name, + clientId: 'client-id', + clientSecret: 'client-secret', + issuerUrl: 'https://my-issuer-url.com', + })).toThrow(`Expected provider name must match [^_\p{Z}][\p{L}\p{M}\p{S}\p{N}\p{P}][^_\p{Z}]+, received ${name}`); + }); + test('generates a valid name when unique id is too short', () => { // GIVEN const stack = new Stack(); From 990ead35abdb8f4860280a3c75d923b1e5f5b1ba Mon Sep 17 00:00:00 2001 From: George Christman <93141496+georgechristman@users.noreply.github.com> Date: Mon, 29 Jan 2024 19:10:27 -0500 Subject: [PATCH 3/7] fix(apigatewayv2): WebSocketAwsIntegration ignores requestParameters and integrationPassThrough behaviors (#28921) https://github.com/aws/aws-cdk/pull/28718 missed adding requestParameters to the WebSocketAwsIntegration. Also added integrationPassThrough behaviors. --- ...efaultTestDeployAssert51A62E2E.assets.json | 19 + ...aultTestDeployAssert51A62E2E.template.json | 36 ++ .../websocket/integ.sqs.js.snapshot/cdk.out | 1 + ...-aws-websocket-sqs-integration.assets.json | 19 + ...ws-websocket-sqs-integration.template.json | 176 +++++++++ .../integ.sqs.js.snapshot/integ.json | 19 + .../integ.sqs.js.snapshot/manifest.json | 149 +++++++ .../websocket/integ.sqs.js.snapshot/tree.json | 371 ++++++++++++++++++ .../test/websocket/integ.sqs.ts | 72 ++++ .../lib/websocket/aws.ts | 14 + .../lib/websocket/integration.ts | 44 +++ 11 files changed, 920 insertions(+) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/apigatewayv2awsintegrationsqsintegtestDefaultTestDeployAssert51A62E2E.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/apigatewayv2awsintegrationsqsintegtestDefaultTestDeployAssert51A62E2E.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/integ-aws-websocket-sqs-integration.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/integ-aws-websocket-sqs-integration.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/tree.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.ts diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/apigatewayv2awsintegrationsqsintegtestDefaultTestDeployAssert51A62E2E.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/apigatewayv2awsintegrationsqsintegtestDefaultTestDeployAssert51A62E2E.assets.json new file mode 100644 index 0000000000000..8807965bba16b --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/apigatewayv2awsintegrationsqsintegtestDefaultTestDeployAssert51A62E2E.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "apigatewayv2awsintegrationsqsintegtestDefaultTestDeployAssert51A62E2E.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/apigatewayv2awsintegrationsqsintegtestDefaultTestDeployAssert51A62E2E.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/apigatewayv2awsintegrationsqsintegtestDefaultTestDeployAssert51A62E2E.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/apigatewayv2awsintegrationsqsintegtestDefaultTestDeployAssert51A62E2E.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/integ-aws-websocket-sqs-integration.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/integ-aws-websocket-sqs-integration.assets.json new file mode 100644 index 0000000000000..8fb1b6982818c --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/integ-aws-websocket-sqs-integration.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "d180cf28b233d302bb39a6b0f591e18057775009ad9c2cf014d786e425cd95f9": { + "source": { + "path": "integ-aws-websocket-sqs-integration.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "d180cf28b233d302bb39a6b0f591e18057775009ad9c2cf014d786e425cd95f9.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/integ-aws-websocket-sqs-integration.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/integ-aws-websocket-sqs-integration.template.json new file mode 100644 index 0000000000000..87040beb21176 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/integ-aws-websocket-sqs-integration.template.json @@ -0,0 +1,176 @@ +{ + "Resources": { + "MessageSQSQueueF7E656B7": { + "Type": "AWS::SQS::Queue", + "Properties": { + "FifoQueue": true, + "QueueName": "MessageSQSQueue.fifo" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "webSocketApi5AB89700": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Description": "Send websocket data to SQS which is then processed by a Lambda 2", + "Name": "webSocketApi", + "ProtocolType": "WEBSOCKET", + "RouteSelectionExpression": "$request.body.action" + } + }, + "webSocketApidefaultRouteSQSSendMessageFC4F9133": { + "Type": "AWS::ApiGatewayV2::Integration", + "Properties": { + "ApiId": { + "Ref": "webSocketApi5AB89700" + }, + "CredentialsArn": { + "Fn::GetAtt": [ + "webSocketApiRoleE85311F3", + "Arn" + ] + }, + "IntegrationMethod": "POST", + "IntegrationType": "AWS", + "IntegrationUri": { + "Fn::Join": [ + "", + [ + "arn:aws:apigateway:", + { + "Ref": "AWS::Region" + }, + ":sqs:path/", + { + "Ref": "AWS::AccountId" + }, + "/", + { + "Fn::GetAtt": [ + "MessageSQSQueueF7E656B7", + "QueueName" + ] + } + ] + ] + }, + "PassthroughBehavior": "NEVER", + "RequestParameters": { + "integration.request.header.Content-Type": "'application/x-www-form-urlencoded'" + }, + "RequestTemplates": { + "$default": "Action=SendMessage&MessageGroupId=$input.path('$.MessageGroupId')&MessageDeduplicationId=$context.requestId&MessageAttribute.1.Name=connectionId&MessageAttribute.1.Value.StringValue=$context.connectionId&MessageAttribute.1.Value.DataType=String&MessageAttribute.2.Name=requestId&MessageAttribute.2.Value.StringValue=$context.requestId&MessageAttribute.2.Value.DataType=String&MessageBody=$input.json('$')" + }, + "TemplateSelectionExpression": "\\$default" + } + }, + "webSocketApidefaultRoute749519EC": { + "Type": "AWS::ApiGatewayV2::Route", + "Properties": { + "ApiId": { + "Ref": "webSocketApi5AB89700" + }, + "AuthorizationType": "NONE", + "RouteKey": "$default", + "Target": { + "Fn::Join": [ + "", + [ + "integrations/", + { + "Ref": "webSocketApidefaultRouteSQSSendMessageFC4F9133" + } + ] + ] + } + } + }, + "DevStage520A913F": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "webSocketApi5AB89700" + }, + "AutoDeploy": true, + "StageName": "dev" + } + }, + "webSocketApiRoleE85311F3": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "apigateway.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "webSocketApiRoleDefaultPolicyF067C420": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sqs:SendMessage", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MessageSQSQueueF7E656B7", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "webSocketApiRoleDefaultPolicyF067C420", + "Roles": [ + { + "Ref": "webSocketApiRoleE85311F3" + } + ] + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/integ.json new file mode 100644 index 0000000000000..78a24199fd08e --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/integ.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "testCases": { + "apigatewayv2-aws-integration-sqs-integ-test/DefaultTest": { + "stacks": [ + "integ-aws-websocket-sqs-integration" + ], + "cdkCommandOptions": { + "deploy": { + "args": { + "rollback": true + } + } + }, + "assertionStack": "apigatewayv2-aws-integration-sqs-integ-test/DefaultTest/DeployAssert", + "assertionStackName": "apigatewayv2awsintegrationsqsintegtestDefaultTestDeployAssert51A62E2E" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/manifest.json new file mode 100644 index 0000000000000..65bd421a8430d --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/manifest.json @@ -0,0 +1,149 @@ +{ + "version": "36.0.0", + "artifacts": { + "integ-aws-websocket-sqs-integration.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "integ-aws-websocket-sqs-integration.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "integ-aws-websocket-sqs-integration": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "integ-aws-websocket-sqs-integration.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/d180cf28b233d302bb39a6b0f591e18057775009ad9c2cf014d786e425cd95f9.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "integ-aws-websocket-sqs-integration.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "integ-aws-websocket-sqs-integration.assets" + ], + "metadata": { + "/integ-aws-websocket-sqs-integration/MessageSQSQueue/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MessageSQSQueueF7E656B7" + } + ], + "/integ-aws-websocket-sqs-integration/webSocketApi/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "webSocketApi5AB89700" + } + ], + "/integ-aws-websocket-sqs-integration/webSocketApi/$default-Route/SQSSendMessage/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "webSocketApidefaultRouteSQSSendMessageFC4F9133" + } + ], + "/integ-aws-websocket-sqs-integration/webSocketApi/$default-Route/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "webSocketApidefaultRoute749519EC" + } + ], + "/integ-aws-websocket-sqs-integration/DevStage/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "DevStage520A913F" + } + ], + "/integ-aws-websocket-sqs-integration/webSocketApiRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "webSocketApiRoleE85311F3" + } + ], + "/integ-aws-websocket-sqs-integration/webSocketApiRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "webSocketApiRoleDefaultPolicyF067C420" + } + ], + "/integ-aws-websocket-sqs-integration/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/integ-aws-websocket-sqs-integration/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "integ-aws-websocket-sqs-integration" + }, + "apigatewayv2awsintegrationsqsintegtestDefaultTestDeployAssert51A62E2E.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "apigatewayv2awsintegrationsqsintegtestDefaultTestDeployAssert51A62E2E.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "apigatewayv2awsintegrationsqsintegtestDefaultTestDeployAssert51A62E2E": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "apigatewayv2awsintegrationsqsintegtestDefaultTestDeployAssert51A62E2E.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "apigatewayv2awsintegrationsqsintegtestDefaultTestDeployAssert51A62E2E.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "apigatewayv2awsintegrationsqsintegtestDefaultTestDeployAssert51A62E2E.assets" + ], + "metadata": { + "/apigatewayv2-aws-integration-sqs-integ-test/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/apigatewayv2-aws-integration-sqs-integ-test/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "apigatewayv2-aws-integration-sqs-integ-test/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/tree.json new file mode 100644 index 0000000000000..07ff51e9701b3 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.js.snapshot/tree.json @@ -0,0 +1,371 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "integ-aws-websocket-sqs-integration": { + "id": "integ-aws-websocket-sqs-integration", + "path": "integ-aws-websocket-sqs-integration", + "children": { + "MessageSQSQueue": { + "id": "MessageSQSQueue", + "path": "integ-aws-websocket-sqs-integration/MessageSQSQueue", + "children": { + "Resource": { + "id": "Resource", + "path": "integ-aws-websocket-sqs-integration/MessageSQSQueue/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SQS::Queue", + "aws:cdk:cloudformation:props": { + "fifoQueue": true, + "queueName": "MessageSQSQueue.fifo" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_sqs.CfnQueue", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_sqs.Queue", + "version": "0.0.0" + } + }, + "webSocketApi": { + "id": "webSocketApi", + "path": "integ-aws-websocket-sqs-integration/webSocketApi", + "children": { + "Resource": { + "id": "Resource", + "path": "integ-aws-websocket-sqs-integration/webSocketApi/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ApiGatewayV2::Api", + "aws:cdk:cloudformation:props": { + "description": "Send websocket data to SQS which is then processed by a Lambda 2", + "name": "webSocketApi", + "protocolType": "WEBSOCKET", + "routeSelectionExpression": "$request.body.action" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_apigatewayv2.CfnApi", + "version": "0.0.0" + } + }, + "$default-Route": { + "id": "$default-Route", + "path": "integ-aws-websocket-sqs-integration/webSocketApi/$default-Route", + "children": { + "SQSSendMessage": { + "id": "SQSSendMessage", + "path": "integ-aws-websocket-sqs-integration/webSocketApi/$default-Route/SQSSendMessage", + "children": { + "Resource": { + "id": "Resource", + "path": "integ-aws-websocket-sqs-integration/webSocketApi/$default-Route/SQSSendMessage/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ApiGatewayV2::Integration", + "aws:cdk:cloudformation:props": { + "apiId": { + "Ref": "webSocketApi5AB89700" + }, + "credentialsArn": { + "Fn::GetAtt": [ + "webSocketApiRoleE85311F3", + "Arn" + ] + }, + "integrationMethod": "POST", + "integrationType": "AWS", + "integrationUri": { + "Fn::Join": [ + "", + [ + "arn:aws:apigateway:", + { + "Ref": "AWS::Region" + }, + ":sqs:path/", + { + "Ref": "AWS::AccountId" + }, + "/", + { + "Fn::GetAtt": [ + "MessageSQSQueueF7E656B7", + "QueueName" + ] + } + ] + ] + }, + "passthroughBehavior": "NEVER", + "requestParameters": { + "integration.request.header.Content-Type": "'application/x-www-form-urlencoded'" + }, + "requestTemplates": { + "$default": "Action=SendMessage&MessageGroupId=$input.path('$.MessageGroupId')&MessageDeduplicationId=$context.requestId&MessageAttribute.1.Name=connectionId&MessageAttribute.1.Value.StringValue=$context.connectionId&MessageAttribute.1.Value.DataType=String&MessageAttribute.2.Name=requestId&MessageAttribute.2.Value.StringValue=$context.requestId&MessageAttribute.2.Value.DataType=String&MessageBody=$input.json('$')" + }, + "templateSelectionExpression": "\\$default" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_apigatewayv2.CfnIntegration", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_apigatewayv2.WebSocketIntegration", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "integ-aws-websocket-sqs-integration/webSocketApi/$default-Route/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ApiGatewayV2::Route", + "aws:cdk:cloudformation:props": { + "apiId": { + "Ref": "webSocketApi5AB89700" + }, + "authorizationType": "NONE", + "routeKey": "$default", + "target": { + "Fn::Join": [ + "", + [ + "integrations/", + { + "Ref": "webSocketApidefaultRouteSQSSendMessageFC4F9133" + } + ] + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_apigatewayv2.CfnRoute", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_apigatewayv2.WebSocketRoute", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_apigatewayv2.WebSocketApi", + "version": "0.0.0" + } + }, + "DevStage": { + "id": "DevStage", + "path": "integ-aws-websocket-sqs-integration/DevStage", + "children": { + "Resource": { + "id": "Resource", + "path": "integ-aws-websocket-sqs-integration/DevStage/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ApiGatewayV2::Stage", + "aws:cdk:cloudformation:props": { + "apiId": { + "Ref": "webSocketApi5AB89700" + }, + "autoDeploy": true, + "stageName": "dev" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_apigatewayv2.CfnStage", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_apigatewayv2.WebSocketStage", + "version": "0.0.0" + } + }, + "webSocketApiRole": { + "id": "webSocketApiRole", + "path": "integ-aws-websocket-sqs-integration/webSocketApiRole", + "children": { + "ImportwebSocketApiRole": { + "id": "ImportwebSocketApiRole", + "path": "integ-aws-websocket-sqs-integration/webSocketApiRole/ImportwebSocketApiRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "integ-aws-websocket-sqs-integration/webSocketApiRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "apigateway.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "integ-aws-websocket-sqs-integration/webSocketApiRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "integ-aws-websocket-sqs-integration/webSocketApiRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": "sqs:SendMessage", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MessageSQSQueueF7E656B7", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "webSocketApiRoleDefaultPolicyF067C420", + "roles": [ + { + "Ref": "webSocketApiRoleE85311F3" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "integ-aws-websocket-sqs-integration/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "integ-aws-websocket-sqs-integration/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "apigatewayv2-aws-integration-sqs-integ-test": { + "id": "apigatewayv2-aws-integration-sqs-integ-test", + "path": "apigatewayv2-aws-integration-sqs-integ-test", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "apigatewayv2-aws-integration-sqs-integ-test/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "apigatewayv2-aws-integration-sqs-integ-test/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "apigatewayv2-aws-integration-sqs-integ-test/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "apigatewayv2-aws-integration-sqs-integ-test/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "apigatewayv2-aws-integration-sqs-integ-test/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.ts new file mode 100644 index 0000000000000..49d430bf4ebe2 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.sqs.ts @@ -0,0 +1,72 @@ +import { HttpMethod, PassthroughBehavior, WebSocketApi, WebSocketStage } from 'aws-cdk-lib/aws-apigatewayv2'; +import * as sqs from 'aws-cdk-lib/aws-sqs'; +import * as iam from 'aws-cdk-lib/aws-iam'; +import { App, Stack, Aws } from 'aws-cdk-lib'; +import { WebSocketAwsIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; + +/* + * Stack verification steps: + * 1. Verify manually that the integration has type "AWS" + */ + +const app = new App(); +const stack = new Stack(app, 'integ-aws-websocket-sqs-integration'); + +const sqsMessageQueue = new sqs.Queue(stack, 'MessageSQSQueue', { + fifo: true, + queueName: 'MessageSQSQueue.fifo', +}); + +// API Gateway WebSocket API +const webSocketApi = new WebSocketApi(stack, 'webSocketApi', { + description: 'Send websocket data to SQS which is then processed by a Lambda 2', + routeSelectionExpression: '$request.body.action', +}); + +// Optionally, create a WebSocket stage +new WebSocketStage(stack, 'DevStage', { + webSocketApi: webSocketApi, + stageName: 'dev', + autoDeploy: true, +}); + +// IAM Role for API Gateway +const webSocketApiRole = new iam.Role(stack, 'webSocketApiRole', { + assumedBy: new iam.ServicePrincipal('apigateway.amazonaws.com'), +}); + +webSocketApiRole.addToPolicy( + new iam.PolicyStatement({ + actions: ['sqs:SendMessage'], + effect: iam.Effect.ALLOW, + resources: [sqsMessageQueue.queueArn], + }), +); + +webSocketApi.addRoute('$default', { + integration: new WebSocketAwsIntegration('SQSSendMessage', { + integrationUri: `arn:aws:apigateway:${Aws.REGION}:sqs:path/${Aws.ACCOUNT_ID}/${sqsMessageQueue.queueName}`, + integrationMethod: HttpMethod.POST, + credentialsRole: webSocketApiRole, + passthroughBehavior: PassthroughBehavior.NEVER, + templateSelectionExpression: '\\$default', + requestTemplates: { + $default: 'Action=SendMessage&MessageGroupId=$input.path(\'$.MessageGroupId\')&MessageDeduplicationId=$context.requestId&MessageAttribute.1.Name=connectionId&MessageAttribute.1.Value.StringValue=$context.connectionId&MessageAttribute.1.Value.DataType=String&MessageAttribute.2.Name=requestId&MessageAttribute.2.Value.StringValue=$context.requestId&MessageAttribute.2.Value.DataType=String&MessageBody=$input.json(\'$\')', + }, + requestParameters: { + 'integration.request.header.Content-Type': '\'application/x-www-form-urlencoded\'', + }, + }), +}); + +new IntegTest(app, 'apigatewayv2-aws-integration-sqs-integ-test', { + testCases: [stack], + cdkCommandOptions: { + deploy: { + args: { + rollback: true, + }, + }, + }, +}); diff --git a/packages/aws-cdk-lib/aws-apigatewayv2-integrations/lib/websocket/aws.ts b/packages/aws-cdk-lib/aws-apigatewayv2-integrations/lib/websocket/aws.ts index 6d7654480b8b4..33ce2bbd0c033 100644 --- a/packages/aws-cdk-lib/aws-apigatewayv2-integrations/lib/websocket/aws.ts +++ b/packages/aws-cdk-lib/aws-apigatewayv2-integrations/lib/websocket/aws.ts @@ -3,6 +3,7 @@ import { WebSocketIntegrationType, WebSocketRouteIntegrationConfig, WebSocketRouteIntegrationBindOptions, + PassthroughBehavior, } from '../../../aws-apigatewayv2'; import { IRole } from '../../../aws-iam'; @@ -54,6 +55,17 @@ export interface WebSocketAwsIntegrationProps { * @default - No template selection expression provided. */ readonly templateSelectionExpression?: string; + + /** + * Specifies the pass-through behavior for incoming requests based on the + * Content-Type header in the request, and the available mapping templates + * specified as the requestTemplates property on the Integration resource. + * There are three valid values: WHEN_NO_MATCH, WHEN_NO_TEMPLATES, and + * NEVER. + * + * @default - No passthrough behavior required. + */ + readonly passthroughBehavior?: PassthroughBehavior } /** @@ -73,7 +85,9 @@ export class WebSocketAwsIntegration extends WebSocketRouteIntegration { uri: this.props.integrationUri, method: this.props.integrationMethod, credentialsRole: this.props.credentialsRole, + requestParameters: this.props.requestParameters, requestTemplates: this.props.requestTemplates, + passthroughBehavior: this.props.passthroughBehavior, templateSelectionExpression: this.props.templateSelectionExpression, }; } diff --git a/packages/aws-cdk-lib/aws-apigatewayv2/lib/websocket/integration.ts b/packages/aws-cdk-lib/aws-apigatewayv2/lib/websocket/integration.ts index 0eeeeaa97a927..30e9d6b51e9c2 100644 --- a/packages/aws-cdk-lib/aws-apigatewayv2/lib/websocket/integration.ts +++ b/packages/aws-cdk-lib/aws-apigatewayv2/lib/websocket/integration.ts @@ -32,6 +32,30 @@ export enum WebSocketIntegrationType { AWS = 'AWS', } +/** + * Integration Passthrough Behavior + */ +export enum PassthroughBehavior { + /** + * Passes the request body for unmapped content types through to the + * integration back end without transformation. + */ + WHEN_NO_MATCH = 'WHEN_NO_MATCH', + + /** + * Rejects unmapped content types with an HTTP 415 'Unsupported Media Type' + * response + */ + NEVER = 'NEVER', + + /** + * Allows pass-through when the integration has NO content types mapped to + * templates. However if there is at least one content type defined, + * unmapped content types will be rejected with the same 415 response. + */ + WHEN_NO_TEMPLATES = 'WHEN_NO_TEMPLATES' +} + /** * The integration properties */ @@ -92,6 +116,17 @@ export interface WebSocketIntegrationProps { * @default - No template selection expression required. */ readonly templateSelectionExpression?: string; + + /** + * Specifies the pass-through behavior for incoming requests based on the + * Content-Type header in the request, and the available mapping templates + * specified as the requestTemplates property on the Integration resource. + * There are three valid values: WHEN_NO_MATCH, WHEN_NO_TEMPLATES, and + * NEVER. + * + * @default - No passthrough behavior required. + */ + readonly passthroughBehavior?: PassthroughBehavior } /** @@ -112,6 +147,7 @@ export class WebSocketIntegration extends Resource implements IWebSocketIntegrat credentialsArn: props.credentialsRole?.roleArn, requestParameters: props.requestParameters, requestTemplates: props.requestTemplates, + passthroughBehavior: props.passthroughBehavior, templateSelectionExpression: props.templateSelectionExpression, }); this.integrationId = integ.ref; @@ -168,6 +204,7 @@ export abstract class WebSocketRouteIntegration { credentialsRole: config.credentialsRole, requestTemplates: config.requestTemplates, requestParameters: config.requestParameters, + passthroughBehavior: config.passthroughBehavior, templateSelectionExpression: config.templateSelectionExpression, }); } @@ -229,4 +266,11 @@ export interface WebSocketRouteIntegrationConfig { * @default - No template selection expression. */ readonly templateSelectionExpression?: string; + + /** + * Integration passthrough behaviors. + * + * @default - No pass through bahavior. + */ + readonly passthroughBehavior?: PassthroughBehavior; } From 56794fc8a0f1f5d3c1ab29e3ee0a46b138895d32 Mon Sep 17 00:00:00 2001 From: kazuho cryer-shinozuka Date: Tue, 30 Jan 2024 09:37:50 +0900 Subject: [PATCH 4/7] fix(neptune-alpha): multiple `cloudwatchLogsExports` cannot be set when configuring log retention (#28643) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR includes a breaking change to the aws-neptune-alpha module. I have resolved an issue where it was not possible to set multiple `cloudwatchLogsExports` when configuring log retention for a `DatabaseCluster`. The root cause of the problem was that the LogRetention creation included `[object object]` in the construct's ID. With the current fix, the given `logType` can now be correctly included in the `LogRetention`. ```diff - new logs.LogRetention(this, `${logType}LogRetention`, {..} // "DatabaseobjectObjectLogRetentionA247AF0C" + new logs.LogRetention(this, `${logType.value}LogRetention`, {..} // "DatabaseauditLogRetention8A29F5CC" ``` BREAKING CHANGE: Corrected LogRetention IDs for DatabaseCluster. Previously, regardless of the log type, the string ‘objectObject’ was always included, but after the correction, the log type is now included. Closes #26295. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-neptune-alpha/lib/cluster.ts | 2 +- .../aws-neptune-alpha/test/cluster.test.ts | 49 ++++ ...efaultTestDeployAssert6A1BBA9D.assets.json | 2 +- .../index.d.ts | 14 -- .../index.js | 192 --------------- .../index.ts | 229 ------------------ .../index.js | 1 + .../aws-cdk-neptune-integ.assets.json | 12 +- .../aws-cdk-neptune-integ.template.json | 7 +- .../test/integ.cluster.js.snapshot/cdk.out | 2 +- .../test/integ.cluster.js.snapshot/integ.json | 2 +- .../integ.cluster.js.snapshot/manifest.json | 10 +- .../test/integ.cluster.js.snapshot/tree.json | 17 +- .../aws-neptune-alpha/test/integ.cluster.ts | 6 +- 14 files changed, 83 insertions(+), 462 deletions(-) delete mode 100644 packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/asset.a8515c042d9c942705087943220417be929ac44f968d8fcef2681681b400c0c0/index.d.ts delete mode 100644 packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/asset.a8515c042d9c942705087943220417be929ac44f968d8fcef2681681b400c0c0/index.js delete mode 100644 packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/asset.a8515c042d9c942705087943220417be929ac44f968d8fcef2681681b400c0c0/index.ts create mode 100644 packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/asset.e4afb15788ec44ed9ff3377e1d131ba2768d7b2e2931bc000d1f2005879b3035/index.js diff --git a/packages/@aws-cdk/aws-neptune-alpha/lib/cluster.ts b/packages/@aws-cdk/aws-neptune-alpha/lib/cluster.ts index 39d5db9cdbb36..8b3cce4ef5aff 100644 --- a/packages/@aws-cdk/aws-neptune-alpha/lib/cluster.ts +++ b/packages/@aws-cdk/aws-neptune-alpha/lib/cluster.ts @@ -633,7 +633,7 @@ export class DatabaseCluster extends DatabaseClusterBase implements IDatabaseClu const retention = props.cloudwatchLogsRetention; if (retention) { props.cloudwatchLogsExports?.forEach(logType => { - new logs.LogRetention(this, `${logType}LogRetention`, { + new logs.LogRetention(this, `${logType.value}LogRetention`, { logGroupName: `/aws/neptune/${this.clusterIdentifier}/${logType.value}`, role: props.cloudwatchLogsRetentionRole, retention, diff --git a/packages/@aws-cdk/aws-neptune-alpha/test/cluster.test.ts b/packages/@aws-cdk/aws-neptune-alpha/test/cluster.test.ts index 5c1d012f470d6..dc9de61ce3768 100644 --- a/packages/@aws-cdk/aws-neptune-alpha/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-neptune-alpha/test/cluster.test.ts @@ -830,6 +830,55 @@ describe('DatabaseCluster', () => { }); }).toThrow(/ServerlessScalingConfiguration minCapacity 10 must be less than serverlessScalingConfiguration maxCapacity 5/); }); + + test('cloudwatchLogsExports log retention is enabled when configured for multiple logs exports', () => { + // GIVEN + const stack = testStack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + + // WHEN + new DatabaseCluster(stack, 'Cluster', { + vpc, + instanceType: InstanceType.R5_LARGE, + cloudwatchLogsExports: [LogType.AUDIT, new LogType('slowquery')], + cloudwatchLogsRetention: logs.RetentionDays.ONE_MONTH, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Neptune::DBCluster', { + EnableCloudwatchLogsExports: ['audit', 'slowquery'], + }); + Template.fromStack(stack).hasResourceProperties('Custom::LogRetention', { + LogGroupName: { + 'Fn::Join': [ + '', + [ + '/aws/neptune/', + { + Ref: 'ClusterEB0386A7', + }, + '/audit', + ], + ], + }, + RetentionInDays: 30, + }); + Template.fromStack(stack).hasResourceProperties('Custom::LogRetention', { + LogGroupName: { + 'Fn::Join': [ + '', + [ + '/aws/neptune/', + { + Ref: 'ClusterEB0386A7', + }, + '/slowquery', + ], + ], + }, + RetentionInDays: 30, + }); + }); }); function testStack() { diff --git a/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/ClusterTestDefaultTestDeployAssert6A1BBA9D.assets.json b/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/ClusterTestDefaultTestDeployAssert6A1BBA9D.assets.json index 5ddb3aeafc6f7..1a49d2e15b492 100644 --- a/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/ClusterTestDefaultTestDeployAssert6A1BBA9D.assets.json +++ b/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/ClusterTestDefaultTestDeployAssert6A1BBA9D.assets.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "36.0.0", "files": { "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { "source": { diff --git a/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/asset.a8515c042d9c942705087943220417be929ac44f968d8fcef2681681b400c0c0/index.d.ts b/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/asset.a8515c042d9c942705087943220417be929ac44f968d8fcef2681681b400c0c0/index.d.ts deleted file mode 100644 index 60193f14b4d6d..0000000000000 --- a/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/asset.a8515c042d9c942705087943220417be929ac44f968d8fcef2681681b400c0c0/index.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -interface LogRetentionEvent extends Omit { - ResourceProperties: { - ServiceToken: string; - LogGroupName: string; - LogGroupRegion?: string; - RetentionInDays?: string; - SdkRetry?: { - maxRetries?: string; - }; - RemovalPolicy?: string; - }; -} -export declare function handler(event: LogRetentionEvent, context: AWSLambda.Context): Promise; -export {}; diff --git a/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/asset.a8515c042d9c942705087943220417be929ac44f968d8fcef2681681b400c0c0/index.js b/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/asset.a8515c042d9c942705087943220417be929ac44f968d8fcef2681681b400c0c0/index.js deleted file mode 100644 index 8d4f5cf13f88d..0000000000000 --- a/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/asset.a8515c042d9c942705087943220417be929ac44f968d8fcef2681681b400c0c0/index.js +++ /dev/null @@ -1,192 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.handler = void 0; -/* eslint-disable no-console */ -// eslint-disable-next-line import/no-extraneous-dependencies -const Logs = require("@aws-sdk/client-cloudwatch-logs"); -/** - * Creates a log group and doesn't throw if it exists. - */ -async function createLogGroupSafe(logGroupName, client, withDelay) { - await withDelay(async () => { - try { - const params = { logGroupName }; - const command = new Logs.CreateLogGroupCommand(params); - await client.send(command); - } - catch (error) { - if (error instanceof Logs.ResourceAlreadyExistsException || error.name === 'ResourceAlreadyExistsException') { - // The log group is already created by the lambda execution - return; - } - throw error; - } - }); -} -/** - * Deletes a log group and doesn't throw if it does not exist. - */ -async function deleteLogGroup(logGroupName, client, withDelay) { - await withDelay(async () => { - try { - const params = { logGroupName }; - const command = new Logs.DeleteLogGroupCommand(params); - await client.send(command); - } - catch (error) { - if (error instanceof Logs.ResourceNotFoundException || error.name === 'ResourceNotFoundException') { - // The log group doesn't exist - return; - } - throw error; - } - }); -} -/** - * Puts or deletes a retention policy on a log group. - */ -async function setRetentionPolicy(logGroupName, client, withDelay, retentionInDays) { - await withDelay(async () => { - if (!retentionInDays) { - const params = { logGroupName }; - const deleteCommand = new Logs.DeleteRetentionPolicyCommand(params); - await client.send(deleteCommand); - } - else { - const params = { logGroupName, retentionInDays }; - const putCommand = new Logs.PutRetentionPolicyCommand(params); - await client.send(putCommand); - } - }); -} -async function handler(event, context) { - try { - console.log(JSON.stringify({ ...event, ResponseURL: '...' })); - // The target log group - const logGroupName = event.ResourceProperties.LogGroupName; - // The region of the target log group - const logGroupRegion = event.ResourceProperties.LogGroupRegion; - // Parse to AWS SDK retry options - const maxRetries = parseIntOptional(event.ResourceProperties.SdkRetry?.maxRetries) ?? 5; - const withDelay = makeWithDelay(maxRetries); - const sdkConfig = { - logger: console, - region: logGroupRegion, - maxAttempts: Math.max(5, maxRetries), // Use a minimum for SDK level retries, because it might include retryable failures that withDelay isn't checking for - }; - const client = new Logs.CloudWatchLogsClient(sdkConfig); - if (event.RequestType === 'Create' || event.RequestType === 'Update') { - // Act on the target log group - await createLogGroupSafe(logGroupName, client, withDelay); - await setRetentionPolicy(logGroupName, client, withDelay, parseIntOptional(event.ResourceProperties.RetentionInDays)); - // Configure the Log Group for the Custom Resource function itself - if (event.RequestType === 'Create') { - const clientForCustomResourceFunction = new Logs.CloudWatchLogsClient({ - logger: console, - region: process.env.AWS_REGION, - }); - // Set a retention policy of 1 day on the logs of this very function. - // Due to the async nature of the log group creation, the log group for this function might - // still be not created yet at this point. Therefore we attempt to create it. - // In case it is being created, createLogGroupSafe will handle the conflict. - await createLogGroupSafe(`/aws/lambda/${context.functionName}`, clientForCustomResourceFunction, withDelay); - // If createLogGroupSafe fails, the log group is not created even after multiple attempts. - // In this case we have nothing to set the retention policy on but an exception will skip - // the next line. - await setRetentionPolicy(`/aws/lambda/${context.functionName}`, clientForCustomResourceFunction, withDelay, 1); - } - } - // When the requestType is delete, delete the log group if the removal policy is delete - if (event.RequestType === 'Delete' && event.ResourceProperties.RemovalPolicy === 'destroy') { - await deleteLogGroup(logGroupName, client, withDelay); - // else retain the log group - } - await respond('SUCCESS', 'OK', logGroupName); - } - catch (e) { - console.log(e); - await respond('FAILED', e.message, event.ResourceProperties.LogGroupName); - } - function respond(responseStatus, reason, physicalResourceId) { - const responseBody = JSON.stringify({ - Status: responseStatus, - Reason: reason, - PhysicalResourceId: physicalResourceId, - StackId: event.StackId, - RequestId: event.RequestId, - LogicalResourceId: event.LogicalResourceId, - Data: { - // Add log group name as part of the response so that it's available via Fn::GetAtt - LogGroupName: event.ResourceProperties.LogGroupName, - }, - }); - console.log('Responding', responseBody); - // eslint-disable-next-line @typescript-eslint/no-require-imports - const parsedUrl = require('url').parse(event.ResponseURL); - const requestOptions = { - hostname: parsedUrl.hostname, - path: parsedUrl.path, - method: 'PUT', - headers: { - 'content-type': '', - 'content-length': Buffer.byteLength(responseBody, 'utf8'), - }, - }; - return new Promise((resolve, reject) => { - try { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const request = require('https').request(requestOptions, resolve); - request.on('error', reject); - request.write(responseBody); - request.end(); - } - catch (e) { - reject(e); - } - }); - } -} -exports.handler = handler; -function parseIntOptional(value, base = 10) { - if (value === undefined) { - return undefined; - } - return parseInt(value, base); -} -function makeWithDelay(maxRetries, delayBase = 100, delayCap = 10 * 1000) { - // If we try to update the log group, then due to the async nature of - // Lambda logging there could be a race condition when the same log group is - // already being created by the lambda execution. This can sometime result in - // an error "OperationAbortedException: A conflicting operation is currently - // in progress...Please try again." - // To avoid an error, we do as requested and try again. - return async (block) => { - let attempts = 0; - do { - try { - return await block(); - } - catch (error) { - if (error instanceof Logs.OperationAbortedException - || error.name === 'OperationAbortedException' - || error.name === 'ThrottlingException' // There is no class to check with instanceof, see https://github.com/aws/aws-sdk-js-v3/issues/5140 - ) { - if (attempts < maxRetries) { - attempts++; - await new Promise(resolve => setTimeout(resolve, calculateDelay(attempts, delayBase, delayCap))); - continue; - } - else { - // The log group is still being changed by another execution but we are out of retries - throw new Error('Out of attempts to change log group'); - } - } - throw error; - } - } while (true); // exit happens on retry count check - }; -} -function calculateDelay(attempt, base, cap) { - return Math.round(Math.random() * Math.min(cap, base * 2 ** attempt)); -} -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwrQkFBK0I7QUFDL0IsNkRBQTZEO0FBQzdELHdEQUF3RDtBQWV4RDs7R0FFRztBQUNILEtBQUssVUFBVSxrQkFBa0IsQ0FBQyxZQUFvQixFQUFFLE1BQWlDLEVBQUUsU0FBd0Q7SUFDakosTUFBTSxTQUFTLENBQUMsS0FBSyxJQUFJLEVBQUU7UUFDekIsSUFBSTtZQUNGLE1BQU0sTUFBTSxHQUFHLEVBQUUsWUFBWSxFQUFFLENBQUM7WUFDaEMsTUFBTSxPQUFPLEdBQUcsSUFBSSxJQUFJLENBQUMscUJBQXFCLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDdkQsTUFBTSxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1NBRTVCO1FBQUMsT0FBTyxLQUFVLEVBQUU7WUFDbkIsSUFBSSxLQUFLLFlBQVksSUFBSSxDQUFDLDhCQUE4QixJQUFJLEtBQUssQ0FBQyxJQUFJLEtBQUssZ0NBQWdDLEVBQUU7Z0JBQzNHLDJEQUEyRDtnQkFDM0QsT0FBTzthQUNSO1lBRUQsTUFBTSxLQUFLLENBQUM7U0FDYjtJQUNILENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVEOztHQUVHO0FBQ0gsS0FBSyxVQUFVLGNBQWMsQ0FBQyxZQUFvQixFQUFFLE1BQWlDLEVBQUUsU0FBd0Q7SUFDN0ksTUFBTSxTQUFTLENBQUMsS0FBSyxJQUFJLEVBQUU7UUFDekIsSUFBSTtZQUNGLE1BQU0sTUFBTSxHQUFHLEVBQUUsWUFBWSxFQUFFLENBQUM7WUFDaEMsTUFBTSxPQUFPLEdBQUcsSUFBSSxJQUFJLENBQUMscUJBQXFCLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDdkQsTUFBTSxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1NBRTVCO1FBQUMsT0FBTyxLQUFVLEVBQUU7WUFDbkIsSUFBSSxLQUFLLFlBQVksSUFBSSxDQUFD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LEVBQUUsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxJQUFJLEdBQUcsQ0FBQyxJQUFJLE9BQU8sQ0FBQyxDQUFDLENBQUM7QUFDeEUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qIGVzbGludC1kaXNhYmxlIG5vLWNvbnNvbGUgKi9cbi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBpbXBvcnQvbm8tZXh0cmFuZW91cy1kZXBlbmRlbmNpZXNcbmltcG9ydCAqIGFzIExvZ3MgZnJvbSAnQGF3cy1zZGsvY2xpZW50LWNsb3Vkd2F0Y2gtbG9ncyc7XG5cbmludGVyZmFjZSBMb2dSZXRlbnRpb25FdmVudCBleHRlbmRzIE9taXQ8QVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCwgJ1Jlc291cmNlUHJvcGVydGllcyc+IHtcbiAgUmVzb3VyY2VQcm9wZXJ0aWVzOiB7XG4gICAgU2VydmljZVRva2VuOiBzdHJpbmc7XG4gICAgTG9nR3JvdXBOYW1lOiBzdHJpbmc7XG4gICAgTG9nR3JvdXBSZWdpb24/OiBzdHJpbmc7XG4gICAgUmV0ZW50aW9uSW5EYXlzPzogc3RyaW5nO1xuICAgIFNka1JldHJ5Pzoge1xuICAgICAgbWF4UmV0cmllcz86IHN0cmluZztcbiAgICB9O1xuICAgIFJlbW92YWxQb2xpY3k/OiBzdHJpbmdcbiAgfTtcbn1cblxuLyoqXG4gKiBDcmVhdGVzIGEgbG9nIGdyb3VwIGFuZCBkb2Vzbid0IHRocm93IGlmIGl0IGV4aXN0cy5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gY3JlYXRlTG9nR3JvdXBTYWZlKGxvZ0dyb3VwTmFtZTogc3RyaW5nLCBjbGllbnQ6IExvZ3MuQ2xvdWRXYXRjaExvZ3NDbGllbnQsIHdpdGhEZWxheTogKGJsb2NrOiAoKSA9PiBQcm9taXNlPHZvaWQ+KSA9PiBQcm9taXNlPHZvaWQ+KSB7XG4gIGF3YWl0IHdpdGhEZWxheShhc3luYyAoKSA9PiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHBhcmFtcyA9IHsgbG9nR3JvdXBOYW1lIH07XG4gICAgICBjb25zdCBjb21tYW5kID0gbmV3IExvZ3MuQ3JlYXRlTG9nR3JvdXBDb21tYW5kKHBhcmFtcyk7XG4gICAgICBhd2FpdCBjbGllbnQuc2VuZChjb21tYW5kKTtcblxuICAgIH0gY2F0Y2ggKGVycm9yOiBhbnkpIHtcbiAgICAgIGlmIChlcnJvciBpbnN0YW5jZW9mIExvZ3MuUmVzb3VyY2VBbHJlYWR5RXhpc3RzRXhjZXB0aW9uIHx8IGVycm9yLm5hbWUgPT09ICdSZXNvdXJjZUFscmVhZHlFeGlzdHNFeGNlcHRpb24nKSB7XG4gICAgICAgIC8vIFRoZSBsb2cgZ3JvdXAgaXMgYWxyZWFkeSBjcmVhdGVkIGJ5IHRoZSBsYW1iZGEgZXhlY3V0aW9uXG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgdGhyb3cgZXJyb3I7XG4gICAgfVxuICB9KTtcbn1cblxuLyoqXG4gKiBEZWxldGVzIGEgbG9nIGdyb3VwIGFuZCBkb2Vzbid0IHRocm93IGlmIGl0IGRvZXMgbm90IGV4aXN0LlxuICovXG5hc3luYyBmdW5jdGlvbiBkZWxldGVMb2dHcm91cChsb2dHcm91cE5hbWU6IHN0cmluZywgY2xpZW50OiBMb2dzLkNsb3VkV2F0Y2hMb2dzQ2xpZW50LCB3aXRoRGVsYXk6IChibG9jazogKCkgPT4gUHJvbWlzZTx2b2lkPikgPT4gUHJvbWlzZTx2b2lkPikge1xuICBhd2FpdCB3aXRoRGVsYXkoYXN5bmMgKCkgPT4ge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBwYXJhbXMgPSB7IGxvZ0dyb3VwTmFtZSB9O1xuICAgICAgY29uc3QgY29tbWFuZCA9IG5ldyBMb2dzLkRlbGV0ZUxvZ0dyb3VwQ29tbWFuZChwYXJhbXMpO1xuICAgICAgYXdhaXQgY2xpZW50LnNlbmQoY29tbWFuZCk7XG5cbiAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICBpZiAoZXJyb3IgaW5zdGFuY2VvZiBMb2dzLlJlc291cmNlTm90Rm91bmRFeGNlcHRpb24gfHwgZXJyb3IubmFtZSA9PT0gJ1Jlc291cmNlTm90Rm91bmRFeGNlcHRpb24nKSB7XG4gICAgICAgIC8vIFRoZSBsb2cgZ3JvdXAgZG9lc24ndCBleGlzdFxuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIHRocm93IGVycm9yO1xuICAgIH1cbiAgfSk7XG59XG5cbi8qKlxuICogUHV0cyBvciBkZWxldGVzIGEgcmV0ZW50aW9uIHBvbGljeSBvbiBhIGxvZyBncm91cC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gc2V0UmV0ZW50aW9uUG9saWN5KFxuICBsb2dHcm91cE5hbWU6IHN0cmluZyxcbiAgY2xpZW50OiBMb2dzLkNsb3VkV2F0Y2hMb2dzQ2xpZW50LFxuICB3aXRoRGVsYXk6IChibG9jazogKCkgPT4gUHJvbWlzZTx2b2lkPikgPT4gUHJvbWlzZTx2b2lkPixcbiAgcmV0ZW50aW9uSW5EYXlzPzogbnVtYmVyLFxuKSB7XG5cbiAgYXdhaXQgd2l0aERlbGF5KGFzeW5jICgpID0+IHtcbiAgICBpZiAoIXJldGVudGlvbkluRGF5cykge1xuICAgICAgY29uc3QgcGFyYW1zID0geyBsb2dHcm91cE5hbWUgfTtcbiAgICAgIGNvbnN0IGRlbGV0ZUNvbW1hbmQgPSBuZXcgTG9ncy5EZWxldGVSZXRlbnRpb25Qb2xpY3lDb21tYW5kKHBhcmFtcyk7XG4gICAgICBhd2FpdCBjbGllbnQuc2VuZChkZWxldGVDb21tYW5kKTtcbiAgICB9IGVsc2Uge1xuICAgICAgY29uc3QgcGFyYW1zID0geyBsb2dHcm91cE5hbWUsIHJldGVudGlvbkluRGF5cyB9O1xuICAgICAgY29uc3QgcHV0Q29tbWFuZCA9IG5ldyBMb2dzLlB1dFJldGVudGlvblBvbGljeUNvbW1hbmQocGFyYW1zKTtcbiAgICAgIGF3YWl0IGNsaWVudC5zZW5kKHB1dENvbW1hbmQpO1xuICAgIH1cbiAgfSk7XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBoYW5kbGVyKGV2ZW50OiBMb2dSZXRlbnRpb25FdmVudCwgY29udGV4dDogQVdTTGFtYmRhLkNvbnRleHQpIHtcbiAgdHJ5IHtcbiAgICBjb25zb2xlLmxvZyhKU09OLnN0cmluZ2lmeSh7IC4uLmV2ZW50LCBSZXNwb25zZVVSTDogJy4uLicgfSkpO1xuXG4gICAgLy8gVGhlIHRhcmdldCBsb2cgZ3JvdXBcbiAgICBjb25zdCBsb2dHcm91cE5hbWUgPSBldmVudC5SZXNvdXJjZVByb3BlcnRpZXMuTG9nR3JvdXBOYW1lO1xuXG4gICAgLy8gVGhlIHJlZ2lvbiBvZiB0aGUgdGFyZ2V0IGxvZyBncm91cFxuICAgIGNvbnN0IGxvZ0dyb3VwUmVnaW9uID0gZXZlbnQuUmVzb3VyY2VQcm9wZXJ0aWVzLkxvZ0dyb3VwUmVnaW9uO1xuXG4gICAgLy8gUGFyc2UgdG8gQVdTIFNESyByZXRyeSBvcHRpb25zXG4gICAgY29uc3QgbWF4UmV0cmllcyA9IHBhcnNlSW50T3B0aW9uYWwoZXZlbnQuUmVzb3VyY2VQcm9wZXJ0aWVzLlNka1JldHJ5Py5tYXhSZXRyaWVzKSA/PyA1O1xuICAgIGNvbnN0IHdpdGhEZWxheSA9IG1ha2VXaXRoRGVsYXkobWF4UmV0cmllcyk7XG5cbiAgICBjb25zdCBzZGtDb25maWc6IExvZ3MuQ2xvdWRXYXRjaExvZ3NDbGllbnRDb25maWcgPSB7XG4gICAgICBsb2dnZXI6IGNvbnNvbGUsXG4gICAgICByZWdpb246IGxvZ0dyb3VwUmVnaW9uLFxuICAgICAgbWF4QXR0ZW1wdHM6IE1hdGgubWF4KDUsIG1heFJldHJpZXMpLCAvLyBVc2UgYSBtaW5pbXVtIGZvciBTREsgbGV2ZWwgcmV0cmllcywgYmVjYXVzZSBpdCBtaWdodCBpbmNsdWRlIHJldHJ5YWJsZSBmYWlsdXJlcyB0aGF0IHdpdGhEZWxheSBpc24ndCBjaGVja2luZyBmb3JcbiAgICB9O1xuICAgIGNvbnN0IGNsaWVudCA9IG5ldyBMb2dzLkNsb3VkV2F0Y2hMb2dzQ2xpZW50KHNka0NvbmZpZyk7XG5cbiAgICBpZiAoZXZlbnQuUmVxdWVzdFR5cGUgPT09ICdDcmVhdGUnIHx8IGV2ZW50LlJlcXVlc3RUeXBlID09PSAnVXBkYXRlJykge1xuICAgICAgLy8gQWN0IG9uIHRoZSB0YXJnZXQgbG9nIGdyb3VwXG4gICAgICBhd2FpdCBjcmVhdGVMb2dHcm91cFNhZmUobG9nR3JvdXBOYW1lLCBjbGllbnQsIHdpdGhEZWxheSk7XG4gICAgICBhd2FpdCBzZXRSZXRlbnRpb25Qb2xpY3kobG9nR3JvdXBOYW1lLCBjbGllbnQsIHdpdGhEZWxheSwgcGFyc2VJbnRPcHRpb25hbChldmVudC5SZXNvdXJjZVByb3BlcnRpZXMuUmV0ZW50aW9uSW5EYXlzKSk7XG5cbiAgICAgIC8vIENvbmZpZ3VyZSB0aGUgTG9nIEdyb3VwIGZvciB0aGUgQ3VzdG9tIFJlc291cmNlIGZ1bmN0aW9uIGl0c2VsZlxuICAgICAgaWYgKGV2ZW50LlJlcXVlc3RUeXBlID09PSAnQ3JlYXRlJykge1xuICAgICAgICBjb25zdCBjbGllbnRGb3JDdXN0b21SZXNvdXJjZUZ1bmN0aW9uID0gbmV3IExvZ3MuQ2xvdWRXYXRjaExvZ3NDbGllbnQoe1xuICAgICAgICAgIGxvZ2dlcjogY29uc29sZSxcbiAgICAgICAgICByZWdpb246IHByb2Nlc3MuZW52LkFXU19SRUdJT04sXG4gICAgICAgIH0pO1xuICAgICAgICAvLyBTZXQgYSByZXRlbnRpb24gcG9saWN5IG9mIDEgZGF5IG9uIHRoZSBsb2dzIG9mIHRoaXMgdmVyeSBmdW5jdGlvbi5cbiAgICAgICAgLy8gRHVlIHRvIHRoZSBhc3luYyBuYXR1cmUgb2YgdGhlIGxvZyBncm91cCBjcmVhdGlvbiwgdGhlIGxvZyBncm91cCBmb3IgdGhpcyBmdW5jdGlvbiBtaWdodFxuICAgICAgICAvLyBzdGlsbCBiZSBub3QgY3JlYXRlZCB5ZXQgYXQgdGhpcyBwb2ludC4gVGhlcmVmb3JlIHdlIGF0dGVtcHQgdG8gY3JlYXRlIGl0LlxuICAgICAgICAvLyBJbiBjYXNlIGl0IGlzIGJlaW5nIGNyZWF0ZWQsIGNyZWF0ZUxvZ0dyb3VwU2FmZSB3aWxsIGhhbmRsZSB0aGUgY29uZmxpY3QuXG4gICAgICAgIGF3YWl0IGNyZWF0ZUxvZ0dyb3VwU2FmZShgL2F3cy9sYW1iZGEvJHtjb250ZXh0LmZ1bmN0aW9uTmFtZX1gLCBjbGllbnRGb3JDdXN0b21SZXNvdXJjZUZ1bmN0aW9uLCB3aXRoRGVsYXkpO1xuICAgICAgICAvLyBJZiBjcmVhdGVMb2dHcm91cFNhZmUgZmFpbHMsIHRoZSBsb2cgZ3JvdXAgaXMgbm90IGNyZWF0ZWQgZXZlbiBhZnRlciBtdWx0aXBsZSBhdHRlbXB0cy5cbiAgICAgICAgLy8gSW4gdGhpcyBjYXNlIHdlIGhhdmUgbm90aGluZyB0byBzZXQgdGhlIHJldGVudGlvbiBwb2xpY3kgb24gYnV0IGFuIGV4Y2VwdGlvbiB3aWxsIHNraXBcbiAgICAgICAgLy8gdGhlIG5leHQgbGluZS5cbiAgICAgICAgYXdhaXQgc2V0UmV0ZW50aW9uUG9saWN5KGAvYXdzL2xhbWJkYS8ke2NvbnRleHQuZnVuY3Rpb25OYW1lfWAsIGNsaWVudEZvckN1c3RvbVJlc291cmNlRnVuY3Rpb24sIHdpdGhEZWxheSwgMSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gV2hlbiB0aGUgcmVxdWVzdFR5cGUgaXMgZGVsZXRlLCBkZWxldGUgdGhlIGxvZyBncm91cCBpZiB0aGUgcmVtb3ZhbCBwb2xpY3kgaXMgZGVsZXRlXG4gICAgaWYgKGV2ZW50LlJlcXVlc3RUeXBlID09PSAnRGVsZXRlJyAmJiBldmVudC5SZXNvdXJjZVByb3BlcnRpZXMuUmVtb3ZhbFBvbGljeSA9PT0gJ2Rlc3Ryb3knKSB7XG4gICAgICBhd2FpdCBkZWxldGVMb2dHcm91cChsb2dHcm91cE5hbWUsIGNsaWVudCwgd2l0aERlbGF5KTtcbiAgICAgIC8vIGVsc2UgcmV0YWluIHRoZSBsb2cgZ3JvdXBcbiAgICB9XG5cbiAgICBhd2FpdCByZXNwb25kKCdTVUNDRVNTJywgJ09LJywgbG9nR3JvdXBOYW1lKTtcbiAgfSBjYXRjaCAoZTogYW55KSB7XG4gICAgY29uc29sZS5sb2coZSk7XG4gICAgYXdhaXQgcmVzcG9uZCgnRkFJTEVEJywgZS5tZXNzYWdlLCBldmVudC5SZXNvdXJjZVByb3BlcnRpZXMuTG9nR3JvdXBOYW1lKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIHJlc3BvbmQocmVzcG9uc2VTdGF0dXM6IHN0cmluZywgcmVhc29uOiBzdHJpbmcsIHBoeXNpY2FsUmVzb3VyY2VJZDogc3RyaW5nKSB7XG4gICAgY29uc3QgcmVzcG9uc2VCb2R5ID0gSlNPTi5zdHJpbmdpZnkoe1xuICAgICAgU3RhdHVzOiByZXNwb25zZVN0YXR1cyxcbiAgICAgIFJlYXNvbjogcmVhc29uLFxuICAgICAgUGh5c2ljYWxSZXNvdXJjZUlkOiBwaHlzaWNhbFJlc291cmNlSWQsXG4gICAgICBTdGFja0lkOiBldmVudC5TdGFja0lkLFxuICAgICAgUmVxdWVzdElkOiBldmVudC5SZXF1ZXN0SWQsXG4gICAgICBMb2dpY2FsUmVzb3VyY2VJZDogZXZlbnQuTG9naWNhbFJlc291cmNlSWQsXG4gICAgICBEYXRhOiB7XG4gICAgICAgIC8vIEFkZCBsb2cgZ3JvdXAgbmFtZSBhcyBwYXJ0IG9mIHRoZSByZXNwb25zZSBzbyB0aGF0IGl0J3MgYXZhaWxhYmxlIHZpYSBGbjo6R2V0QXR0XG4gICAgICAgIExvZ0dyb3VwTmFtZTogZXZlbnQuUmVzb3VyY2VQcm9wZXJ0aWVzLkxvZ0dyb3VwTmFtZSxcbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICBjb25zb2xlLmxvZygnUmVzcG9uZGluZycsIHJlc3BvbnNlQm9keSk7XG5cbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXJlcXVpcmUtaW1wb3J0c1xuICAgIGNvbnN0IHBhcnNlZFVybCA9IHJlcXVpcmUoJ3VybCcpLnBhcnNlKGV2ZW50LlJlc3BvbnNlVVJMKTtcbiAgICBjb25zdCByZXF1ZXN0T3B0aW9ucyA9IHtcbiAgICAgIGhvc3RuYW1lOiBwYXJzZWRVcmwuaG9zdG5hbWUsXG4gICAgICBwYXRoOiBwYXJzZWRVcmwucGF0aCxcbiAgICAgIG1ldGhvZDogJ1BVVCcsXG4gICAgICBoZWFkZXJzOiB7XG4gICAgICAgICdjb250ZW50LXR5cGUnOiAnJyxcbiAgICAgICAgJ2NvbnRlbnQtbGVuZ3RoJzogQnVmZmVyLmJ5dGVMZW5ndGgocmVzcG9uc2VCb2R5LCAndXRmOCcpLFxuICAgICAgfSxcbiAgICB9O1xuXG4gICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgIHRyeSB7XG4gICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tcmVxdWlyZS1pbXBvcnRzXG4gICAgICAgIGNvbnN0IHJlcXVlc3QgPSByZXF1aXJlKCdodHRwcycpLnJlcXVlc3QocmVxdWVzdE9wdGlvbnMsIHJlc29sdmUpO1xuICAgICAgICByZXF1ZXN0Lm9uKCdlcnJvcicsIHJlamVjdCk7XG4gICAgICAgIHJlcXVlc3Qud3JpdGUocmVzcG9uc2VCb2R5KTtcbiAgICAgICAgcmVxdWVzdC5lbmQoKTtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgcmVqZWN0KGUpO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG59XG5cbmZ1bmN0aW9uIHBhcnNlSW50T3B0aW9uYWwodmFsdWU/OiBzdHJpbmcsIGJhc2UgPSAxMCk6IG51bWJlciB8IHVuZGVmaW5lZCB7XG4gIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxuXG4gIHJldHVybiBwYXJzZUludCh2YWx1ZSwgYmFzZSk7XG59XG5cbmZ1bmN0aW9uIG1ha2VXaXRoRGVsYXkoXG4gIG1heFJldHJpZXM6IG51bWJlcixcbiAgZGVsYXlCYXNlOiBudW1iZXIgPSAxMDAsXG4gIGRlbGF5Q2FwID0gMTAgKiAxMDAwLCAvLyAxMHNcbik6IChibG9jazogKCkgPT4gUHJvbWlzZTx2b2lkPikgPT4gUHJvbWlzZTx2b2lkPiB7XG4gIC8vIElmIHdlIHRyeSB0byB1cGRhdGUgdGhlIGxvZyBncm91cCwgdGhlbiBkdWUgdG8gdGhlIGFzeW5jIG5hdHVyZSBvZlxuICAvLyBMYW1iZGEgbG9nZ2luZyB0aGVyZSBjb3VsZCBiZSBhIHJhY2UgY29uZGl0aW9uIHdoZW4gdGhlIHNhbWUgbG9nIGdyb3VwIGlzXG4gIC8vIGFscmVhZHkgYmVpbmcgY3JlYXRlZCBieSB0aGUgbGFtYmRhIGV4ZWN1dGlvbi4gVGhpcyBjYW4gc29tZXRpbWUgcmVzdWx0IGluXG4gIC8vIGFuIGVycm9yIFwiT3BlcmF0aW9uQWJvcnRlZEV4Y2VwdGlvbjogQSBjb25mbGljdGluZyBvcGVyYXRpb24gaXMgY3VycmVudGx5XG4gIC8vIGluIHByb2dyZXNzLi4uUGxlYXNlIHRyeSBhZ2Fpbi5cIlxuICAvLyBUbyBhdm9pZCBhbiBlcnJvciwgd2UgZG8gYXMgcmVxdWVzdGVkIGFuZCB0cnkgYWdhaW4uXG5cbiAgcmV0dXJuIGFzeW5jIChibG9jazogKCkgPT4gUHJvbWlzZTx2b2lkPikgPT4ge1xuICAgIGxldCBhdHRlbXB0cyA9IDA7XG4gICAgZG8ge1xuICAgICAgdHJ5IHtcbiAgICAgICAgcmV0dXJuIGF3YWl0IGJsb2NrKCk7XG4gICAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICAgIGlmIChcbiAgICAgICAgICBlcnJvciBpbnN0YW5jZW9mIExvZ3MuT3BlcmF0aW9uQWJvcnRlZEV4Y2VwdGlvblxuICAgICAgICAgIHx8IGVycm9yLm5hbWUgPT09ICdPcGVyYXRpb25BYm9ydGVkRXhjZXB0aW9uJ1xuICAgICAgICAgIHx8IGVycm9yLm5hbWUgPT09ICdUaHJvdHRsaW5nRXhjZXB0aW9uJyAvLyBUaGVyZSBpcyBubyBjbGFzcyB0byBjaGVjayB3aXRoIGluc3RhbmNlb2YsIHNlZSBodHRwczovL2dpdGh1Yi5jb20vYXdzL2F3cy1zZGstanMtdjMvaXNzdWVzLzUxNDBcbiAgICAgICAgKSB7XG4gICAgICAgICAgaWYgKGF0dGVtcHRzIDwgbWF4UmV0cmllcyApIHtcbiAgICAgICAgICAgIGF0dGVtcHRzKys7XG4gICAgICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZShyZXNvbHZlID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgY2FsY3VsYXRlRGVsYXkoYXR0ZW1wdHMsIGRlbGF5QmFzZSwgZGVsYXlDYXApKSk7XG4gICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gVGhlIGxvZyBncm91cCBpcyBzdGlsbCBiZWluZyBjaGFuZ2VkIGJ5IGFub3RoZXIgZXhlY3V0aW9uIGJ1dCB3ZSBhcmUgb3V0IG9mIHJldHJpZXNcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignT3V0IG9mIGF0dGVtcHRzIHRvIGNoYW5nZSBsb2cgZ3JvdXAnKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICB9XG4gICAgfSB3aGlsZSAodHJ1ZSk7IC8vIGV4aXQgaGFwcGVucyBvbiByZXRyeSBjb3VudCBjaGVja1xuICB9O1xufVxuXG5mdW5jdGlvbiBjYWxjdWxhdGVEZWxheShhdHRlbXB0OiBudW1iZXIsIGJhc2U6IG51bWJlciwgY2FwOiBudW1iZXIpOiBudW1iZXIge1xuICByZXR1cm4gTWF0aC5yb3VuZChNYXRoLnJhbmRvbSgpICogTWF0aC5taW4oY2FwLCBiYXNlICogMiAqKiBhdHRlbXB0KSk7XG59XG4iXX0= \ No newline at end of file diff --git a/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/asset.a8515c042d9c942705087943220417be929ac44f968d8fcef2681681b400c0c0/index.ts b/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/asset.a8515c042d9c942705087943220417be929ac44f968d8fcef2681681b400c0c0/index.ts deleted file mode 100644 index 88843fe3fa016..0000000000000 --- a/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/asset.a8515c042d9c942705087943220417be929ac44f968d8fcef2681681b400c0c0/index.ts +++ /dev/null @@ -1,229 +0,0 @@ -/* eslint-disable no-console */ -// eslint-disable-next-line import/no-extraneous-dependencies -import * as Logs from '@aws-sdk/client-cloudwatch-logs'; - -interface LogRetentionEvent extends Omit { - ResourceProperties: { - ServiceToken: string; - LogGroupName: string; - LogGroupRegion?: string; - RetentionInDays?: string; - SdkRetry?: { - maxRetries?: string; - }; - RemovalPolicy?: string - }; -} - -/** - * Creates a log group and doesn't throw if it exists. - */ -async function createLogGroupSafe(logGroupName: string, client: Logs.CloudWatchLogsClient, withDelay: (block: () => Promise) => Promise) { - await withDelay(async () => { - try { - const params = { logGroupName }; - const command = new Logs.CreateLogGroupCommand(params); - await client.send(command); - - } catch (error: any) { - if (error instanceof Logs.ResourceAlreadyExistsException || error.name === 'ResourceAlreadyExistsException') { - // The log group is already created by the lambda execution - return; - } - - throw error; - } - }); -} - -/** - * Deletes a log group and doesn't throw if it does not exist. - */ -async function deleteLogGroup(logGroupName: string, client: Logs.CloudWatchLogsClient, withDelay: (block: () => Promise) => Promise) { - await withDelay(async () => { - try { - const params = { logGroupName }; - const command = new Logs.DeleteLogGroupCommand(params); - await client.send(command); - - } catch (error: any) { - if (error instanceof Logs.ResourceNotFoundException || error.name === 'ResourceNotFoundException') { - // The log group doesn't exist - return; - } - - throw error; - } - }); -} - -/** - * Puts or deletes a retention policy on a log group. - */ -async function setRetentionPolicy( - logGroupName: string, - client: Logs.CloudWatchLogsClient, - withDelay: (block: () => Promise) => Promise, - retentionInDays?: number, -) { - - await withDelay(async () => { - if (!retentionInDays) { - const params = { logGroupName }; - const deleteCommand = new Logs.DeleteRetentionPolicyCommand(params); - await client.send(deleteCommand); - } else { - const params = { logGroupName, retentionInDays }; - const putCommand = new Logs.PutRetentionPolicyCommand(params); - await client.send(putCommand); - } - }); -} - -export async function handler(event: LogRetentionEvent, context: AWSLambda.Context) { - try { - console.log(JSON.stringify({ ...event, ResponseURL: '...' })); - - // The target log group - const logGroupName = event.ResourceProperties.LogGroupName; - - // The region of the target log group - const logGroupRegion = event.ResourceProperties.LogGroupRegion; - - // Parse to AWS SDK retry options - const maxRetries = parseIntOptional(event.ResourceProperties.SdkRetry?.maxRetries) ?? 5; - const withDelay = makeWithDelay(maxRetries); - - const sdkConfig: Logs.CloudWatchLogsClientConfig = { - logger: console, - region: logGroupRegion, - maxAttempts: Math.max(5, maxRetries), // Use a minimum for SDK level retries, because it might include retryable failures that withDelay isn't checking for - }; - const client = new Logs.CloudWatchLogsClient(sdkConfig); - - if (event.RequestType === 'Create' || event.RequestType === 'Update') { - // Act on the target log group - await createLogGroupSafe(logGroupName, client, withDelay); - await setRetentionPolicy(logGroupName, client, withDelay, parseIntOptional(event.ResourceProperties.RetentionInDays)); - - // Configure the Log Group for the Custom Resource function itself - if (event.RequestType === 'Create') { - const clientForCustomResourceFunction = new Logs.CloudWatchLogsClient({ - logger: console, - region: process.env.AWS_REGION, - }); - // Set a retention policy of 1 day on the logs of this very function. - // Due to the async nature of the log group creation, the log group for this function might - // still be not created yet at this point. Therefore we attempt to create it. - // In case it is being created, createLogGroupSafe will handle the conflict. - await createLogGroupSafe(`/aws/lambda/${context.functionName}`, clientForCustomResourceFunction, withDelay); - // If createLogGroupSafe fails, the log group is not created even after multiple attempts. - // In this case we have nothing to set the retention policy on but an exception will skip - // the next line. - await setRetentionPolicy(`/aws/lambda/${context.functionName}`, clientForCustomResourceFunction, withDelay, 1); - } - } - - // When the requestType is delete, delete the log group if the removal policy is delete - if (event.RequestType === 'Delete' && event.ResourceProperties.RemovalPolicy === 'destroy') { - await deleteLogGroup(logGroupName, client, withDelay); - // else retain the log group - } - - await respond('SUCCESS', 'OK', logGroupName); - } catch (e: any) { - console.log(e); - await respond('FAILED', e.message, event.ResourceProperties.LogGroupName); - } - - function respond(responseStatus: string, reason: string, physicalResourceId: string) { - const responseBody = JSON.stringify({ - Status: responseStatus, - Reason: reason, - PhysicalResourceId: physicalResourceId, - StackId: event.StackId, - RequestId: event.RequestId, - LogicalResourceId: event.LogicalResourceId, - Data: { - // Add log group name as part of the response so that it's available via Fn::GetAtt - LogGroupName: event.ResourceProperties.LogGroupName, - }, - }); - - console.log('Responding', responseBody); - - // eslint-disable-next-line @typescript-eslint/no-require-imports - const parsedUrl = require('url').parse(event.ResponseURL); - const requestOptions = { - hostname: parsedUrl.hostname, - path: parsedUrl.path, - method: 'PUT', - headers: { - 'content-type': '', - 'content-length': Buffer.byteLength(responseBody, 'utf8'), - }, - }; - - return new Promise((resolve, reject) => { - try { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const request = require('https').request(requestOptions, resolve); - request.on('error', reject); - request.write(responseBody); - request.end(); - } catch (e) { - reject(e); - } - }); - } -} - -function parseIntOptional(value?: string, base = 10): number | undefined { - if (value === undefined) { - return undefined; - } - - return parseInt(value, base); -} - -function makeWithDelay( - maxRetries: number, - delayBase: number = 100, - delayCap = 10 * 1000, // 10s -): (block: () => Promise) => Promise { - // If we try to update the log group, then due to the async nature of - // Lambda logging there could be a race condition when the same log group is - // already being created by the lambda execution. This can sometime result in - // an error "OperationAbortedException: A conflicting operation is currently - // in progress...Please try again." - // To avoid an error, we do as requested and try again. - - return async (block: () => Promise) => { - let attempts = 0; - do { - try { - return await block(); - } catch (error: any) { - if ( - error instanceof Logs.OperationAbortedException - || error.name === 'OperationAbortedException' - || error.name === 'ThrottlingException' // There is no class to check with instanceof, see https://github.com/aws/aws-sdk-js-v3/issues/5140 - ) { - if (attempts < maxRetries ) { - attempts++; - await new Promise(resolve => setTimeout(resolve, calculateDelay(attempts, delayBase, delayCap))); - continue; - } else { - // The log group is still being changed by another execution but we are out of retries - throw new Error('Out of attempts to change log group'); - } - } - throw error; - } - } while (true); // exit happens on retry count check - }; -} - -function calculateDelay(attempt: number, base: number, cap: number): number { - return Math.round(Math.random() * Math.min(cap, base * 2 ** attempt)); -} diff --git a/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/asset.e4afb15788ec44ed9ff3377e1d131ba2768d7b2e2931bc000d1f2005879b3035/index.js b/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/asset.e4afb15788ec44ed9ff3377e1d131ba2768d7b2e2931bc000d1f2005879b3035/index.js new file mode 100644 index 0000000000000..ae6165a46ea1e --- /dev/null +++ b/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/asset.e4afb15788ec44ed9ff3377e1d131ba2768d7b2e2931bc000d1f2005879b3035/index.js @@ -0,0 +1 @@ +"use strict";var h=Object.create;var d=Object.defineProperty;var w=Object.getOwnPropertyDescriptor;var f=Object.getOwnPropertyNames;var C=Object.getPrototypeOf,P=Object.prototype.hasOwnProperty;var b=(e,o)=>{for(var n in o)d(e,n,{get:o[n],enumerable:!0})},p=(e,o,n,t)=>{if(o&&typeof o=="object"||typeof o=="function")for(let r of f(o))!P.call(e,r)&&r!==n&&d(e,r,{get:()=>o[r],enumerable:!(t=w(o,r))||t.enumerable});return e};var S=(e,o,n)=>(n=e!=null?h(C(e)):{},p(o||!e||!e.__esModule?d(n,"default",{value:e,enumerable:!0}):n,e)),G=e=>p(d({},"__esModule",{value:!0}),e);var q={};b(q,{handler:()=>E});module.exports=G(q);var i=S(require("@aws-sdk/client-cloudwatch-logs"));async function R(e,o,n){await n(async()=>{try{let t={logGroupName:e},r=new i.CreateLogGroupCommand(t);await o.send(r)}catch(t){if(t.name==="ResourceAlreadyExistsException")return;throw t}})}async function x(e,o,n){await n(async()=>{try{let t={logGroupName:e},r=new i.DeleteLogGroupCommand(t);await o.send(r)}catch(t){if(t.name==="ResourceNotFoundException")return;throw t}})}async function y(e,o,n,t){await n(async()=>{if(t){let r={logGroupName:e,retentionInDays:t},s=new i.PutRetentionPolicyCommand(r);await o.send(s)}else{let r={logGroupName:e},s=new i.DeleteRetentionPolicyCommand(r);await o.send(s)}})}async function E(e,o){try{console.log(JSON.stringify({...e,ResponseURL:"..."}));let t=e.ResourceProperties.LogGroupName,r=e.ResourceProperties.LogGroupRegion,s=L(e.ResourceProperties.SdkRetry?.maxRetries)??5,a=I(s),m={logger:console,region:r,maxAttempts:Math.max(5,s)},c=new i.CloudWatchLogsClient(m);if((e.RequestType==="Create"||e.RequestType==="Update")&&(await R(t,c,a),await y(t,c,a,L(e.ResourceProperties.RetentionInDays)),e.RequestType==="Create")){let g=new i.CloudWatchLogsClient({logger:console,region:process.env.AWS_REGION});await R(`/aws/lambda/${o.functionName}`,g,a),await y(`/aws/lambda/${o.functionName}`,g,a,1)}e.RequestType==="Delete"&&e.ResourceProperties.RemovalPolicy==="destroy"&&await x(t,c,a),await n("SUCCESS","OK",t)}catch(t){console.log(t),await n("FAILED",t.message,e.ResourceProperties.LogGroupName)}function n(t,r,s){let a=JSON.stringify({Status:t,Reason:r,PhysicalResourceId:s,StackId:e.StackId,RequestId:e.RequestId,LogicalResourceId:e.LogicalResourceId,Data:{LogGroupName:e.ResourceProperties.LogGroupName}});console.log("Responding",a);let m=require("url").parse(e.ResponseURL),c={hostname:m.hostname,path:m.path,method:"PUT",headers:{"content-type":"","content-length":Buffer.byteLength(a,"utf8")}};return new Promise((g,l)=>{try{let u=require("https").request(c,g);u.on("error",l),u.write(a),u.end()}catch(u){l(u)}})}}function L(e,o=10){if(e!==void 0)return parseInt(e,o)}function I(e,o=100,n=10*1e3){return async t=>{let r=0;do try{return await t()}catch(s){if(s.name==="OperationAbortedException"||s.name==="ThrottlingException")if(rsetTimeout(a,k(r,o,n)));continue}else throw new Error("Out of attempts to change log group");throw s}while(!0)}}function k(e,o,n){return Math.round(Math.random()*Math.min(n,o*2**e))}0&&(module.exports={handler}); diff --git a/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/aws-cdk-neptune-integ.assets.json b/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/aws-cdk-neptune-integ.assets.json index 28615be1e1498..c919416d31418 100644 --- a/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/aws-cdk-neptune-integ.assets.json +++ b/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/aws-cdk-neptune-integ.assets.json @@ -1,20 +1,20 @@ { - "version": "34.0.0", + "version": "36.0.0", "files": { - "a8515c042d9c942705087943220417be929ac44f968d8fcef2681681b400c0c0": { + "e4afb15788ec44ed9ff3377e1d131ba2768d7b2e2931bc000d1f2005879b3035": { "source": { - "path": "asset.a8515c042d9c942705087943220417be929ac44f968d8fcef2681681b400c0c0", + "path": "asset.e4afb15788ec44ed9ff3377e1d131ba2768d7b2e2931bc000d1f2005879b3035", "packaging": "zip" }, "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "a8515c042d9c942705087943220417be929ac44f968d8fcef2681681b400c0c0.zip", + "objectKey": "e4afb15788ec44ed9ff3377e1d131ba2768d7b2e2931bc000d1f2005879b3035.zip", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } }, - "31a87fb5aef089dd280a31f79cc97e52f83ad369806b71883a396db15816a8d4": { + "6f53e0ab5a266567481c2d3d250ae20e1c8dd93706e7aaf5f4ebc4942db6ca62": { "source": { "path": "aws-cdk-neptune-integ.template.json", "packaging": "file" @@ -22,7 +22,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "31a87fb5aef089dd280a31f79cc97e52f83ad369806b71883a396db15816a8d4.json", + "objectKey": "6f53e0ab5a266567481c2d3d250ae20e1c8dd93706e7aaf5f4ebc4942db6ca62.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/aws-cdk-neptune-integ.template.json b/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/aws-cdk-neptune-integ.template.json index edbf8c2936e38..d2ce907e3161b 100644 --- a/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/aws-cdk-neptune-integ.template.json +++ b/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/aws-cdk-neptune-integ.template.json @@ -430,7 +430,7 @@ "Type": "AWS::Neptune::DBClusterParameterGroup", "Properties": { "Description": "A nice parameter group", - "Family": "neptune1", + "Family": "neptune1.2", "Parameters": { "neptune_enable_audit_log": "1", "neptune_query_timeout": "100000" @@ -505,6 +505,7 @@ "EnableCloudwatchLogsExports": [ "audit" ], + "EngineVersion": "1.2.1.0", "KmsKeyId": { "Fn::GetAtt": [ "DbSecurity381C2C15", @@ -524,7 +525,7 @@ "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, - "DatabaseobjectObjectLogRetentionA247AF0C": { + "DatabaseauditLogRetention8A29F5CC": { "Type": "Custom::LogRetention", "Properties": { "ServiceToken": { @@ -631,7 +632,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "a8515c042d9c942705087943220417be929ac44f968d8fcef2681681b400c0c0.zip" + "S3Key": "e4afb15788ec44ed9ff3377e1d131ba2768d7b2e2931bc000d1f2005879b3035.zip" }, "Role": { "Fn::GetAtt": [ diff --git a/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/cdk.out b/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/cdk.out index 2313ab5436501..1f0068d32659a 100644 --- a/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/cdk.out @@ -1 +1 @@ -{"version":"34.0.0"} \ No newline at end of file +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/integ.json b/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/integ.json index e7e257d602124..4d7b253875d40 100644 --- a/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/integ.json +++ b/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "36.0.0", "testCases": { "ClusterTest/DefaultTest": { "stacks": [ diff --git a/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/manifest.json b/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/manifest.json index 6f3e6c0db8318..b643adda4f179 100644 --- a/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "36.0.0", "artifacts": { "aws-cdk-neptune-integ.assets": { "type": "cdk:asset-manifest", @@ -14,10 +14,11 @@ "environment": "aws://unknown-account/unknown-region", "properties": { "templateFile": "aws-cdk-neptune-integ.template.json", + "terminationProtection": false, "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/31a87fb5aef089dd280a31f79cc97e52f83ad369806b71883a396db15816a8d4.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/6f53e0ab5a266567481c2d3d250ae20e1c8dd93706e7aaf5f4ebc4942db6ca62.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -207,10 +208,10 @@ "data": "DatabaseB269D8BB" } ], - "/aws-cdk-neptune-integ/Database/[object Object]LogRetention/Resource": [ + "/aws-cdk-neptune-integ/Database/auditLogRetention/Resource": [ { "type": "aws:cdk:logicalId", - "data": "DatabaseobjectObjectLogRetentionA247AF0C" + "data": "DatabaseauditLogRetention8A29F5CC" } ], "/aws-cdk-neptune-integ/Database/Instance1": [ @@ -271,6 +272,7 @@ "environment": "aws://unknown-account/unknown-region", "properties": { "templateFile": "ClusterTestDefaultTestDeployAssert6A1BBA9D.template.json", + "terminationProtection": false, "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", diff --git a/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/tree.json b/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/tree.json index 6ab4ca12e4bcc..c08d4ad25445c 100644 --- a/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/tree.json +++ b/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.js.snapshot/tree.json @@ -713,7 +713,7 @@ "aws:cdk:cloudformation:type": "AWS::Neptune::DBClusterParameterGroup", "aws:cdk:cloudformation:props": { "description": "A nice parameter group", - "family": "neptune1", + "family": "neptune1.2", "parameters": { "neptune_enable_audit_log": "1", "neptune_query_timeout": "100000" @@ -850,6 +850,7 @@ "enableCloudwatchLogsExports": [ "audit" ], + "engineVersion": "1.2.1.0", "kmsKeyId": { "Fn::GetAtt": [ "DbSecurity381C2C15", @@ -872,13 +873,13 @@ "version": "0.0.0" } }, - "[object Object]LogRetention": { - "id": "[object Object]LogRetention", - "path": "aws-cdk-neptune-integ/Database/[object Object]LogRetention", + "auditLogRetention": { + "id": "auditLogRetention", + "path": "aws-cdk-neptune-integ/Database/auditLogRetention", "children": { "Resource": { "id": "Resource", - "path": "aws-cdk-neptune-integ/Database/[object Object]LogRetention/Resource", + "path": "aws-cdk-neptune-integ/Database/auditLogRetention/Resource", "constructInfo": { "fqn": "aws-cdk-lib.CfnResource", "version": "0.0.0" @@ -1054,7 +1055,7 @@ }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.70" + "version": "10.3.0" } }, "Alarm": { @@ -1130,7 +1131,7 @@ "path": "ClusterTest/DefaultTest/Default", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.70" + "version": "10.3.0" } }, "DeployAssert": { @@ -1176,7 +1177,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.70" + "version": "10.3.0" } } }, diff --git a/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.ts b/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.ts index c7703edeef279..9eb4e088b00d2 100644 --- a/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.ts +++ b/packages/@aws-cdk/aws-neptune-alpha/test/integ.cluster.ts @@ -4,8 +4,8 @@ import * as kms from 'aws-cdk-lib/aws-kms'; import * as logs from 'aws-cdk-lib/aws-logs'; import * as cdk from 'aws-cdk-lib'; import * as integ from '@aws-cdk/integ-tests-alpha'; -import { DatabaseCluster, InstanceType, LogType } from '../lib'; -import { ClusterParameterGroup } from '../lib/parameter-group'; +import { DatabaseCluster, EngineVersion, InstanceType, LogType } from '../lib'; +import { ClusterParameterGroup, ParameterGroupFamily } from '../lib/parameter-group'; /* * Test creating a cluster without specifying engine version. @@ -27,6 +27,7 @@ const kmsKey = new kms.Key(stack, 'DbSecurity', { const clusterParameterGroup = new ClusterParameterGroup(stack, 'Params', { description: 'A nice parameter group', + family: ParameterGroupFamily.NEPTUNE_1_2, parameters: { neptune_enable_audit_log: '1', neptune_query_timeout: '100000', @@ -37,6 +38,7 @@ const cluster = new DatabaseCluster(stack, 'Database', { vpc, vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS }, instanceType: InstanceType.R5_LARGE, + engineVersion: EngineVersion.V1_2_1_0, clusterParameterGroup, kmsKey, removalPolicy: cdk.RemovalPolicy.DESTROY, From ac8251fcdb8b5cc26d917c6d4a95e5eb3ccb2316 Mon Sep 17 00:00:00 2001 From: Xia Zhao <78883180+xazhao@users.noreply.github.com> Date: Mon, 29 Jan 2024 17:10:13 -0800 Subject: [PATCH 5/7] =?UTF-8?q?feat(batch):=20add=20fargate=20Runtime=20Pl?= =?UTF-8?q?atform=20properties=20to=20ECS=20Fargate=20C=E2=80=A6=20(#28841?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The property [RuntimePlatform](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-batch-jobdefinition-containerproperties.html#cfn-batch-jobdefinition-containerproperties-runtimeplatform) is not present in the AWS Batch ECS Fargate Job Definition. This PR adds flatten properties fargateCpuArchitecture and fargateOperatingSystemFamily to the ECS Fargate Job Definition in AWS Batch. Closes #26484. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- ...efaultTestDeployAssertE5BAAC9B.assets.json | 2 +- .../cdk.out | 2 +- .../integ.json | 2 +- .../manifest.json | 11 ++++-- .../stack.assets.json | 6 ++-- .../stack.template.json | 6 +++- .../tree.json | 20 ++++++----- .../test/integ.ecs-job-definition.ts | 2 ++ packages/aws-cdk-lib/aws-batch/README.md | 15 ++++++++ .../aws-batch/lib/ecs-container-definition.ts | 36 +++++++++++++++++++ .../aws-batch/test/ecs-job-definition.test.ts | 21 +++++++++++ 11 files changed, 105 insertions(+), 18 deletions(-) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.js.snapshot/BatchEcsJobDefinitionTestDefaultTestDeployAssertE5BAAC9B.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.js.snapshot/BatchEcsJobDefinitionTestDefaultTestDeployAssertE5BAAC9B.assets.json index e4db2badc242c..0dee86e8f3ad8 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.js.snapshot/BatchEcsJobDefinitionTestDefaultTestDeployAssertE5BAAC9B.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.js.snapshot/BatchEcsJobDefinitionTestDefaultTestDeployAssertE5BAAC9B.assets.json @@ -1,5 +1,5 @@ { - "version": "32.0.0", + "version": "36.0.0", "files": { "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { "source": { diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.js.snapshot/cdk.out index f0b901e7c06e5..1f0068d32659a 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.js.snapshot/cdk.out +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.js.snapshot/cdk.out @@ -1 +1 @@ -{"version":"32.0.0"} \ No newline at end of file +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.js.snapshot/integ.json index 6039a8d046450..15cfdc292a2cf 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.js.snapshot/integ.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.js.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "32.0.0", + "version": "36.0.0", "testCases": { "BatchEcsJobDefinitionTest/DefaultTest": { "stacks": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.js.snapshot/manifest.json index f03b75f4b6728..d4e5db68eb0de 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.js.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "32.0.0", + "version": "36.0.0", "artifacts": { "stack.assets": { "type": "cdk:asset-manifest", @@ -14,10 +14,11 @@ "environment": "aws://unknown-account/unknown-region", "properties": { "templateFile": "stack.template.json", + "terminationProtection": false, "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/44f37496af0bd2235d6a2c7ceed3f438eb5fa8c898a3d4b7773bd7fdb113b176.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/80ca79d29007dc0a645c6568c47e0730a748e22b27eaba47a7336b97d459edcc.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -246,7 +247,10 @@ "/stack/ECSFargateJobDefn/Resource": [ { "type": "aws:cdk:logicalId", - "data": "ECSFargateJobDefn327BE725" + "data": "ECSFargateJobDefn327BE725", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_REPLACE" + ] } ], "/stack/EcsDockerContainer/ExecutionRole/Resource": [ @@ -295,6 +299,7 @@ "environment": "aws://unknown-account/unknown-region", "properties": { "templateFile": "BatchEcsJobDefinitionTestDefaultTestDeployAssertE5BAAC9B.template.json", + "terminationProtection": false, "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.js.snapshot/stack.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.js.snapshot/stack.assets.json index 979f37004cfdc..be9330c4ed096 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.js.snapshot/stack.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.js.snapshot/stack.assets.json @@ -1,7 +1,7 @@ { - "version": "32.0.0", + "version": "36.0.0", "files": { - "44f37496af0bd2235d6a2c7ceed3f438eb5fa8c898a3d4b7773bd7fdb113b176": { + "80ca79d29007dc0a645c6568c47e0730a748e22b27eaba47a7336b97d459edcc": { "source": { "path": "stack.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "44f37496af0bd2235d6a2c7ceed3f438eb5fa8c898a3d4b7773bd7fdb113b176.json", + "objectKey": "80ca79d29007dc0a645c6568c47e0730a748e22b27eaba47a7336b97d459edcc.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.js.snapshot/stack.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.js.snapshot/stack.template.json index 11f2f5d11a342..07857e0e45000 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.js.snapshot/stack.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.js.snapshot/stack.template.json @@ -828,7 +828,11 @@ "Type": "VCPU", "Value": "16" } - ] + ], + "RuntimePlatform": { + "CpuArchitecture": "ARM64", + "OperatingSystemFamily": "LINUX" + } }, "JobDefinitionName": "foofoo", "Parameters": { diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.js.snapshot/tree.json index f1c82c6fd1f5c..a6452a3856934 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.js.snapshot/tree.json @@ -1033,7 +1033,7 @@ } }, "constructInfo": { - "fqn": "@aws-cdk/aws-batch-alpha.EcsEc2ContainerDefinition", + "fqn": "aws-cdk-lib.aws_batch.EcsEc2ContainerDefinition", "version": "0.0.0" } }, @@ -1173,7 +1173,7 @@ } }, "constructInfo": { - "fqn": "@aws-cdk/aws-batch-alpha.EcsJobDefinition", + "fqn": "aws-cdk-lib.aws_batch.EcsJobDefinition", "version": "0.0.0" } }, @@ -1295,7 +1295,7 @@ } }, "constructInfo": { - "fqn": "@aws-cdk/aws-batch-alpha.EcsFargateContainerDefinition", + "fqn": "aws-cdk-lib.aws_batch.EcsFargateContainerDefinition", "version": "0.0.0" } }, @@ -1337,6 +1337,10 @@ }, "networkConfiguration": { "assignPublicIp": "DISABLED" + }, + "runtimePlatform": { + "cpuArchitecture": "ARM64", + "operatingSystemFamily": "LINUX" } }, "jobDefinitionName": "foofoo", @@ -1380,7 +1384,7 @@ } }, "constructInfo": { - "fqn": "@aws-cdk/aws-batch-alpha.EcsJobDefinition", + "fqn": "aws-cdk-lib.aws_batch.EcsJobDefinition", "version": "0.0.0" } }, @@ -1564,7 +1568,7 @@ } }, "constructInfo": { - "fqn": "@aws-cdk/aws-batch-alpha.EcsEc2ContainerDefinition", + "fqn": "aws-cdk-lib.aws_batch.EcsEc2ContainerDefinition", "version": "0.0.0" } }, @@ -1616,7 +1620,7 @@ } }, "constructInfo": { - "fqn": "@aws-cdk/aws-batch-alpha.EcsJobDefinition", + "fqn": "aws-cdk-lib.aws_batch.EcsJobDefinition", "version": "0.0.0" } }, @@ -1655,7 +1659,7 @@ "path": "BatchEcsJobDefinitionTest/DefaultTest/Default", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.55" + "version": "10.3.0" } }, "DeployAssert": { @@ -1701,7 +1705,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.55" + "version": "10.3.0" } } }, diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.ts index e29af1f658043..ec2ac7fdeaec8 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-batch/test/integ.ecs-job-definition.ts @@ -60,6 +60,8 @@ new batch.EcsJobDefinition(stack, 'ECSFargateJobDefn', { memory: Size.mebibytes(32768), ephemeralStorageSize: Size.gibibytes(100), fargatePlatformVersion: FargatePlatformVersion.LATEST, + fargateCpuArchitecture: ecs.CpuArchitecture.ARM64, + fargateOperatingSystemFamily: ecs.OperatingSystemFamily.LINUX, }), jobDefinitionName: 'foofoo', parameters: { diff --git a/packages/aws-cdk-lib/aws-batch/README.md b/packages/aws-cdk-lib/aws-batch/README.md index a579bd0bc2386..1a2acd741c867 100644 --- a/packages/aws-cdk-lib/aws-batch/README.md +++ b/packages/aws-cdk-lib/aws-batch/README.md @@ -479,6 +479,21 @@ jobDefn.container.addVolume(batch.EcsVolume.efs({ })); ``` +### Running an ECS workflow with Fargate container + +```ts +const jobDefn = new batch.EcsJobDefinition(this, 'JobDefn', { + container: new batch.EcsFargateContainerDefinition(this, 'myFargateContainer', { + image: ecs.ContainerImage.fromRegistry('public.ecr.aws/amazonlinux/amazonlinux:latest'), + memory: cdk.Size.mebibytes(2048), + cpu: 256, + ephemeralStorageSize: cdk.Size.gibibytes(100), + fargateCpuArchitecture: ecs.CpuArchitecture.ARM64, + fargateOperatingSystemFamily: ecs.OperatingSystemFamily.LINUX, + }), +}); +``` + ### Secrets You can expose SecretsManager Secret ARNs or SSM Parameters to your container as environment variables. diff --git a/packages/aws-cdk-lib/aws-batch/lib/ecs-container-definition.ts b/packages/aws-cdk-lib/aws-batch/lib/ecs-container-definition.ts index 136adedc77a0d..8d5369d7c88d9 100644 --- a/packages/aws-cdk-lib/aws-batch/lib/ecs-container-definition.ts +++ b/packages/aws-cdk-lib/aws-batch/lib/ecs-container-definition.ts @@ -979,6 +979,20 @@ export interface IEcsFargateContainerDefinition extends IEcsContainerDefinition * @default - 20 GiB */ readonly ephemeralStorageSize?: Size; + + /** + * The vCPU architecture of Fargate Runtime. + * + * @default - X86_64 + */ + readonly fargateCpuArchitecture?: ecs.CpuArchitecture; + + /** + * The operating system for the compute environment. + * + * @default - LINUX + */ + readonly fargateOperatingSystemFamily?: ecs.OperatingSystemFamily; } /** @@ -1009,6 +1023,20 @@ export interface EcsFargateContainerDefinitionProps extends EcsContainerDefiniti * @default - 20 GiB */ readonly ephemeralStorageSize?: Size; + + /** + * The vCPU architecture of Fargate Runtime. + * + * @default - X86_64 + */ + readonly fargateCpuArchitecture?: ecs.CpuArchitecture; + + /** + * The operating system for the compute environment. + * + * @default - LINUX + */ + readonly fargateOperatingSystemFamily?: ecs.OperatingSystemFamily; } /** @@ -1018,12 +1046,16 @@ export class EcsFargateContainerDefinition extends EcsContainerDefinitionBase im public readonly fargatePlatformVersion?: ecs.FargatePlatformVersion; public readonly assignPublicIp?: boolean; public readonly ephemeralStorageSize?: Size; + public readonly fargateCpuArchitecture?: ecs.CpuArchitecture; + public readonly fargateOperatingSystemFamily?: ecs.OperatingSystemFamily; constructor(scope: Construct, id: string, props: EcsFargateContainerDefinitionProps) { super(scope, id, props); this.assignPublicIp = props.assignPublicIp; this.fargatePlatformVersion = props.fargatePlatformVersion; this.ephemeralStorageSize = props.ephemeralStorageSize; + this.fargateCpuArchitecture = props.fargateCpuArchitecture; + this.fargateOperatingSystemFamily = props.fargateOperatingSystemFamily; // validates ephemeralStorageSize is within limits if (props.ephemeralStorageSize) { @@ -1050,6 +1082,10 @@ export class EcsFargateContainerDefinition extends EcsContainerDefinitionBase im networkConfiguration: { assignPublicIp: this.assignPublicIp ? 'ENABLED' : 'DISABLED', }, + runtimePlatform: { + cpuArchitecture: this.fargateCpuArchitecture?._cpuArchitecture, + operatingSystemFamily: this.fargateOperatingSystemFamily?._operatingSystemFamily, + }, }; }; } diff --git a/packages/aws-cdk-lib/aws-batch/test/ecs-job-definition.test.ts b/packages/aws-cdk-lib/aws-batch/test/ecs-job-definition.test.ts index 20b2caadec4d9..9967bb09f688e 100644 --- a/packages/aws-cdk-lib/aws-batch/test/ecs-job-definition.test.ts +++ b/packages/aws-cdk-lib/aws-batch/test/ecs-job-definition.test.ts @@ -63,6 +63,27 @@ test('EcsJobDefinition uses Compatibility.FARGATE for Fargate containers', () => }); }); +test('EcsJobDefinition uses runtimePlatform for Fargate containers', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new EcsJobDefinition(stack, 'ECSJobDefn', { + container: new EcsFargateContainerDefinition(stack, 'EcsContainer', { + cpu: 256, + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + memory: Size.mebibytes(2048), + fargateCpuArchitecture: ecs.CpuArchitecture.ARM64, + fargateOperatingSystemFamily: ecs.OperatingSystemFamily.LINUX, + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Batch::JobDefinition', { + PlatformCapabilities: [Compatibility.FARGATE], + }); +}); + test('can be imported from ARN', () => { // GIVEN const stack = new Stack(); From 41bb1880ace0a41fb69d7071c91c0bfe7032fd1e Mon Sep 17 00:00:00 2001 From: Pahud Hsieh Date: Mon, 29 Jan 2024 20:39:39 -0500 Subject: [PATCH 6/7] chore(step-function-tasks): support additional volume types for emr (#28911) Support additional volume types for `EbsBlockDeviceVolumeType`. Closes https://github.com/aws/aws-cdk/issues/28874 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../lib/emr/emr-create-cluster.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/emr/emr-create-cluster.ts b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/emr/emr-create-cluster.ts index 0f1feb0a096fb..465fe33aba534 100644 --- a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/emr/emr-create-cluster.ts +++ b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/emr/emr-create-cluster.ts @@ -483,9 +483,14 @@ export namespace EmrCreateCluster { /** * EBS Volume Types + * @see https://docs.aws.amazon.com/emr/latest/APIReference/API_VolumeSpecification.html#EMR-Type-VolumeSpecification-VolumeType * */ export enum EbsBlockDeviceVolumeType { + /** + * gp3 Volume Type + */ + GP3 = 'gp3', /** * gp2 Volume Type */ @@ -494,6 +499,14 @@ export namespace EmrCreateCluster { * io1 Volume Type */ IO1 = 'io1', + /** + * st1 Volume Type + */ + ST1 = 'st1', + /** + * sc1 Volume Type + */ + SC1 = 'sc1', /** * Standard Volume Type */ From e9a867a1ba2a3f603d2c27d7384fe9921f26ecfb Mon Sep 17 00:00:00 2001 From: Ayush Shah Date: Tue, 30 Jan 2024 07:41:02 +0530 Subject: [PATCH 7/7] chore(ec2): trn1n instance type (#28914) adding support of trn1n instance type *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/aws-cdk-lib/aws-ec2/lib/instance-types.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/aws-cdk-lib/aws-ec2/lib/instance-types.ts b/packages/aws-cdk-lib/aws-ec2/lib/instance-types.ts index d47b269118efb..935b505a79213 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/instance-types.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/instance-types.ts @@ -616,6 +616,11 @@ export enum InstanceClass { */ TRN1 = 'trn1', + /** + * High performance computing powered by AWS Trainium + */ + TRN1N = 'trn1n', + /** * Storage/compute balanced instances, 1st generation */ @@ -1401,6 +1406,7 @@ export class InstanceType { [InstanceClass.STORAGE3_ENHANCED_NETWORK]: 'd3en', [InstanceClass.D3EN]: 'd3en', [InstanceClass.TRN1]: 'trn1', + [InstanceClass.TRN1N]: 'trn1n', [InstanceClass.STORAGE_COMPUTE_1]: 'h1', [InstanceClass.H1]: 'h1', [InstanceClass.IO3]: 'i3',