From bc10e6ffb6164c212336ada745923e91adb8fe05 Mon Sep 17 00:00:00 2001 From: Roger Chi Date: Tue, 9 Nov 2021 11:56:04 -0500 Subject: [PATCH 01/23] feat(stepfunctions-tasks): Support `DynamoAttributeValue.listFromJsonPath` (#17376) Add `DynamoAttributeValue.listFromJsonPath(value: string)` to allow specifying a list/array from the state input as a parameter in a DynamoDB PutItem or UpdateItem optimized integration in stepfunctions-tasks. Closes #17375 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../lib/dynamodb/shared-types.ts | 10 ++++++++++ .../dynamodb/integ.call-dynamodb.expected.json | 2 +- .../test/dynamodb/integ.call-dynamodb.ts | 1 + .../test/dynamodb/shared-types.test.ts | 16 ++++++++++++++++ 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/dynamodb/shared-types.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/dynamodb/shared-types.ts index c07d1e8fc5e12..0921cf3b2521a 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/dynamodb/shared-types.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/dynamodb/shared-types.ts @@ -219,6 +219,16 @@ export class DynamoAttributeValue { return new DynamoAttributeValue({ L: value.map((val) => val.toObject()) }); } + /** + * Sets an attribute of type List. For example: "L": [ {"S": "Cookies"} , {"S": "Coffee"}, {"S", "Veggies"}] + * + * @param value Json path that specifies state input to be used + */ + public static listFromJsonPath(value: string) { + validateJsonPath(value); + return new DynamoAttributeValue({ L: value }); + } + /** * Sets an attribute of type Null. For example: "NULL": true */ diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/dynamodb/integ.call-dynamodb.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/dynamodb/integ.call-dynamodb.expected.json index 094221d1ba6bb..a8b7510287767 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/dynamodb/integ.call-dynamodb.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/dynamodb/integ.call-dynamodb.expected.json @@ -193,7 +193,7 @@ { "Ref": "AWS::Partition" }, - ":states:::dynamodb:putItem\",\"Parameters\":{\"Item\":{\"MessageId\":{\"S\":\"1234\"},\"Text\":{\"S.$\":\"$.bar\"},\"TotalCount\":{\"N\":\"18\"},\"Activated\":{\"BOOL.$\":\"$.foo\"}},\"TableName\":\"", + ":states:::dynamodb:putItem\",\"Parameters\":{\"Item\":{\"MessageId\":{\"S\":\"1234\"},\"Text\":{\"S.$\":\"$.bar\"},\"TotalCount\":{\"N\":\"18\"},\"Activated\":{\"BOOL.$\":\"$.foo\"},\"List\":{\"L.$\":\"$.list\"}},\"TableName\":\"", { "Ref": "Messages804FA4EB" }, diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/dynamodb/integ.call-dynamodb.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/test/dynamodb/integ.call-dynamodb.ts index e78bba4f6721e..18be323c22db3 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/dynamodb/integ.call-dynamodb.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/dynamodb/integ.call-dynamodb.ts @@ -37,6 +37,7 @@ class CallDynamoDBStack extends cdk.Stack { Text: tasks.DynamoAttributeValue.fromString(sfn.JsonPath.stringAt('$.bar')), TotalCount: tasks.DynamoAttributeValue.fromNumber(firstNumber), Activated: tasks.DynamoAttributeValue.booleanFromJsonPath(sfn.JsonPath.stringAt('$.foo')), + List: tasks.DynamoAttributeValue.listFromJsonPath(sfn.JsonPath.stringAt('$.list')), }, table, }); diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/dynamodb/shared-types.test.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/test/dynamodb/shared-types.test.ts index f0810de3e3949..a882fb9f99b2b 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/dynamodb/shared-types.test.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/dynamodb/shared-types.test.ts @@ -222,6 +222,22 @@ describe('DynamoAttributeValue', () => { }); }); + test('from list with json path', () => { + // GIVEN + const m = '$.path'; + // WHEN + const attribute = tasks.DynamoAttributeValue.listFromJsonPath( + sfn.JsonPath.stringAt(m), + ); + + // THEN + expect(sfn.FieldUtils.renderObject(attribute)).toEqual({ + attributeValue: { + 'L.$': m, + }, + }); + }); + test('from null', () => { // WHEN const attribute = tasks.DynamoAttributeValue.fromNull(true); From 3154a58bfc5ae4b845994c7a0ab45771f5af4cd0 Mon Sep 17 00:00:00 2001 From: JohnLux4 <38677408+eastNine@users.noreply.github.com> Date: Wed, 10 Nov 2021 02:53:31 +0900 Subject: [PATCH 02/23] fix(autoscaling): add timezone property to Scheduled Action (#17330) Add timezone property to ScheduledAction. example: ```typescript import * as asg from '@aws-cdk/aws-autoscaling'; new asg.ScheduledAction(this, 'test', { autoScalingGroup: 'test', desiredCapacity: 1, minCapacity: 1, maxCapacity: 2, schedule: { expressionString: '0 3 * * *' }, timeZone: 'Asia/Seoul' }); ``` ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-autoscaling/lib/scheduled-action.ts | 12 +++++++++++ .../test/scheduled-action.test.ts | 21 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/packages/@aws-cdk/aws-autoscaling/lib/scheduled-action.ts b/packages/@aws-cdk/aws-autoscaling/lib/scheduled-action.ts index 70a269bde9239..fa5d431bcf47b 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/scheduled-action.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/scheduled-action.ts @@ -8,6 +8,17 @@ import { Schedule } from './schedule'; * Properties for a scheduled scaling action */ export interface BasicScheduledActionProps { + /** + * Specifies the time zone for a cron expression. If a time zone is not provided, UTC is used by default. + * + * Valid values are the canonical names of the IANA time zones, derived from the IANA Time Zone Database (such as Etc/GMT+9 or Pacific/Tahiti). + * + * For more information, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. + * + * @default - UTC + * + */ + readonly timeZone?: string; /** * When to perform this action. * @@ -94,6 +105,7 @@ export class ScheduledAction extends Resource { maxSize: props.maxCapacity, desiredCapacity: props.desiredCapacity, recurrence: props.schedule.expressionString, + timeZone: props.timeZone, }); } } diff --git a/packages/@aws-cdk/aws-autoscaling/test/scheduled-action.test.ts b/packages/@aws-cdk/aws-autoscaling/test/scheduled-action.test.ts index 3afbd887b45ae..e7aeb92a81b6d 100644 --- a/packages/@aws-cdk/aws-autoscaling/test/scheduled-action.test.ts +++ b/packages/@aws-cdk/aws-autoscaling/test/scheduled-action.test.ts @@ -46,6 +46,27 @@ describe('scheduled action', () => { }); + test('have timezone property', () => { + // GIVEN + const stack = new cdk.Stack(); + const asg = makeAutoScalingGroup(stack); + + // WHEN + asg.scaleOnSchedule('ScaleOutAtMiddaySeoul', { + schedule: autoscaling.Schedule.cron({ hour: '12', minute: '0' }), + minCapacity: 12, + timeZone: 'Asia/Seoul', + }); + + // THEN + expect(stack).to(haveResource('AWS::AutoScaling::ScheduledAction', { + MinSize: 12, + Recurrence: '0 12 * * *', + TimeZone: 'Asia/Seoul', + })); + + }); + test('autoscaling group has recommended updatepolicy for scheduled actions', () => { // GIVEN const stack = new cdk.Stack(); From bde44e7a767b88762ecb1370e605e6e5dfc85b52 Mon Sep 17 00:00:00 2001 From: Niranjan Jayakar Date: Tue, 9 Nov 2021 18:31:00 +0000 Subject: [PATCH 03/23] feat(assertions): support assertions over nested stacks (#16972) Add support for nested stacks ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- package.json | 4 + packages/@aws-cdk/assertions/NOTICE | 88 ++++++++++++++++++- packages/@aws-cdk/assertions/lib/template.ts | 6 ++ packages/@aws-cdk/assertions/package.json | 7 +- .../@aws-cdk/assertions/test/template.test.ts | 44 ++++++++-- 5 files changed, 138 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 6f8bbee09c04f..aea497b0788f1 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,10 @@ "nohoist": [ "**/jszip", "**/jszip/**", + "@aws-cdk/assertions-alpha/fs-extra", + "@aws-cdk/assertions-alpha/fs-extra/**", + "@aws-cdk/assertions/fs-extra", + "@aws-cdk/assertions/fs-extra/**", "@aws-cdk/aws-amplify-alpha/yaml", "@aws-cdk/aws-amplify-alpha/yaml/**", "@aws-cdk/aws-amplify/yaml", diff --git a/packages/@aws-cdk/assertions/NOTICE b/packages/@aws-cdk/assertions/NOTICE index ac712780ec5ca..9e3fd34dbe209 100644 --- a/packages/@aws-cdk/assertions/NOTICE +++ b/packages/@aws-cdk/assertions/NOTICE @@ -1,2 +1,88 @@ AWS Cloud Development Kit (AWS CDK) -Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. \ No newline at end of file +Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +------------------------------------------------------------------------------- + +The AWS CDK includes the following third-party software/licensing: + +** fs-extra - https://www.npmjs.com/package/fs-extra +Copyright (c) 2011-2017 JP Richardson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files +(the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +---------------- + +** at-least-node - https://www.npmjs.com/package/at-least-node +Copyright (c) 2020 Ryan Zimmerman + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +---------------- + +** graceful-fs - https://www.npmjs.com/package/graceful-fs +Copyright (c) Isaac Z. Schlueter, Ben Noordhuis, and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +---------------- + +** jsonfile - https://www.npmjs.com/package/jsonfile +Copyright (c) 2012-2015, JP Richardson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files +(the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +---------------- + +** universalify - https://www.npmjs.com/package/universalify +Copyright (c) 2017, Ryan Zimmerman + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the 'Software'), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +---------------- \ No newline at end of file diff --git a/packages/@aws-cdk/assertions/lib/template.ts b/packages/@aws-cdk/assertions/lib/template.ts index 01e0d3376dc8c..dfc830cf8d822 100644 --- a/packages/@aws-cdk/assertions/lib/template.ts +++ b/packages/@aws-cdk/assertions/lib/template.ts @@ -1,4 +1,6 @@ +import * as path from 'path'; import { Stack, Stage } from '@aws-cdk/core'; +import * as fs from 'fs-extra'; import { Match } from './match'; import { Matcher } from './matcher'; import { findMappings, hasMapping } from './private/mappings'; @@ -179,5 +181,9 @@ function toTemplate(stack: Stack): any { throw new Error('unexpected: all stacks must be part of a Stage or an App'); } const assembly = root.synth(); + if (stack.nestedStackParent) { + // if this is a nested stack (it has a parent), then just read the template as a string + return JSON.parse(fs.readFileSync(path.join(assembly.directory, stack.templateFile)).toString('utf-8')); + } return assembly.getStackArtifact(stack.artifactId).template; } \ No newline at end of file diff --git a/packages/@aws-cdk/assertions/package.json b/packages/@aws-cdk/assertions/package.json index 2e814d423cacd..35e2842a2bd0c 100644 --- a/packages/@aws-cdk/assertions/package.json +++ b/packages/@aws-cdk/assertions/package.json @@ -64,6 +64,7 @@ "devDependencies": { "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/pkglint": "0.0.0", + "@types/fs-extra": "^9.0.13", "@types/jest": "^27.0.2", "constructs": "^3.3.69", "jest": "^27.3.1", @@ -73,7 +74,8 @@ "@aws-cdk/cloud-assembly-schema": "0.0.0", "@aws-cdk/core": "0.0.0", "@aws-cdk/cx-api": "0.0.0", - "constructs": "^3.3.69" + "constructs": "^3.3.69", + "fs-extra": "^9.1.0" }, "peerDependencies": { "@aws-cdk/cloud-assembly-schema": "0.0.0", @@ -81,6 +83,9 @@ "@aws-cdk/cx-api": "0.0.0", "constructs": "^3.3.69" }, + "bundledDependencies": [ + "fs-extra" + ], "repository": { "url": "https://github.com/aws/aws-cdk.git", "type": "git", diff --git a/packages/@aws-cdk/assertions/test/template.test.ts b/packages/@aws-cdk/assertions/test/template.test.ts index 3384cda21207f..64141edf52e89 100644 --- a/packages/@aws-cdk/assertions/test/template.test.ts +++ b/packages/@aws-cdk/assertions/test/template.test.ts @@ -1,11 +1,10 @@ -import { App, CfnMapping, CfnOutput, CfnResource, Stack } from '@aws-cdk/core'; +import { App, CfnMapping, CfnOutput, CfnResource, NestedStack, Stack } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { Match, Template } from '../lib'; describe('Template', () => { - describe('asObject', () => { - test('fromString', () => { - const template = Template.fromString(`{ + test('fromString', () => { + const template = Template.fromString(`{ "Resources": { "Foo": { "Type": "Baz::Qux", @@ -14,30 +13,57 @@ describe('Template', () => { } }`); + expect(template.toJSON()).toEqual({ + Resources: { + Foo: { + Type: 'Baz::Qux', + Properties: { Fred: 'Waldo' }, + }, + }, + }); + }); + + describe('fromStack', () => { + test('default', () => { + const app = new App({ + context: { + '@aws-cdk/core:newStyleStackSynthesis': false, + }, + }); + const stack = new Stack(app); + new CfnResource(stack, 'Foo', { + type: 'Foo::Bar', + properties: { + Baz: 'Qux', + }, + }); + const template = Template.fromStack(stack); + expect(template.toJSON()).toEqual({ Resources: { Foo: { - Type: 'Baz::Qux', - Properties: { Fred: 'Waldo' }, + Type: 'Foo::Bar', + Properties: { Baz: 'Qux' }, }, }, }); }); - test('fromStack', () => { + test('nested', () => { const app = new App({ context: { '@aws-cdk/core:newStyleStackSynthesis': false, }, }); const stack = new Stack(app); - new CfnResource(stack, 'Foo', { + const nested = new NestedStack(stack, 'MyNestedStack'); + new CfnResource(nested, 'Foo', { type: 'Foo::Bar', properties: { Baz: 'Qux', }, }); - const template = Template.fromStack(stack); + const template = Template.fromStack(nested); expect(template.toJSON()).toEqual({ Resources: { From acd64f4f23840e51ba432934b9d1377c1ed46ef2 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Tue, 9 Nov 2021 20:11:57 +0100 Subject: [PATCH 04/23] docs: re-enable example 'strict' mode where possible (#17421) This will prevent regression in compilation status for the top 25 construct libraries, that we've gone through the effort of making compile. This also gets rid of the `@example` values in most of the construct libraries here. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/assertions/package.json | 2 +- packages/@aws-cdk/aws-apigateway/package.json | 9 +++- .../aws-apigatewayv2-authorizers/package.json | 9 +++- .../aws-apigatewayv2-integrations/README.md | 13 +++--- .../package.json | 9 +++- .../lib/scalable-target.ts | 8 ++-- .../lib/target-tracking-scaling-policy.ts | 2 +- .../aws-applicationautoscaling/package.json | 9 +++- .../aws-appmesh/lib/virtual-service.ts | 2 +- packages/@aws-cdk/aws-appmesh/package.json | 9 +++- packages/@aws-cdk/aws-appsync/package.json | 9 +++- .../@aws-cdk/aws-autoscaling/package.json | 9 +++- packages/@aws-cdk/aws-cloudwatch/package.json | 9 +++- packages/@aws-cdk/aws-codebuild/README.md | 2 +- .../aws-codebuild/lib/file-location.ts | 3 +- packages/@aws-cdk/aws-codebuild/package.json | 9 +++- .../aws-codepipeline-actions/README.md | 2 +- .../aws-codepipeline-actions/package.json | 9 +++- packages/@aws-cdk/aws-dynamodb/package.json | 9 +++- packages/@aws-cdk/aws-ec2/package.json | 9 +++- packages/@aws-cdk/aws-ecr/package.json | 9 +++- .../@aws-cdk/aws-ecs-patterns/package.json | 9 +++- packages/@aws-cdk/aws-ecs/package.json | 9 +++- .../lib/shared/base-load-balancer.ts | 21 ++++++--- .../lib/shared/base-target-group.ts | 2 +- .../aws-elasticloadbalancingv2/package.json | 9 +++- .../@aws-cdk/aws-events-targets/package.json | 9 +++- packages/@aws-cdk/aws-events/package.json | 9 +++- packages/@aws-cdk/aws-iam/package.json | 9 +++- packages/@aws-cdk/aws-kms/package.json | 9 +++- packages/@aws-cdk/aws-lambda/package.json | 9 +++- packages/@aws-cdk/aws-logs/README.md | 27 +++--------- packages/@aws-cdk/aws-logs/package.json | 9 +++- .../aws-logs/rosetta/default.ts-fixture | 1 + packages/@aws-cdk/aws-rds/package.json | 9 +++- .../aws-route53-patterns/package.json | 9 +++- .../@aws-cdk/aws-route53-targets/README.md | 21 +++++---- .../@aws-cdk/aws-route53-targets/package.json | 9 +++- packages/@aws-cdk/aws-route53/package.json | 9 +++- packages/@aws-cdk/aws-s3/lib/bucket.ts | 44 +++++++++++-------- packages/@aws-cdk/aws-s3/package.json | 9 +++- .../aws-sns-subscriptions/package.json | 9 +++- packages/@aws-cdk/aws-sns/package.json | 9 +++- packages/@aws-cdk/aws-sqs/package.json | 9 +++- packages/@aws-cdk/aws-ssm/package.json | 9 +++- .../lib/athena/get-query-execution.ts | 2 +- .../lib/athena/get-query-results.ts | 4 +- .../lib/athena/start-query-execution.ts | 3 +- .../lib/dynamodb/shared-types.ts | 6 +-- .../lib/evaluate-expression.ts | 2 +- .../lib/eventbridge/put-events.ts | 10 +++-- .../aws-stepfunctions-tasks/package.json | 9 +++- .../aws-stepfunctions/lib/states/wait.ts | 6 +-- .../@aws-cdk/aws-stepfunctions/package.json | 9 +++- packages/@aws-cdk/aws-synthetics/package.json | 9 +++- .../lib/blueprint/stack-deployment.ts | 2 +- packages/@aws-cdk/pipelines/package.json | 9 +++- 57 files changed, 375 insertions(+), 125 deletions(-) diff --git a/packages/@aws-cdk/assertions/package.json b/packages/@aws-cdk/assertions/package.json index 35e2842a2bd0c..1b515143d4d58 100644 --- a/packages/@aws-cdk/assertions/package.json +++ b/packages/@aws-cdk/assertions/package.json @@ -50,7 +50,7 @@ "metadata": { "jsii": { "rosetta": { - "strict": false + "strict": true } } } diff --git a/packages/@aws-cdk/aws-apigateway/package.json b/packages/@aws-cdk/aws-apigateway/package.json index 0901cd7438a7b..d20ad7f17d5f5 100644 --- a/packages/@aws-cdk/aws-apigateway/package.json +++ b/packages/@aws-cdk/aws-apigateway/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json b/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json index 67b23ee362f5a..8465cb5e8a9a5 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json @@ -30,7 +30,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-apigatewayv2-integrations/README.md b/packages/@aws-cdk/aws-apigatewayv2-integrations/README.md index dc29843d00209..d284be9491e99 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-integrations/README.md +++ b/packages/@aws-cdk/aws-apigatewayv2-integrations/README.md @@ -172,7 +172,7 @@ The following example creates a new header - `header2` - as a copy of `header1` ```ts import { HttpAlbIntegration } from '@aws-cdk/aws-apigatewayv2-integrations'; -declare const lb: elbv2.NetworkLoadBalancer; +declare const lb: elbv2.ApplicationLoadBalancer; const listener = lb.addListener('listener', { port: 80 }); listener.addTargets('target', { port: 80, @@ -182,9 +182,8 @@ const httpEndpoint = new apigwv2.HttpApi(this, 'HttpProxyPrivateApi', { defaultIntegration: new HttpAlbIntegration({ listener, parameterMapping: new apigwv2.ParameterMapping() - .appendHeader('header2', apigwv2.MappingValue.header('header1')) + .appendHeader('header2', apigwv2.MappingValue.requestHeader('header1')) .removeHeader('header1'), - }), }), }); ``` @@ -194,7 +193,7 @@ To add mapping keys and values not yet supported by the CDK, use the `custom()` ```ts import { HttpAlbIntegration } from '@aws-cdk/aws-apigatewayv2-integrations'; -declare const lb: elbv2.NetworkLoadBalancer; +declare const lb: elbv2.ApplicationLoadBalancer; const listener = lb.addListener('listener', { port: 80 }); listener.addTargets('target', { port: 80, @@ -203,9 +202,7 @@ listener.addTargets('target', { const httpEndpoint = new apigwv2.HttpApi(this, 'HttpProxyPrivateApi', { defaultIntegration: new HttpAlbIntegration({ listener, - parameterMapping: new apigwv2.ParameterMapping() - .custom('myKey', 'myValue'), - }), + parameterMapping: new apigwv2.ParameterMapping().custom('myKey', 'myValue'), }), }); ``` @@ -217,7 +214,7 @@ WebSocket integrations connect a route to backend resources. The following integ ### Lambda WebSocket Integration -Lambda integrations enable integrating a WebSocket API route with a Lambda function. When a client connects/disconnects +Lambda integrations enable integrating a WebSocket API route with a Lambda function. When a client connects/disconnects or sends message specific to a route, the API Gateway service forwards the request to the Lambda function The API Gateway service will invoke the lambda function with an event payload of a specific format. diff --git a/packages/@aws-cdk/aws-apigatewayv2-integrations/package.json b/packages/@aws-cdk/aws-apigatewayv2-integrations/package.json index 10cde741e9d26..203793018a92a 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-integrations/package.json +++ b/packages/@aws-cdk/aws-apigatewayv2-integrations/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-applicationautoscaling/lib/scalable-target.ts b/packages/@aws-cdk/aws-applicationautoscaling/lib/scalable-target.ts index ca5f973a8fa50..7937b331c018a 100644 --- a/packages/@aws-cdk/aws-applicationautoscaling/lib/scalable-target.ts +++ b/packages/@aws-cdk/aws-applicationautoscaling/lib/scalable-target.ts @@ -39,7 +39,8 @@ export interface ScalableTargetProps { * * This string consists of the resource type and unique identifier. * - * @example service/ecsStack-MyECSCluster-AB12CDE3F4GH/ecsStack-MyECSService-AB12CDE3F4GH + * Example value: `service/ecsStack-MyECSCluster-AB12CDE3F4GH/ecsStack-MyECSService-AB12CDE3F4GH` + * * @see https://docs.aws.amazon.com/autoscaling/application/APIReference/API_RegisterScalableTarget.html */ readonly resourceId: string; @@ -49,7 +50,7 @@ export interface ScalableTargetProps { * * Specify the service namespace, resource type, and scaling property. * - * @example ecs:service:DesiredCount + * Example value: `ecs:service:DesiredCount` * @see https://docs.aws.amazon.com/autoscaling/application/APIReference/API_ScalingPolicy.html */ readonly scalableDimension: string; @@ -82,7 +83,8 @@ export class ScalableTarget extends Resource implements IScalableTarget { /** * ID of the Scalable Target * - * @example service/ecsStack-MyECSCluster-AB12CDE3F4GH/ecsStack-MyECSService-AB12CDE3F4GH|ecs:service:DesiredCount|ecs + * Example value: `service/ecsStack-MyECSCluster-AB12CDE3F4GH/ecsStack-MyECSService-AB12CDE3F4GH|ecs:service:DesiredCount|ecs` + * * @attribute */ public readonly scalableTargetId: string; diff --git a/packages/@aws-cdk/aws-applicationautoscaling/lib/target-tracking-scaling-policy.ts b/packages/@aws-cdk/aws-applicationautoscaling/lib/target-tracking-scaling-policy.ts index f7ccaff153ffe..de48ab5fd21e7 100644 --- a/packages/@aws-cdk/aws-applicationautoscaling/lib/target-tracking-scaling-policy.ts +++ b/packages/@aws-cdk/aws-applicationautoscaling/lib/target-tracking-scaling-policy.ts @@ -88,7 +88,7 @@ export interface BasicTargetTrackingScalingPolicyProps extends BaseTargetTrackin * * Only used for predefined metric ALBRequestCountPerTarget. * - * @example app///targetgroup// + * Example value: `app///targetgroup//` * * @default - No resource label. */ diff --git a/packages/@aws-cdk/aws-applicationautoscaling/package.json b/packages/@aws-cdk/aws-applicationautoscaling/package.json index ca025554d085f..17bfd09967f88 100644 --- a/packages/@aws-cdk/aws-applicationautoscaling/package.json +++ b/packages/@aws-cdk/aws-applicationautoscaling/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-service.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-service.ts index cb561eff19308..a75989bbbe66a 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-service.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-service.ts @@ -40,7 +40,7 @@ export interface VirtualServiceProps { * It is recommended this follows the fully-qualified domain name format, * such as "my-service.default.svc.cluster.local". * - * @example service.domain.local + * Example value: `service.domain.local` * @default - A name is automatically generated */ readonly virtualServiceName?: string; diff --git a/packages/@aws-cdk/aws-appmesh/package.json b/packages/@aws-cdk/aws-appmesh/package.json index a7024d16cbc9b..0bf1fc94a030d 100644 --- a/packages/@aws-cdk/aws-appmesh/package.json +++ b/packages/@aws-cdk/aws-appmesh/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-appsync/package.json b/packages/@aws-cdk/aws-appsync/package.json index f36a7ada3d7ec..814bfb98e9600 100644 --- a/packages/@aws-cdk/aws-appsync/package.json +++ b/packages/@aws-cdk/aws-appsync/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-autoscaling/package.json b/packages/@aws-cdk/aws-autoscaling/package.json index 0fd6ce84bf75c..dfdb08ad1ac2b 100644 --- a/packages/@aws-cdk/aws-autoscaling/package.json +++ b/packages/@aws-cdk/aws-autoscaling/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-cloudwatch/package.json b/packages/@aws-cdk/aws-cloudwatch/package.json index 5466e925c6663..b02f9e882b51a 100644 --- a/packages/@aws-cdk/aws-cloudwatch/package.json +++ b/packages/@aws-cdk/aws-cloudwatch/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-codebuild/README.md b/packages/@aws-cdk/aws-codebuild/README.md index 29d06892bc682..4bccbbc68cfa1 100644 --- a/packages/@aws-cdk/aws-codebuild/README.md +++ b/packages/@aws-cdk/aws-codebuild/README.md @@ -243,7 +243,7 @@ new codebuild.Project(this, 'Project', { }), // Enable Docker AND custom caching - cache: codebuild.Cache.local(codebuild.LocalCacheMode.DOCKER_LAYER, codebuild.LocalCacheMode.CUSTOM) + cache: codebuild.Cache.local(codebuild.LocalCacheMode.DOCKER_LAYER, codebuild.LocalCacheMode.CUSTOM), // BuildSpec with a 'cache' section necessary for 'CUSTOM' caching. This can // also come from 'buildspec.yml' in your source. diff --git a/packages/@aws-cdk/aws-codebuild/lib/file-location.ts b/packages/@aws-cdk/aws-codebuild/lib/file-location.ts index eabfd95570b06..b836fc92bc22a 100644 --- a/packages/@aws-cdk/aws-codebuild/lib/file-location.ts +++ b/packages/@aws-cdk/aws-codebuild/lib/file-location.ts @@ -71,7 +71,8 @@ export interface EfsFileSystemLocationProps { /** * A string that specifies the location of the file system, like Amazon EFS. - * @example 'fs-abcd1234.efs.us-west-2.amazonaws.com:/my-efs-mount-directory'. + * + * This value looks like `fs-abcd1234.efs.us-west-2.amazonaws.com:/my-efs-mount-directory`. */ readonly location: string; diff --git a/packages/@aws-cdk/aws-codebuild/package.json b/packages/@aws-cdk/aws-codebuild/package.json index 50ac7d24ef78a..05c5e55339acb 100644 --- a/packages/@aws-cdk/aws-codebuild/package.json +++ b/packages/@aws-cdk/aws-codebuild/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-codepipeline-actions/README.md b/packages/@aws-cdk/aws-codepipeline-actions/README.md index 05860cfe20233..7625ed08bfb8e 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/README.md +++ b/packages/@aws-cdk/aws-codepipeline-actions/README.md @@ -987,7 +987,7 @@ The Lambda Action supports custom user parameters that pipeline will pass to the Lambda function: ```ts -import * as lambda from '@aws-cdk/aws-lambda'; +declare const fn: lambda.Function; const pipeline = new codepipeline.Pipeline(this, 'MyPipeline'); const lambdaAction = new codepipeline_actions.LambdaInvokeAction({ diff --git a/packages/@aws-cdk/aws-codepipeline-actions/package.json b/packages/@aws-cdk/aws-codepipeline-actions/package.json index f1624f676dc9c..dc2c146e3ddd7 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/package.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-dynamodb/package.json b/packages/@aws-cdk/aws-dynamodb/package.json index f1fdf012ab0b8..d63167cf3d5de 100644 --- a/packages/@aws-cdk/aws-dynamodb/package.json +++ b/packages/@aws-cdk/aws-dynamodb/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-ec2/package.json b/packages/@aws-cdk/aws-ec2/package.json index f8664e445ec97..44347facefb48 100644 --- a/packages/@aws-cdk/aws-ec2/package.json +++ b/packages/@aws-cdk/aws-ec2/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-ecr/package.json b/packages/@aws-cdk/aws-ecr/package.json index 704de9272f366..c9039866abcc0 100644 --- a/packages/@aws-cdk/aws-ecr/package.json +++ b/packages/@aws-cdk/aws-ecr/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-ecs-patterns/package.json b/packages/@aws-cdk/aws-ecs-patterns/package.json index e399ff853d46b..96a5eccecc75f 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/package.json +++ b/packages/@aws-cdk/aws-ecs-patterns/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-ecs/package.json b/packages/@aws-cdk/aws-ecs/package.json index 7cdcc0b9b0934..dc6c57ffe9285 100644 --- a/packages/@aws-cdk/aws-ecs/package.json +++ b/packages/@aws-cdk/aws-ecs/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-load-balancer.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-load-balancer.ts index bcf51d4d81a78..85ae9d143f0e2 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-load-balancer.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-load-balancer.ts @@ -52,16 +52,18 @@ export interface ILoadBalancerV2 extends IResource { /** * The canonical hosted zone ID of this load balancer * + * Example value: `Z2P70J7EXAMPLE` + * * @attribute - * @example Z2P70J7EXAMPLE */ readonly loadBalancerCanonicalHostedZoneId: string; /** * The DNS name of this load balancer * + * Example value: `my-load-balancer-424835706.us-west-2.elb.amazonaws.com` + * * @attribute - * @example my-load-balancer-424835706.us-west-2.elb.amazonaws.com */ readonly loadBalancerDnsName: string; } @@ -141,40 +143,45 @@ export abstract class BaseLoadBalancer extends Resource { /** * The canonical hosted zone ID of this load balancer * + * Example value: `Z2P70J7EXAMPLE` + * * @attribute - * @example Z2P70J7EXAMPLE */ public readonly loadBalancerCanonicalHostedZoneId: string; /** * The DNS name of this load balancer * + * Example value: `my-load-balancer-424835706.us-west-2.elb.amazonaws.com` + * * @attribute - * @example my-load-balancer-424835706.us-west-2.elb.amazonaws.com */ public readonly loadBalancerDnsName: string; /** * The full name of this load balancer * + * Example value: `app/my-load-balancer/50dc6c495c0c9188` + * * @attribute - * @example app/my-load-balancer/50dc6c495c0c9188 */ public readonly loadBalancerFullName: string; /** * The name of this load balancer * + * Example value: `my-load-balancer` + * * @attribute - * @example my-load-balancer */ public readonly loadBalancerName: string; /** * The ARN of this load balancer * + * Example value: `arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/my-internal-load-balancer/50dc6c495c0c9188` + * * @attribute - * @example arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/my-internal-load-balancer/50dc6c495c0c9188 */ public readonly loadBalancerArn: string; diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-target-group.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-target-group.ts index 29a9cb707556e..69d925bc933e9 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-target-group.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-target-group.ts @@ -186,7 +186,7 @@ export abstract class TargetGroupBase extends CoreConstruct implements ITargetGr * This identifier is emitted as a dimensions of the metrics of this target * group. * - * @example app/my-load-balancer/123456789 + * Example value: `app/my-load-balancer/123456789` */ public abstract readonly firstLoadBalancerFullName: string; diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json b/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json index f7f5546a41b05..d2a9b352b4163 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-events-targets/package.json b/packages/@aws-cdk/aws-events-targets/package.json index e937e5c40ac4b..c59f621de9d2b 100644 --- a/packages/@aws-cdk/aws-events-targets/package.json +++ b/packages/@aws-cdk/aws-events-targets/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-events/package.json b/packages/@aws-cdk/aws-events/package.json index fb10cafd5c60b..7159bada50ee2 100644 --- a/packages/@aws-cdk/aws-events/package.json +++ b/packages/@aws-cdk/aws-events/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-iam/package.json b/packages/@aws-cdk/aws-iam/package.json index 7fda5ccefad3d..e448eaf64cec0 100644 --- a/packages/@aws-cdk/aws-iam/package.json +++ b/packages/@aws-cdk/aws-iam/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-kms/package.json b/packages/@aws-cdk/aws-kms/package.json index 66a47be7c03da..e38c467b3b4f7 100644 --- a/packages/@aws-cdk/aws-kms/package.json +++ b/packages/@aws-cdk/aws-kms/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json index b966e65d20c98..6afda74f971fc 100644 --- a/packages/@aws-cdk/aws-lambda/package.json +++ b/packages/@aws-cdk/aws-lambda/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-logs/README.md b/packages/@aws-cdk/aws-logs/README.md index 631bc382365e0..804b5c56c4eb3 100644 --- a/packages/@aws-cdk/aws-logs/README.md +++ b/packages/@aws-cdk/aws-logs/README.md @@ -51,14 +51,11 @@ publish their log group to a specific region, such as AWS Chatbot creating a log ## Resource Policy CloudWatch Resource Policies allow other AWS services or IAM Principals to put log events into the log groups. -A resource policy is automatically created when `addToResourcePolicy` is called on the LogGroup for the first time. - -`ResourcePolicy` can also be created manually. +A resource policy is automatically created when `addToResourcePolicy` is called on the LogGroup for the first time: ```ts -const logGroup = new LogGroup(this, 'LogGroup'); -const resourcePolicy = new ResourcePolicy(this, 'ResourcePolicy'); -resourcePolicy.document.addStatements(new iam.PolicyStatement({ +const logGroup = new logs.LogGroup(this, 'LogGroup'); +logGroup.addToResourcePolicy(new iam.PolicyStatement({ actions: ['logs:CreateLogStream', 'logs:PutLogEvents'], principals: [new iam.ServicePrincipal('es.amazonaws.com')], resources: [logGroup.logGroupArn], @@ -68,22 +65,8 @@ resourcePolicy.document.addStatements(new iam.PolicyStatement({ Or more conveniently, write permissions to the log group can be granted as follows which gives same result as in the above example. ```ts -const logGroup = new LogGroup(this, 'LogGroup'); -logGroup.grantWrite(iam.ServicePrincipal('es.amazonaws.com')); -``` - -Optionally name and policy statements can also be passed on `ResourcePolicy` construction. - -```ts -const policyStatement = new new iam.PolicyStatement({ - resources: ["*"], - actions: ['logs:PutLogEvents'], - principals: [new iam.ArnPrincipal('arn:aws:iam::123456789012:user/user-name')], -}); -const resourcePolicy = new ResourcePolicy(this, 'ResourcePolicy', { - policyName: 'myResourcePolicy', - policyStatements: [policyStatement], -}); +const logGroup = new logs.LogGroup(this, 'LogGroup'); +logGroup.grantWrite(new iam.ServicePrincipal('es.amazonaws.com')); ``` ## Encrypting Log Groups diff --git a/packages/@aws-cdk/aws-logs/package.json b/packages/@aws-cdk/aws-logs/package.json index 5c1d328a6f6bb..9169d12ac72fe 100644 --- a/packages/@aws-cdk/aws-logs/package.json +++ b/packages/@aws-cdk/aws-logs/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-logs/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-logs/rosetta/default.ts-fixture index 27c338ce30a32..6c34f60674f19 100644 --- a/packages/@aws-cdk/aws-logs/rosetta/default.ts-fixture +++ b/packages/@aws-cdk/aws-logs/rosetta/default.ts-fixture @@ -2,6 +2,7 @@ import { Construct } from 'constructs'; import { Stack } from '@aws-cdk/core'; import * as logs from '@aws-cdk/aws-logs'; +import * as iam from '@aws-cdk/aws-iam'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import * as lambda from '@aws-cdk/aws-lambda'; diff --git a/packages/@aws-cdk/aws-rds/package.json b/packages/@aws-cdk/aws-rds/package.json index b1bf50b500a40..a9c66bfb3f081 100644 --- a/packages/@aws-cdk/aws-rds/package.json +++ b/packages/@aws-cdk/aws-rds/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-route53-patterns/package.json b/packages/@aws-cdk/aws-route53-patterns/package.json index 7f3d2c396ede3..d8c12460322a6 100644 --- a/packages/@aws-cdk/aws-route53-patterns/package.json +++ b/packages/@aws-cdk/aws-route53-patterns/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-route53-targets/README.md b/packages/@aws-cdk/aws-route53-targets/README.md index 9d53630628ddd..73269abb6d6ed 100644 --- a/packages/@aws-cdk/aws-route53-targets/README.md +++ b/packages/@aws-cdk/aws-route53-targets/README.md @@ -58,7 +58,7 @@ This library contains Route53 Alias Record targets for: ```ts import * as elbv2 from '@aws-cdk/aws-elasticloadbalancingv2'; - + declare const zone: route53.HostedZone; declare const lb: elbv2.ApplicationLoadBalancer; @@ -73,7 +73,7 @@ This library contains Route53 Alias Record targets for: ```ts import * as elb from '@aws-cdk/aws-elasticloadbalancing'; - + declare const zone: route53.HostedZone; declare const lb: elb.LoadBalancer; @@ -129,7 +129,7 @@ See [the Developer Guide](https://docs.aws.amazon.com/Route53/latest/DeveloperGu ```ts import * as s3 from '@aws-cdk/aws-s3'; - + const recordName = 'www'; const domainName = 'example.com'; @@ -176,11 +176,14 @@ See [the Developer Guide](https://docs.aws.amazon.com/Route53/latest/DeveloperGu **Important:** Only supports Elastic Beanstalk environments created after 2016 that have a regional endpoint. - ```ts - new route53.ARecord(this, 'AliasRecord', { - zone, - target: route53.RecordTarget.fromAlias(new alias.ElasticBeanstalkEnvironmentEndpointTarget(ebsEnvironmentUrl)), - }); - ``` +```ts +declare const zone: route53.HostedZone; +declare const ebsEnvironmentUrl: string; + +new route53.ARecord(this, 'AliasRecord', { + zone, + target: route53.RecordTarget.fromAlias(new targets.ElasticBeanstalkEnvironmentEndpointTarget(ebsEnvironmentUrl)), +}); +``` See the documentation of `@aws-cdk/aws-route53` for more information. diff --git a/packages/@aws-cdk/aws-route53-targets/package.json b/packages/@aws-cdk/aws-route53-targets/package.json index cd191bfc3173e..a65e4a0372abf 100644 --- a/packages/@aws-cdk/aws-route53-targets/package.json +++ b/packages/@aws-cdk/aws-route53-targets/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-route53/package.json b/packages/@aws-cdk/aws-route53/package.json index 35c3abefcc477..aabd22c95363b 100644 --- a/packages/@aws-cdk/aws-route53/package.json +++ b/packages/@aws-cdk/aws-route53/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-s3/lib/bucket.ts b/packages/@aws-cdk/aws-s3/lib/bucket.ts index 157aa31a3ed5f..ce8dafd5a69aa 100644 --- a/packages/@aws-cdk/aws-s3/lib/bucket.ts +++ b/packages/@aws-cdk/aws-s3/lib/bucket.ts @@ -105,9 +105,10 @@ export interface IBucket extends IResource { /** * The https URL of an S3 object. For example: - * @example https://s3.us-west-1.amazonaws.com/onlybucket - * @example https://s3.us-west-1.amazonaws.com/bucket/key - * @example https://s3.cn-north-1.amazonaws.com.cn/china-bucket/mykey + * + * - `https://s3.us-west-1.amazonaws.com/onlybucket` + * - `https://s3.us-west-1.amazonaws.com/bucket/key` + * - `https://s3.cn-north-1.amazonaws.com.cn/china-bucket/mykey` * @param key The S3 key of the object. If not specified, the URL of the * bucket is returned. * @returns an ObjectS3Url token @@ -117,10 +118,11 @@ export interface IBucket extends IResource { /** * The virtual hosted-style URL of an S3 object. Specify `regional: false` at * the options for non-regional URL. For example: - * @example https://only-bucket.s3.us-west-1.amazonaws.com - * @example https://bucket.s3.us-west-1.amazonaws.com/key - * @example https://bucket.s3.amazonaws.com/key - * @example https://china-bucket.s3.cn-north-1.amazonaws.com.cn/mykey + * + * - `https://only-bucket.s3.us-west-1.amazonaws.com` + * - `https://bucket.s3.us-west-1.amazonaws.com/key` + * - `https://bucket.s3.amazonaws.com/key` + * - `https://china-bucket.s3.cn-north-1.amazonaws.com.cn/mykey` * @param key The S3 key of the object. If not specified, the URL of the * bucket is returned. * @param options Options for generating URL. @@ -130,8 +132,8 @@ export interface IBucket extends IResource { /** * The S3 URL of an S3 object. For example: - * @example s3://onlybucket - * @example s3://bucket/key + * - `s3://onlybucket` + * - `s3://bucket/key` * @param key The S3 key of the object. If not specified, the S3 URL of the * bucket is returned. * @returns an ObjectS3Url token @@ -603,9 +605,11 @@ export abstract class BucketBase extends Resource implements IBucket { /** * The https URL of an S3 object. Specify `regional: false` at the options * for non-regional URLs. For example: - * @example https://s3.us-west-1.amazonaws.com/onlybucket - * @example https://s3.us-west-1.amazonaws.com/bucket/key - * @example https://s3.cn-north-1.amazonaws.com.cn/china-bucket/mykey + * + * - `https://s3.us-west-1.amazonaws.com/onlybucket` + * - `https://s3.us-west-1.amazonaws.com/bucket/key` + * - `https://s3.cn-north-1.amazonaws.com.cn/china-bucket/mykey` + * * @param key The S3 key of the object. If not specified, the URL of the * bucket is returned. * @returns an ObjectS3Url token @@ -622,10 +626,12 @@ export abstract class BucketBase extends Resource implements IBucket { /** * The virtual hosted-style URL of an S3 object. Specify `regional: false` at * the options for non-regional URL. For example: - * @example https://only-bucket.s3.us-west-1.amazonaws.com - * @example https://bucket.s3.us-west-1.amazonaws.com/key - * @example https://bucket.s3.amazonaws.com/key - * @example https://china-bucket.s3.cn-north-1.amazonaws.com.cn/mykey + * + * - `https://only-bucket.s3.us-west-1.amazonaws.com` + * - `https://bucket.s3.us-west-1.amazonaws.com/key` + * - `https://bucket.s3.amazonaws.com/key` + * - `https://china-bucket.s3.cn-north-1.amazonaws.com.cn/mykey` + * * @param key The S3 key of the object. If not specified, the URL of the * bucket is returned. * @param options Options for generating URL. @@ -642,8 +648,10 @@ export abstract class BucketBase extends Resource implements IBucket { /** * The S3 URL of an S3 object. For example: - * @example s3://onlybucket - * @example s3://bucket/key + * + * - `s3://onlybucket` + * - `s3://bucket/key` + * * @param key The S3 key of the object. If not specified, the S3 URL of the * bucket is returned. * @returns an ObjectS3Url token diff --git a/packages/@aws-cdk/aws-s3/package.json b/packages/@aws-cdk/aws-s3/package.json index e3feff8d0fe6c..f8153d202545d 100644 --- a/packages/@aws-cdk/aws-s3/package.json +++ b/packages/@aws-cdk/aws-s3/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-sns-subscriptions/package.json b/packages/@aws-cdk/aws-sns-subscriptions/package.json index ba1cfe852a8aa..6f8a4f5a2b9cc 100644 --- a/packages/@aws-cdk/aws-sns-subscriptions/package.json +++ b/packages/@aws-cdk/aws-sns-subscriptions/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-sns/package.json b/packages/@aws-cdk/aws-sns/package.json index 2250bcb72ba1b..b5f4039c63744 100644 --- a/packages/@aws-cdk/aws-sns/package.json +++ b/packages/@aws-cdk/aws-sns/package.json @@ -31,7 +31,14 @@ "excludeTypescript": [ "examples" ], - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-sqs/package.json b/packages/@aws-cdk/aws-sqs/package.json index 874d8caf10d72..af74b209b3931 100644 --- a/packages/@aws-cdk/aws-sqs/package.json +++ b/packages/@aws-cdk/aws-sqs/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-ssm/package.json b/packages/@aws-cdk/aws-ssm/package.json index 55ad40f1dfd04..cdf17bfb69c1c 100644 --- a/packages/@aws-cdk/aws-ssm/package.json +++ b/packages/@aws-cdk/aws-ssm/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/athena/get-query-execution.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/athena/get-query-execution.ts index 6a9eaab73eb4b..23e4079b77921 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/athena/get-query-execution.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/athena/get-query-execution.ts @@ -10,7 +10,7 @@ export interface AthenaGetQueryExecutionProps extends sfn.TaskStateBaseProps { /** * Query that will be retrieved * - * @example 'adfsaf-23trf23-f23rt23' + * Example value: `adfsaf-23trf23-f23rt23` */ readonly queryExecutionId: string; } diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/athena/get-query-results.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/athena/get-query-results.ts index 07ec38efa97e1..700b43fc2a599 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/athena/get-query-results.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/athena/get-query-results.ts @@ -10,8 +10,8 @@ export interface AthenaGetQueryResultsProps extends sfn.TaskStateBaseProps { /** * Query that will be retrieved * - * @example 'adfsaf-23trf23-f23rt23' - */ + * Example value: `adfsaf-23trf23-f23rt23` + */ readonly queryExecutionId: string; /** diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/athena/start-query-execution.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/athena/start-query-execution.ts index f23bd66fc5591..ba3f68b95abb1 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/athena/start-query-execution.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/athena/start-query-execution.ts @@ -212,8 +212,9 @@ export interface ResultConfiguration { /** * S3 path of query results * + * Example value: `s3://query-results-bucket/folder/` + * * @default - Query Result Location set in Athena settings for this workgroup - * @example s3://query-results-bucket/folder/ */ readonly outputLocation?: s3.Location; diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/dynamodb/shared-types.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/dynamodb/shared-types.ts index 0921cf3b2521a..bed1b16a86f70 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/dynamodb/shared-types.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/dynamodb/shared-types.ts @@ -119,10 +119,10 @@ export class DynamoProjectionExpression { export class DynamoAttributeValue { /** * Sets an attribute of type String. For example: "S": "Hello" - * Strings may be literal values or as JsonPath. + * Strings may be literal values or as JsonPath. Example values: * - * @example `DynamoAttributeValue.fromString('someValue') - * @example `DynamoAttributeValue.fromString(JsonPath.stringAt('$.bar')) + * - `DynamoAttributeValue.fromString('someValue')` + * - `DynamoAttributeValue.fromString(JsonPath.stringAt('$.bar'))` */ public static fromString(value: string) { return new DynamoAttributeValue({ S: value }); diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/evaluate-expression.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/evaluate-expression.ts index 7fb010119373b..5b90ce066c70d 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/evaluate-expression.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/evaluate-expression.ts @@ -12,7 +12,7 @@ export interface EvaluateExpressionProps extends sfn.TaskStateBaseProps { /** * The expression to evaluate. The expression may contain state paths. * - * @example '$.a + $.b' + * Example value: `'$.a + $.b'` */ readonly expression: string; diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/eventbridge/put-events.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/eventbridge/put-events.ts index 457bdfa6d0011..2c08c1ac41966 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/eventbridge/put-events.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/eventbridge/put-events.ts @@ -16,9 +16,10 @@ export interface EventBridgePutEventsEntry { * * Can either be provided as an object or as a JSON-serialized string * @example - * sfn.TaskInput.fromText('{"instance-id": "i-1234567890abcdef0", "state": "terminated"}') - * sfn.TaskInput.fromObject({ Message: 'Hello from Step Functions' }) - * sfn.TaskInput.fromJsonPathAt('$.EventDetail') + * + * sfn.TaskInput.fromText('{"instance-id": "i-1234567890abcdef0", "state": "terminated"}'); + * sfn.TaskInput.fromObject({ Message: 'Hello from Step Functions' }); + * sfn.TaskInput.fromJsonPathAt('$.EventDetail'); */ readonly detail: sfn.TaskInput; @@ -40,7 +41,8 @@ export interface EventBridgePutEventsEntry { /** * The service or application that caused this event to be generated * - * @example 'com.example.service' + * Example value: `com.example.service` + * * @see https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-events.html */ readonly source: string; diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/package.json b/packages/@aws-cdk/aws-stepfunctions-tasks/package.json index 2ff381bd3f9d0..daa88936b437c 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/package.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/states/wait.ts b/packages/@aws-cdk/aws-stepfunctions/lib/states/wait.ts index 8386b3ce334b1..f0fd18d22c090 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/states/wait.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/states/wait.ts @@ -18,21 +18,21 @@ export class WaitTime { /** * Wait until the given ISO8601 timestamp * - * @example 2016-03-14T01:59:00Z + * Example value: `2016-03-14T01:59:00Z` */ public static timestamp(timestamp: string) { return new WaitTime({ Timestamp: timestamp }); } /** * Wait for a number of seconds stored in the state object. * - * @example $.waitSeconds + * Example value: `$.waitSeconds` */ public static secondsPath(path: string) { return new WaitTime({ SecondsPath: path }); } /** * Wait until a timestamp found in the state object. * - * @example $.waitTimestamp + * Example value: `$.waitTimestamp` */ public static timestampPath(path: string) { return new WaitTime({ TimestampPath: path }); } diff --git a/packages/@aws-cdk/aws-stepfunctions/package.json b/packages/@aws-cdk/aws-stepfunctions/package.json index 701d06a238562..40cea293bc09f 100644 --- a/packages/@aws-cdk/aws-stepfunctions/package.json +++ b/packages/@aws-cdk/aws-stepfunctions/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-synthetics/package.json b/packages/@aws-cdk/aws-synthetics/package.json index 76e2fa4520b1e..745f97fc21261 100644 --- a/packages/@aws-cdk/aws-synthetics/package.json +++ b/packages/@aws-cdk/aws-synthetics/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/pipelines/lib/blueprint/stack-deployment.ts b/packages/@aws-cdk/pipelines/lib/blueprint/stack-deployment.ts index 488551f4eefb7..b3e88cbfe026e 100644 --- a/packages/@aws-cdk/pipelines/lib/blueprint/stack-deployment.ts +++ b/packages/@aws-cdk/pipelines/lib/blueprint/stack-deployment.ts @@ -183,7 +183,7 @@ export class StackDeployment { * This is `undefined` if the stack template is not published. Use the * `DefaultStackSynthesizer` to ensure it is. * - * @example https://bucket.s3.amazonaws.com/object/key + * Example value: `https://bucket.s3.amazonaws.com/object/key` */ public readonly templateUrl?: string; diff --git a/packages/@aws-cdk/pipelines/package.json b/packages/@aws-cdk/pipelines/package.json index b3d838723ed75..ec3538c30b4c9 100644 --- a/packages/@aws-cdk/pipelines/package.json +++ b/packages/@aws-cdk/pipelines/package.json @@ -126,7 +126,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "awscdkio": { "announce": false From 69372962ee79c75ff5ff9cdd15026563d79fcb33 Mon Sep 17 00:00:00 2001 From: Tatsuya Yamamoto Date: Wed, 10 Nov 2021 04:51:26 +0900 Subject: [PATCH 05/23] chore(iot-actions): simplify cloudwatch-logs-action test (#17385) This PR refactor the test that I committed earlier based on the above comment. - https://github.com/aws/aws-cdk/pull/17307#discussion_r742181458 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../cloudwatch-logs-action.test.ts | 52 +++++-------------- 1 file changed, 12 insertions(+), 40 deletions(-) diff --git a/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/cloudwatch-logs-action.test.ts b/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/cloudwatch-logs-action.test.ts index 4e25f43367c31..3b6ecd2d57fbc 100644 --- a/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/cloudwatch-logs-action.test.ts +++ b/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/cloudwatch-logs-action.test.ts @@ -11,7 +11,7 @@ test('Default cloudwatch logs action', () => { const topicRule = new iot.TopicRule(stack, 'MyTopicRule', { sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id FROM 'device/+/data'"), }); - const logGroup = new logs.LogGroup(stack, 'MyLogGroup'); + const logGroup = logs.LogGroup.fromLogGroupArn(stack, 'my-log-group', 'arn:aws:logs:us-east-1:123456789012:log-group:my-log-group'); // WHEN topicRule.addAction( @@ -24,7 +24,7 @@ test('Default cloudwatch logs action', () => { Actions: [ { CloudwatchLogs: { - LogGroupName: { Ref: 'MyLogGroup5C0DAD85' }, + LogGroupName: 'my-log-group', RoleArn: { 'Fn::GetAtt': [ 'MyTopicRuleTopicRuleActionRoleCE2D05DA', @@ -58,16 +58,12 @@ test('Default cloudwatch logs action', () => { { Action: ['logs:CreateLogStream', 'logs:PutLogEvents'], Effect: 'Allow', - Resource: { - 'Fn::GetAtt': ['MyLogGroup5C0DAD85', 'Arn'], - }, + Resource: 'arn:aws:logs:us-east-1:123456789012:log-group:my-log-group:*', }, { Action: 'logs:DescribeLogStreams', Effect: 'Allow', - Resource: { - 'Fn::GetAtt': ['MyLogGroup5C0DAD85', 'Arn'], - }, + Resource: 'arn:aws:logs:us-east-1:123456789012:log-group:my-log-group:*', }, ], Version: '2012-10-17', @@ -85,7 +81,7 @@ test('can set role', () => { const topicRule = new iot.TopicRule(stack, 'MyTopicRule', { sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id FROM 'device/+/data'"), }); - const logGroup = new logs.LogGroup(stack, 'MyLogGroup'); + const logGroup = logs.LogGroup.fromLogGroupArn(stack, 'my-log-group', 'arn:aws:logs:us-east-1:123456789012:log-group:my-log-group'); const role = iam.Role.fromRoleArn(stack, 'MyRole', 'arn:aws:iam::123456789012:role/ForTest'); // WHEN @@ -101,48 +97,26 @@ test('can set role', () => { Actions: [ { CloudwatchLogs: { - LogGroupName: { Ref: 'MyLogGroup5C0DAD85' }, + LogGroupName: 'my-log-group', RoleArn: 'arn:aws:iam::123456789012:role/ForTest', }, }, ], }, }); -}); - -test('The specified role is added a policy needed for sending data to logs', () => { - // GIVEN - const stack = new cdk.Stack(); - const topicRule = new iot.TopicRule(stack, 'MyTopicRule', { - sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id FROM 'device/+/data'"), - }); - const logGroup = new logs.LogGroup(stack, 'MyLogGroup'); - const role = iam.Role.fromRoleArn(stack, 'MyRole', 'arn:aws:iam::123456789012:role/ForTest'); - - // WHEN - topicRule.addAction( - new actions.CloudWatchLogsAction(logGroup, { - role, - }), - ); - // THEN Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { Action: ['logs:CreateLogStream', 'logs:PutLogEvents'], Effect: 'Allow', - Resource: { - 'Fn::GetAtt': ['MyLogGroup5C0DAD85', 'Arn'], - }, + Resource: 'arn:aws:logs:us-east-1:123456789012:log-group:my-log-group:*', }, { Action: 'logs:DescribeLogStreams', Effect: 'Allow', - Resource: { - 'Fn::GetAtt': ['MyLogGroup5C0DAD85', 'Arn'], - }, + Resource: 'arn:aws:logs:us-east-1:123456789012:log-group:my-log-group:*', }, ], Version: '2012-10-17', @@ -152,16 +126,14 @@ test('The specified role is added a policy needed for sending data to logs', () }); }); - test('When multiple actions are omitted role property, the actions use same one role', () => { - // GIVEN // GIVEN const stack = new cdk.Stack(); const topicRule = new iot.TopicRule(stack, 'MyTopicRule', { sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id FROM 'device/+/data'"), }); - const logGroup1 = new logs.LogGroup(stack, 'MyLogGroup1'); - const logGroup2 = new logs.LogGroup(stack, 'MyLogGroup2'); + const logGroup1 = logs.LogGroup.fromLogGroupArn(stack, 'my-log-group1', 'arn:aws:logs:us-east-1:123456789012:log-group:my-log-group1'); + const logGroup2 = logs.LogGroup.fromLogGroupArn(stack, 'my-log-group2', 'arn:aws:logs:us-east-1:123456789012:log-group:my-log-group2'); // WHEN topicRule.addAction(new actions.CloudWatchLogsAction(logGroup1)); @@ -173,7 +145,7 @@ test('When multiple actions are omitted role property, the actions use same one Actions: [ { CloudwatchLogs: { - LogGroupName: { Ref: 'MyLogGroup14A6E382A' }, + LogGroupName: 'my-log-group1', RoleArn: { 'Fn::GetAtt': [ 'MyTopicRuleTopicRuleActionRoleCE2D05DA', @@ -184,7 +156,7 @@ test('When multiple actions are omitted role property, the actions use same one }, { CloudwatchLogs: { - LogGroupName: { Ref: 'MyLogGroup279D6359D' }, + LogGroupName: 'my-log-group2', RoleArn: { 'Fn::GetAtt': [ 'MyTopicRuleTopicRuleActionRoleCE2D05DA', From a39146840a10472c8afee71bf1a1cfc3cacb5f72 Mon Sep 17 00:00:00 2001 From: Ryan Parker Date: Tue, 9 Nov 2021 12:30:07 -0800 Subject: [PATCH 06/23] fix(aws-logs): include new `policy.ts` exports in `index.ts` exports (#17403) ## Summary This PR modifies the aws-logs `index.ts` file to also forward the exports from `policy.ts` ([a newly created file](https://github.com/aws/aws-cdk/pull/17015) that implements the `ResourcePolicy` class). Fixes: https://github.com/aws/aws-cdk/issues/17402 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-logs/lib/index.ts | 1 + packages/@aws-cdk/aws-logs/lib/policy.ts | 12 +++-- .../@aws-cdk/aws-logs/test/policy.test.ts | 52 +++++++++++++++++++ 3 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 packages/@aws-cdk/aws-logs/test/policy.test.ts diff --git a/packages/@aws-cdk/aws-logs/lib/index.ts b/packages/@aws-cdk/aws-logs/lib/index.ts index 5054715ffe52b..416a9c9a9b257 100644 --- a/packages/@aws-cdk/aws-logs/lib/index.ts +++ b/packages/@aws-cdk/aws-logs/lib/index.ts @@ -5,6 +5,7 @@ export * from './metric-filter'; export * from './pattern'; export * from './subscription-filter'; export * from './log-retention'; +export * from './policy'; // AWS::Logs CloudFormation Resources: export * from './logs.generated'; diff --git a/packages/@aws-cdk/aws-logs/lib/policy.ts b/packages/@aws-cdk/aws-logs/lib/policy.ts index 974f517d48b25..de3af44f1ae2f 100644 --- a/packages/@aws-cdk/aws-logs/lib/policy.ts +++ b/packages/@aws-cdk/aws-logs/lib/policy.ts @@ -11,7 +11,7 @@ export interface ResourcePolicyProps { * Name of the log group resource policy * @default - Uses a unique id based on the construct path */ - readonly policyName?: string; + readonly resourcePolicyName?: string; /** * Initial statements to add to the resource policy @@ -31,15 +31,19 @@ export class ResourcePolicy extends Resource { public readonly document = new PolicyDocument(); constructor(scope: Construct, id: string, props?: ResourcePolicyProps) { - super(scope, id); - new CfnResourcePolicy(this, 'Resource', { + super(scope, id, { + physicalName: props?.resourcePolicyName, + }); + + new CfnResourcePolicy(this, 'ResourcePolicy', { policyName: Lazy.string({ - produce: () => props?.policyName ?? Names.uniqueId(this), + produce: () => props?.resourcePolicyName ?? Names.uniqueId(this), }), policyDocument: Lazy.string({ produce: () => JSON.stringify(this.document), }), }); + if (props?.policyStatements) { this.document.addStatements(...props.policyStatements); } diff --git a/packages/@aws-cdk/aws-logs/test/policy.test.ts b/packages/@aws-cdk/aws-logs/test/policy.test.ts new file mode 100644 index 0000000000000..4b2684a9957b1 --- /dev/null +++ b/packages/@aws-cdk/aws-logs/test/policy.test.ts @@ -0,0 +1,52 @@ +import '@aws-cdk/assert-internal/jest'; +import { PolicyStatement, ServicePrincipal } from '@aws-cdk/aws-iam'; +import { Stack } from '@aws-cdk/core'; +import { LogGroup, ResourcePolicy } from '../lib'; + +describe('resource policy', () => { + test('ResourcePolicy is added to stack, when .addToResourcePolicy() is provided a valid Statement', () => { + // GIVEN + const stack = new Stack(); + const logGroup = new LogGroup(stack, 'LogGroup'); + + // WHEN + logGroup.addToResourcePolicy(new PolicyStatement({ + actions: ['logs:CreateLogStream'], + resources: ['*'], + })); + + // THEN + expect(stack).toHaveResource('AWS::Logs::ResourcePolicy', { + PolicyName: 'LogGroupPolicy643B329C', + PolicyDocument: JSON.stringify({ + Statement: [ + { + Action: 'logs:CreateLogStream', + Effect: 'Allow', + Resource: '*', + }, + ], + Version: '2012-10-17', + }), + }); + }); + + test('ResourcePolicy is added to stack, when created manually/directly', () => { + // GIVEN + const stack = new Stack(); + const logGroup = new LogGroup(stack, 'LogGroup'); + + // WHEN + const resourcePolicy = new ResourcePolicy(stack, 'ResourcePolicy'); + resourcePolicy.document.addStatements(new PolicyStatement({ + actions: ['logs:CreateLogStream', 'logs:PutLogEvents'], + principals: [new ServicePrincipal('es.amazonaws.com')], + resources: [logGroup.logGroupArn], + })); + + // THEN + expect(stack).toHaveResource('AWS::Logs::ResourcePolicy', { + PolicyName: 'ResourcePolicy', + }); + }); +}); From ec5b102e560e241b21c63773817114fc44f7898a Mon Sep 17 00:00:00 2001 From: Ryan Parker Date: Tue, 9 Nov 2021 13:10:16 -0800 Subject: [PATCH 07/23] feat(lambda): singleton function: access runtime, log group and configure layers and environment (#17372) ## Summary This PR adds the following to `SingletonFunction`: - `runtime` - property - `logGroup` - property - `addLayers()` - method - `addEnvironment()` - method Fixes: https://github.com/aws/aws-cdk/issues/17369 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-lambda/lib/singleton-lambda.ts | 48 +++++++++- .../aws-lambda/test/singleton-lambda.test.ts | 87 +++++++++++++++++++ 2 files changed, 134 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/singleton-lambda.ts b/packages/@aws-cdk/aws-lambda/lib/singleton-lambda.ts index 431b9bf6a71d9..c096730b1e8eb 100644 --- a/packages/@aws-cdk/aws-lambda/lib/singleton-lambda.ts +++ b/packages/@aws-cdk/aws-lambda/lib/singleton-lambda.ts @@ -1,11 +1,14 @@ import * as ec2 from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; +import * as logs from '@aws-cdk/aws-logs'; import * as cdk from '@aws-cdk/core'; import { Construct } from 'constructs'; -import { Function as LambdaFunction, FunctionProps } from './function'; +import { Function as LambdaFunction, FunctionProps, EnvironmentOptions } from './function'; import { FunctionBase } from './function-base'; import { Version } from './lambda-version'; +import { ILayerVersion } from './layers'; import { Permission } from './permission'; +import { Runtime } from './runtime'; /** * Properties for a newly created singleton Lambda @@ -47,6 +50,12 @@ export class SingletonFunction extends FunctionBase { public readonly functionArn: string; public readonly role?: iam.IRole; public readonly permissionsNode: cdk.ConstructNode; + + /** + * The runtime environment for the Lambda function. + */ + public readonly runtime: Runtime; + protected readonly canCreatePermissions: boolean; private lambdaFunction: LambdaFunction; @@ -59,6 +68,7 @@ export class SingletonFunction extends FunctionBase { this.functionArn = this.lambdaFunction.functionArn; this.functionName = this.lambdaFunction.functionName; this.role = this.lambdaFunction.role; + this.runtime = this.lambdaFunction.runtime; this.grantPrincipal = this.lambdaFunction.grantPrincipal; this.canCreatePermissions = true; // Doesn't matter, addPermission is overriden anyway @@ -78,6 +88,20 @@ export class SingletonFunction extends FunctionBase { return this.lambdaFunction.connections; } + /** + * The LogGroup where the Lambda function's logs are made available. + * + * If either `logRetention` is set or this property is called, a CloudFormation custom resource is added to the stack that + * pre-creates the log group as part of the stack deployment, if it already doesn't exist, and sets the correct log retention + * period (never expire, by default). + * + * Further, if the log group already exists and the `logRetention` is not set, the custom resource will reset the log retention + * to never expire even if it was configured with a different value. + */ + public get logGroup(): logs.ILogGroup { + return this.lambdaFunction.logGroup; + } + /** * Returns a `lambda.Version` which represents the current version of this * singleton Lambda function. A new version will be created every time the @@ -90,6 +114,28 @@ export class SingletonFunction extends FunctionBase { return this.lambdaFunction.currentVersion; } + /** + * Adds an environment variable to this Lambda function. + * If this is a ref to a Lambda function, this operation results in a no-op. + * @param key The environment variable key. + * @param value The environment variable's value. + * @param options Environment variable options. + */ + public addEnvironment(key: string, value: string, options?: EnvironmentOptions) { + return this.lambdaFunction.addEnvironment(key, value, options); + } + + /** + * Adds one or more Lambda Layers to this Lambda function. + * + * @param layers the layers to be added. + * + * @throws if there are already 5 layers on this function, or the layer is incompatible with this function's runtime. + */ + public addLayers(...layers: ILayerVersion[]) { + return this.lambdaFunction.addLayers(...layers); + } + public addPermission(name: string, permission: Permission) { return this.lambdaFunction.addPermission(name, permission); } diff --git a/packages/@aws-cdk/aws-lambda/test/singleton-lambda.test.ts b/packages/@aws-cdk/aws-lambda/test/singleton-lambda.test.ts index 3bf78253d5ed6..4200b7a18a6e5 100644 --- a/packages/@aws-cdk/aws-lambda/test/singleton-lambda.test.ts +++ b/packages/@aws-cdk/aws-lambda/test/singleton-lambda.test.ts @@ -2,6 +2,7 @@ import '@aws-cdk/assert-internal/jest'; import { ResourcePart } from '@aws-cdk/assert-internal'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; +import * as s3 from '@aws-cdk/aws-s3'; import * as cdk from '@aws-cdk/core'; import * as lambda from '../lib'; @@ -109,6 +110,57 @@ describe('singleton lambda', () => { }, ResourcePart.CompleteDefinition); }); + test('Environment is added to Lambda, when .addEnvironment() is provided one key pair', () => { + // GIVEN + const stack = new cdk.Stack(); + const singleton = new lambda.SingletonFunction(stack, 'Singleton', { + uuid: '84c0de93-353f-4217-9b0b-45b6c993251a', + code: new lambda.InlineCode('def hello(): pass'), + runtime: lambda.Runtime.PYTHON_2_7, + handler: 'index.hello', + timeout: cdk.Duration.minutes(5), + }); + + // WHEN + singleton.addEnvironment('KEY', 'value'); + + // THEN + expect(stack).toHaveResource('AWS::Lambda::Function', { + Environment: { + Variables: { + KEY: 'value', + }, + }, + }); + }); + + test('Layer is added to Lambda, when .addLayers() is provided a valid layer', () => { + // GIVEN + const stack = new cdk.Stack(); + const singleton = new lambda.SingletonFunction(stack, 'Singleton', { + uuid: '84c0de93-353f-4217-9b0b-45b6c993251a', + code: new lambda.InlineCode('def hello(): pass'), + runtime: lambda.Runtime.PYTHON_2_7, + handler: 'index.hello', + timeout: cdk.Duration.minutes(5), + }); + const bucket = new s3.Bucket(stack, 'Bucket'); + const layer = new lambda.LayerVersion(stack, 'myLayer', { + code: new lambda.S3Code(bucket, 'ObjectKey'), + compatibleRuntimes: [lambda.Runtime.PYTHON_2_7], + }); + + // WHEN + singleton.addLayers(layer); + + // THEN + expect(stack).toHaveResource('AWS::Lambda::Function', { + Layers: [{ + Ref: 'myLayerBA1B098A', + }], + }); + }); + test('grantInvoke works correctly', () => { // GIVEN const stack = new cdk.Stack(); @@ -154,6 +206,41 @@ describe('singleton lambda', () => { .toThrow(/contains environment variables .* and is not compatible with Lambda@Edge/); }); + test('logGroup is correctly returned', () => { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + const singleton = new lambda.SingletonFunction(stack, 'Singleton', { + uuid: '84c0de93-353f-4217-9b0b-45b6c993251a', + code: new lambda.InlineCode('def hello(): pass'), + runtime: lambda.Runtime.PYTHON_2_7, + handler: 'index.hello', + timeout: cdk.Duration.minutes(5), + }); + + // THEN + expect(singleton.logGroup.logGroupName).toBeDefined(); + expect(singleton.logGroup.logGroupArn).toBeDefined(); + }); + + test('runtime is correctly returned', () => { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + const singleton = new lambda.SingletonFunction(stack, 'Singleton', { + uuid: '84c0de93-353f-4217-9b0b-45b6c993251a', + code: new lambda.InlineCode('def hello(): pass'), + runtime: lambda.Runtime.PYTHON_2_7, + handler: 'index.hello', + timeout: cdk.Duration.minutes(5), + }); + + // THEN + expect(singleton.runtime).toStrictEqual(lambda.Runtime.PYTHON_2_7); + }); + test('current version of a singleton function', () => { // GIVEN const stack = new cdk.Stack(); From 9c77e941252ad16a2744577b6333ee5054302a30 Mon Sep 17 00:00:00 2001 From: Ryan Parker Date: Tue, 9 Nov 2021 13:49:17 -0800 Subject: [PATCH 08/23] fix(aws-codebuild): add @aws-cdk/asserts to package deps (#17435) ## Summary This PR adds `@aws-cdk/asserts` to the dependency list of `aws-codebuild`. Fixes: https://github.com/aws/aws-cdk/issues/17416 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-codebuild/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/@aws-cdk/aws-codebuild/package.json b/packages/@aws-cdk/aws-codebuild/package.json index 05c5e55339acb..3d1609d9665b4 100644 --- a/packages/@aws-cdk/aws-codebuild/package.json +++ b/packages/@aws-cdk/aws-codebuild/package.json @@ -95,6 +95,7 @@ "jest": "^27.3.1" }, "dependencies": { + "@aws-cdk/assets": "0.0.0", "@aws-cdk/aws-cloudwatch": "0.0.0", "@aws-cdk/aws-codecommit": "0.0.0", "@aws-cdk/aws-codestarnotifications": "0.0.0", From b307b6996ed13b1f2dedeb41d29409183becb969 Mon Sep 17 00:00:00 2001 From: Dillon Date: Tue, 9 Nov 2021 21:28:14 -0500 Subject: [PATCH 09/23] feat(servicecatalog): support local launch role name in launch role constraint (#17371) Service Catalog Launch Role Constraints support the ability to reference a role by name. Many customers use Service Catalog in a hub and spoke model, sharing portfolios to many accounts. This feature allows the launch role to be account agnostic, as the arn is tied to a single account. As a result, a launch role constraint can be created once with the role name and shared rather than sharing the portfolio and creating a launch role constraint in each spoke account. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* Co-authored-by: Aidan Crank --- .../@aws-cdk/aws-servicecatalog/README.md | 30 ++++- .../aws-servicecatalog/lib/portfolio.ts | 42 ++++++- .../lib/private/association-manager.ts | 67 +++++++---- .../lib/private/validation.ts | 12 ++ .../aws-servicecatalog/test/portfolio.test.ts | 106 +++++++++++++++++- 5 files changed, 234 insertions(+), 23 deletions(-) diff --git a/packages/@aws-cdk/aws-servicecatalog/README.md b/packages/@aws-cdk/aws-servicecatalog/README.md index 2d7694d3e84b6..bbc82e13e2d7b 100644 --- a/packages/@aws-cdk/aws-servicecatalog/README.md +++ b/packages/@aws-cdk/aws-servicecatalog/README.md @@ -307,8 +307,36 @@ const launchRole = new iam.Role(this, 'LaunchRole', { portfolio.setLaunchRole(product, launchRole); ``` +You can also set the launch role using just the name of a role which is locally deployed in end user accounts. +This is useful for when roles and users are separately managed outside of the CDK. +The given role must exist in both the account that creates the launch role constraint, +as well as in any end user accounts that wish to provision a product with the launch role. + +You can do this by passing in the role with an explicitly set name: + +```ts fixture=portfolio-product +import * as iam from '@aws-cdk/aws-iam'; + +const launchRole = new iam.Role(this, 'LaunchRole', { + roleName: 'MyRole', + assumedBy: new iam.ServicePrincipal('servicecatalog.amazonaws.com'), +}); + +portfolio.setLocalLaunchRole(product, launchRole); +``` + +Or you can simply pass in a role name and CDK will create a role with that name that trusts service catalog in the account: + +```ts fixture=portfolio-product +import * as iam from '@aws-cdk/aws-iam'; + +const roleName = 'MyRole'; + +const launchRole: iam.IRole = portfolio.setLocalLaunchRoleName(product, roleName); +``` + See [Launch Constraint](https://docs.aws.amazon.com/servicecatalog/latest/adminguide/constraints-launch.html) documentation -to understand permissions roles need. +to understand the permissions roles need. ### Deploy with StackSets diff --git a/packages/@aws-cdk/aws-servicecatalog/lib/portfolio.ts b/packages/@aws-cdk/aws-servicecatalog/lib/portfolio.ts index 3056a48e19777..36d267d022519 100644 --- a/packages/@aws-cdk/aws-servicecatalog/lib/portfolio.ts +++ b/packages/@aws-cdk/aws-servicecatalog/lib/portfolio.ts @@ -112,6 +112,9 @@ export interface IPortfolio extends cdk.IResource { /** * Force users to assume a certain role when launching a product. + * This sets the launch role using the role arn which is tied to the account this role exists in. + * This is useful if you will be provisioning products from the account where this role exists. + * If you intend to share the portfolio across accounts, use a local launch role. * * @param product A service catalog product. * @param launchRole The IAM role a user must assume when provisioning the product. @@ -120,7 +123,30 @@ export interface IPortfolio extends cdk.IResource { setLaunchRole(product: IProduct, launchRole: iam.IRole, options?: CommonConstraintOptions): void; /** - * Configure deployment options using AWS Cloudformaiton StackSets + * Force users to assume a certain role when launching a product. + * The role will be referenced by name in the local account instead of a static role arn. + * A role with this name will automatically be created and assumable by Service Catalog in this account. + * This is useful when sharing the portfolio with multiple accounts. + * + * @param product A service catalog product. + * @param launchRoleName The name of the IAM role a user must assume when provisioning the product. A role with this name must exist in the account where the portolio is created and the accounts it is shared with. + * @param options options for the constraint. + */ + setLocalLaunchRoleName(product: IProduct, launchRoleName: string, options?: CommonConstraintOptions): iam.IRole; + + /** + * Force users to assume a certain role when launching a product. + * The role name will be referenced by in the local account and must be set explicitly. + * This is useful when sharing the portfolio with multiple accounts. + * + * @param product A service catalog product. + * @param launchRole The IAM role a user must assume when provisioning the product. A role with this name must exist in the account where the portolio is created and the accounts it is shared with. The role name must be set explicitly. + * @param options options for the constraint. + */ + setLocalLaunchRole(product: IProduct, launchRole: iam.IRole, options?: CommonConstraintOptions): void; + + /** + * Configure deployment options using AWS Cloudformation StackSets * * @param product A service catalog product. * @param options Configuration options for the constraint. @@ -179,6 +205,20 @@ abstract class PortfolioBase extends cdk.Resource implements IPortfolio { AssociationManager.setLaunchRole(this, product, launchRole, options); } + public setLocalLaunchRoleName(product: IProduct, launchRoleName: string, options: CommonConstraintOptions = {}): iam.IRole { + const launchRole: iam.IRole = new iam.Role(this, `LaunchRole${launchRoleName}`, { + roleName: launchRoleName, + assumedBy: new iam.ServicePrincipal('servicecatalog.amazonaws.com'), + }); + AssociationManager.setLocalLaunchRoleName(this, product, launchRole.roleName, options); + return launchRole; + } + + public setLocalLaunchRole(product: IProduct, launchRole: iam.IRole, options: CommonConstraintOptions = {}): void { + InputValidator.validateRoleNameSetForLocalLaunchRole(launchRole); + AssociationManager.setLocalLaunchRoleName(this, product, launchRole.roleName, options); + } + public deployWithStackSets(product: IProduct, options: StackSetsConstraintOptions) { AssociationManager.deployWithStackSets(this, product, options); } diff --git a/packages/@aws-cdk/aws-servicecatalog/lib/private/association-manager.ts b/packages/@aws-cdk/aws-servicecatalog/lib/private/association-manager.ts index bf5a68a8e70d3..b92fb2483ad54 100644 --- a/packages/@aws-cdk/aws-servicecatalog/lib/private/association-manager.ts +++ b/packages/@aws-cdk/aws-servicecatalog/lib/private/association-manager.ts @@ -100,27 +100,15 @@ export class AssociationManager { } public static setLaunchRole(portfolio: IPortfolio, product: IProduct, launchRole: iam.IRole, options: CommonConstraintOptions): void { - const association = this.associateProductWithPortfolio(portfolio, product, options); - // Check if a stackset deployment constraint has already been configured. - if (portfolio.node.tryFindChild(this.stackSetConstraintLogicalId(association.associationKey))) { - throw new Error(`Cannot set launch role when a StackSet rule is already defined for association ${this.prettyPrintAssociation(portfolio, product)}`); - } - - const constructId = this.launchRoleConstraintLogicalId(association.associationKey); - if (!portfolio.node.tryFindChild(constructId)) { - const constraint = new CfnLaunchRoleConstraint(portfolio as unknown as cdk.Resource, constructId, { - acceptLanguage: options.messageLanguage, - description: options.description, - portfolioId: portfolio.portfolioId, - productId: product.productId, - roleArn: launchRole.roleArn, - }); + this.setLaunchRoleConstraint(portfolio, product, options, { + roleArn: launchRole.roleArn, + }); + } - // Add dependsOn to force proper order in deployment. - constraint.addDependsOn(association.cfnPortfolioProductAssociation); - } else { - throw new Error(`Cannot set multiple launch roles for association ${this.prettyPrintAssociation(portfolio, product)}`); - } + public static setLocalLaunchRoleName(portfolio: IPortfolio, product: IProduct, launchRoleName: string, options: CommonConstraintOptions): void { + this.setLaunchRoleConstraint(portfolio, product, options, { + localRoleName: launchRoleName, + }); } public static deployWithStackSets(portfolio: IPortfolio, product: IProduct, options: StackSetsConstraintOptions) { @@ -179,6 +167,34 @@ export class AssociationManager { }; } + private static setLaunchRoleConstraint( + portfolio: IPortfolio, product: IProduct, options: CommonConstraintOptions, + roleOptions: LaunchRoleConstraintRoleOptions, + ): void { + const association = this.associateProductWithPortfolio(portfolio, product, options); + // Check if a stackset deployment constraint has already been configured. + if (portfolio.node.tryFindChild(this.stackSetConstraintLogicalId(association.associationKey))) { + throw new Error(`Cannot set launch role when a StackSet rule is already defined for association ${this.prettyPrintAssociation(portfolio, product)}`); + } + + const constructId = this.launchRoleConstraintLogicalId(association.associationKey); + if (!portfolio.node.tryFindChild(constructId)) { + const constraint = new CfnLaunchRoleConstraint(portfolio as unknown as cdk.Resource, constructId, { + acceptLanguage: options.messageLanguage, + description: options.description, + portfolioId: portfolio.portfolioId, + productId: product.productId, + roleArn: roleOptions.roleArn, + localRoleName: roleOptions.localRoleName, + }); + + // Add dependsOn to force proper order in deployment. + constraint.addDependsOn(association.cfnPortfolioProductAssociation); + } else { + throw new Error(`Cannot set multiple launch roles for association ${this.prettyPrintAssociation(portfolio, product)}`); + } + } + private static stackSetConstraintLogicalId(associationKey: string): string { return `StackSetConstraint${associationKey}`; } @@ -213,3 +229,14 @@ export class AssociationManager { }; } +interface LaunchRoleArnOption { + readonly roleArn: string, + readonly localRoleName?: never, +} + +interface LaunchRoleNameOption { + readonly localRoleName: string, + readonly roleArn?: never, +} + +type LaunchRoleConstraintRoleOptions = LaunchRoleArnOption | LaunchRoleNameOption; diff --git a/packages/@aws-cdk/aws-servicecatalog/lib/private/validation.ts b/packages/@aws-cdk/aws-servicecatalog/lib/private/validation.ts index 3beaa42552eff..cd70006fbe373 100644 --- a/packages/@aws-cdk/aws-servicecatalog/lib/private/validation.ts +++ b/packages/@aws-cdk/aws-servicecatalog/lib/private/validation.ts @@ -1,3 +1,4 @@ +import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; /** @@ -36,6 +37,17 @@ export class InputValidator { this.validateRegex(resourceName, inputName, /^[\w\d.%+\-]+@[a-z\d.\-]+\.[a-z]{2,4}$/i, inputString); } + /** + * Validates that a role being used as a local launch role has the role name set + */ + public static validateRoleNameSetForLocalLaunchRole(role: iam.IRole): void { + if (role.node.defaultChild) { + if (cdk.Token.isUnresolved((role.node.defaultChild as iam.CfnRole).roleName)) { + throw new Error(`Role ${role.node.id} used for Local Launch Role must have roleName explicitly set`); + } + } + } + private static truncateString(string: string, maxLength: number): string { if (string.length > maxLength) { return string.substring(0, maxLength) + '[truncated]'; diff --git a/packages/@aws-cdk/aws-servicecatalog/test/portfolio.test.ts b/packages/@aws-cdk/aws-servicecatalog/test/portfolio.test.ts index 8f9a27a96a940..43a283c157f80 100644 --- a/packages/@aws-cdk/aws-servicecatalog/test/portfolio.test.ts +++ b/packages/@aws-cdk/aws-servicecatalog/test/portfolio.test.ts @@ -568,6 +568,7 @@ describe('portfolio associations and product constraints', () => { assumedBy: new iam.AccountRootPrincipal(), }); launchRole = new iam.Role(stack, 'LaunchRole', { + roleName: 'LaunchRole', assumedBy: new iam.ServicePrincipal('servicecatalog.amazonaws.com'), }); }), @@ -591,6 +592,59 @@ describe('portfolio associations and product constraints', () => { }); }), + test('set a launch role constraint using local role name', () => { + portfolio.addProduct(product); + + portfolio.setLocalLaunchRoleName(product, 'LocalLaunchRole', { + description: 'set launch role description', + messageLanguage: servicecatalog.MessageLanguage.EN, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::ServiceCatalog::LaunchRoleConstraint', { + PortfolioId: { Ref: 'MyPortfolio59CCA9C9' }, + ProductId: { Ref: 'MyProduct49A3C587' }, + Description: 'set launch role description', + AcceptLanguage: 'en', + LocalRoleName: { Ref: 'MyPortfolioLaunchRoleLocalLaunchRoleB2E6E22A' }, + }); + }), + + test('set a launch role constraint using local role', () => { + portfolio.addProduct(product); + + portfolio.setLocalLaunchRole(product, launchRole, { + description: 'set launch role description', + messageLanguage: servicecatalog.MessageLanguage.EN, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::ServiceCatalog::LaunchRoleConstraint', { + PortfolioId: { Ref: 'MyPortfolio59CCA9C9' }, + ProductId: { Ref: 'MyProduct49A3C587' }, + Description: 'set launch role description', + AcceptLanguage: 'en', + LocalRoleName: { Ref: 'LaunchRole2CFB2E44' }, + }); + }), + + test('set a launch role constraint using imported local role', () => { + portfolio.addProduct(product); + + const importedLaunchRole = iam.Role.fromRoleArn(portfolio.stack, 'ImportedLaunchRole', 'arn:aws:iam::123456789012:role/ImportedLaunchRole'); + + portfolio.setLocalLaunchRole(product, importedLaunchRole, { + description: 'set launch role description', + messageLanguage: servicecatalog.MessageLanguage.EN, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::ServiceCatalog::LaunchRoleConstraint', { + PortfolioId: { Ref: 'MyPortfolio59CCA9C9' }, + ProductId: { Ref: 'MyProduct49A3C587' }, + Description: 'set launch role description', + AcceptLanguage: 'en', + LocalRoleName: 'ImportedLaunchRole', + }); + }), + test('set launch role constraint still adds without explicit association', () => { portfolio.setLaunchRole(product, launchRole); @@ -606,7 +660,57 @@ describe('portfolio associations and product constraints', () => { expect(() => { portfolio.setLaunchRole(product, otherLaunchRole); - }).toThrowError(/Cannot set multiple launch roles for association/); + }).toThrow(/Cannot set multiple launch roles for association/); + }), + + test('local launch role must have roleName explicitly set', () => { + const otherLaunchRole = new iam.Role(stack, 'otherLaunchRole', { + assumedBy: new iam.ServicePrincipal('servicecatalog.amazonaws.com'), + }); + + expect(() => { + portfolio.setLocalLaunchRole(product, otherLaunchRole); + }).toThrow(/Role otherLaunchRole used for Local Launch Role must have roleName explicitly set/); + }), + + test('fails to add multiple set launch roles - local launch role first', () => { + portfolio.setLocalLaunchRoleName(product, 'LaunchRole'); + + expect(() => { + portfolio.setLaunchRole(product, launchRole); + }).toThrow(/Cannot set multiple launch roles for association/); + }), + + test('fails to add multiple set local launch roles - local launch role first', () => { + portfolio.setLocalLaunchRoleName(product, 'LaunchRole'); + + expect(() => { + portfolio.setLocalLaunchRole(product, launchRole); + }).toThrow(/Cannot set multiple launch roles for association/); + }), + + test('fails to add multiple set local launch roles - local launch role name first', () => { + portfolio.setLocalLaunchRole(product, launchRole); + + expect(() => { + portfolio.setLocalLaunchRoleName(product, 'LaunchRole'); + }).toThrow(/Cannot set multiple launch roles for association/); + }), + + test('fails to add multiple set launch roles - local launch role second', () => { + portfolio.setLaunchRole(product, launchRole); + + expect(() => { + portfolio.setLocalLaunchRole(product, launchRole); + }).toThrow(/Cannot set multiple launch roles for association/); + }), + + test('fails to add multiple set launch roles - local launch role second', () => { + portfolio.setLaunchRole(product, launchRole); + + expect(() => { + portfolio.setLocalLaunchRoleName(product, 'LaunchRole'); + }).toThrow(/Cannot set multiple launch roles for association/); }), test('fails to set launch role if stackset rule is already defined', () => { From 6aec58d564d390b7600f469203fe0326b55e1672 Mon Sep 17 00:00:00 2001 From: kaizen3031593 <36202692+kaizen3031593@users.noreply.github.com> Date: Wed, 10 Nov 2021 04:20:47 -0500 Subject: [PATCH 10/23] chore(monocdk): ignore rosetta folder (#17439) `rosetta` folder is erroneously tracked by git. This PR adds `rosetta` to `.gitignore` and removes the folder from the repo. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/monocdk/.gitignore | 4 +- ...README-custom-resource-provider.ts-fixture | 18 ------- .../rosetta/basic-constructs.ts-fixture | 22 --------- .../rosetta/basic-portfolio.ts-fixture | 16 ------ packages/monocdk/rosetta/basic.ts-fixture | 12 ----- .../monocdk/rosetta/client-vpn.ts-fixture | 17 ------- packages/monocdk/rosetta/cluster.ts-fixture | 20 -------- packages/monocdk/rosetta/default.ts-fixture | 29 ----------- packages/monocdk/rosetta/init.ts-fixture | 3 -- .../rosetta/migrate-opensearch.ts-fixture | 16 ------ .../rosetta/portfolio-product.ts-fixture | 28 ----------- .../monocdk/rosetta/with-bucket.ts-fixture | 13 ----- .../monocdk/rosetta/with-channel.ts-fixture | 15 ------ .../monocdk/rosetta/with-cluster.ts-fixture | 19 ------- .../rosetta/with-delivery-stream.ts-fixture | 12 ----- .../rosetta/with-destination.ts-fixture | 12 ----- .../with-filesystem-instance.ts-fixture | 30 ------------ .../rosetta/with-lambda-trigger.ts-fixture | 26 ---------- .../monocdk/rosetta/with-objects.ts-fixture | 49 ------------------- packages/monocdk/rosetta/with-plan.ts-fixture | 16 ------ packages/monocdk/rosetta/with-vpc.ts-fixture | 13 ----- .../withRepoAndKinesisStream.ts-fixture | 23 --------- .../rosetta/withRepoAndSqsQueue.ts-fixture | 23 --------- .../rosetta/withRepoAndTopic.ts-fixture | 23 --------- 24 files changed, 3 insertions(+), 456 deletions(-) delete mode 100644 packages/monocdk/rosetta/README-custom-resource-provider.ts-fixture delete mode 100644 packages/monocdk/rosetta/basic-constructs.ts-fixture delete mode 100644 packages/monocdk/rosetta/basic-portfolio.ts-fixture delete mode 100644 packages/monocdk/rosetta/basic.ts-fixture delete mode 100644 packages/monocdk/rosetta/client-vpn.ts-fixture delete mode 100644 packages/monocdk/rosetta/cluster.ts-fixture delete mode 100644 packages/monocdk/rosetta/default.ts-fixture delete mode 100644 packages/monocdk/rosetta/init.ts-fixture delete mode 100644 packages/monocdk/rosetta/migrate-opensearch.ts-fixture delete mode 100644 packages/monocdk/rosetta/portfolio-product.ts-fixture delete mode 100644 packages/monocdk/rosetta/with-bucket.ts-fixture delete mode 100644 packages/monocdk/rosetta/with-channel.ts-fixture delete mode 100644 packages/monocdk/rosetta/with-cluster.ts-fixture delete mode 100644 packages/monocdk/rosetta/with-delivery-stream.ts-fixture delete mode 100644 packages/monocdk/rosetta/with-destination.ts-fixture delete mode 100644 packages/monocdk/rosetta/with-filesystem-instance.ts-fixture delete mode 100644 packages/monocdk/rosetta/with-lambda-trigger.ts-fixture delete mode 100644 packages/monocdk/rosetta/with-objects.ts-fixture delete mode 100644 packages/monocdk/rosetta/with-plan.ts-fixture delete mode 100644 packages/monocdk/rosetta/with-vpc.ts-fixture delete mode 100644 packages/monocdk/rosetta/withRepoAndKinesisStream.ts-fixture delete mode 100644 packages/monocdk/rosetta/withRepoAndSqsQueue.ts-fixture delete mode 100644 packages/monocdk/rosetta/withRepoAndTopic.ts-fixture diff --git a/packages/monocdk/.gitignore b/packages/monocdk/.gitignore index 129f2f8e0bc37..e03d056122b0f 100644 --- a/packages/monocdk/.gitignore +++ b/packages/monocdk/.gitignore @@ -16,4 +16,6 @@ dist # Ignore barrel import entry points /*.ts -junit.xml \ No newline at end of file +junit.xml + +rosetta \ No newline at end of file diff --git a/packages/monocdk/rosetta/README-custom-resource-provider.ts-fixture b/packages/monocdk/rosetta/README-custom-resource-provider.ts-fixture deleted file mode 100644 index ae4b1befd4b20..0000000000000 --- a/packages/monocdk/rosetta/README-custom-resource-provider.ts-fixture +++ /dev/null @@ -1,18 +0,0 @@ -import { CfnOutput, Construct, Token } from '@aws-cdk/core'; - -declare interface SumProps { - readonly lhs: number; - readonly rhs: number; -} -declare class Sum extends Construct { - public readonly result: number; - constructor(scope: Construct, id: string, props: SumProps); -} - -class fixture$construct extends Construct { - public constructor(scope: Construct, id: string) { - super(scope, id); - - /// here - } -} diff --git a/packages/monocdk/rosetta/basic-constructs.ts-fixture b/packages/monocdk/rosetta/basic-constructs.ts-fixture deleted file mode 100644 index 19ffd84abf486..0000000000000 --- a/packages/monocdk/rosetta/basic-constructs.ts-fixture +++ /dev/null @@ -1,22 +0,0 @@ -// Fixture with packages imported, but nothing else -import * as cdk from '@aws-cdk/core'; -import * as appreg from '@aws-cdk/aws-servicecatalogappregistry'; - -class Fixture extends cdk.Stack { - constructor(scope: Construct, id: string) { - super(scope, id); - - const application = new appreg.Application(stack, 'MyApplication', { - applicationName: 'MyApplication', - }); - - const attributeGroup = new appreg.AttributeGroup(stack, 'MyAttributeGroup', { - attributeGroupName: 'testAttributeGroup', - attributes: { - key: 'value', - }, - }); - - /// here - } -} diff --git a/packages/monocdk/rosetta/basic-portfolio.ts-fixture b/packages/monocdk/rosetta/basic-portfolio.ts-fixture deleted file mode 100644 index 3029872ea1f0d..0000000000000 --- a/packages/monocdk/rosetta/basic-portfolio.ts-fixture +++ /dev/null @@ -1,16 +0,0 @@ -// Fixture with packages imported, but nothing else -import * as cdk from '@aws-cdk/core'; -import * as servicecatalog from '@aws-cdk/aws-servicecatalog'; - -class Fixture extends cdk.Stack { - constructor(scope: cdk.Construct, id: string) { - super(scope, id); - - const portfolio = new servicecatalog.Portfolio(this, "MyFirstPortfolio", { - displayName: "MyFirstPortfolio", - providerName: "MyTeam", - }); - - /// here - } -} diff --git a/packages/monocdk/rosetta/basic.ts-fixture b/packages/monocdk/rosetta/basic.ts-fixture deleted file mode 100644 index 0cc6d1104d521..0000000000000 --- a/packages/monocdk/rosetta/basic.ts-fixture +++ /dev/null @@ -1,12 +0,0 @@ -// Fixture with packages imported, but nothing else -import { Construct } from 'constructs'; -import { Stack, Duration } from '@aws-cdk/core'; - -class Fixture extends Stack { - constructor(scope: Construct, id: string) { - super(scope, id); - - /// here - } -} - diff --git a/packages/monocdk/rosetta/client-vpn.ts-fixture b/packages/monocdk/rosetta/client-vpn.ts-fixture deleted file mode 100644 index 34c83a31ced35..0000000000000 --- a/packages/monocdk/rosetta/client-vpn.ts-fixture +++ /dev/null @@ -1,17 +0,0 @@ -// Fixture with packages imported and a VPC created -import { Construct, Stack } from '@aws-cdk/core'; -import iam = require('@aws-cdk/aws-iam'); -import ec2 = require('@aws-cdk/aws-ec2'); - -class Fixture extends Stack { - constructor(scope: Construct, id: string) { - super(scope, id); - - const vpc = new ec2.Vpc(this, 'VPC'); - const samlProvider = new iam.SamlProvider(this, 'Provider', { - metadataDocument: iam.SamlMetadataDocument.fromXml('xml'), - }) - - /// here - } -} diff --git a/packages/monocdk/rosetta/cluster.ts-fixture b/packages/monocdk/rosetta/cluster.ts-fixture deleted file mode 100644 index 82d98ca3e381e..0000000000000 --- a/packages/monocdk/rosetta/cluster.ts-fixture +++ /dev/null @@ -1,20 +0,0 @@ -// Fixture with cluster already created -import { Construct, SecretValue, Stack } from '@aws-cdk/core'; -import { Vpc } from '@aws-cdk/aws-ec2'; -import { Cluster, Table, TableAction, User } from '@aws-cdk/aws-redshift'; - -class Fixture extends Stack { - constructor(scope: Construct, id: string) { - super(scope, id); - - const vpc = new Vpc(this, 'Vpc'); - const cluster = new Cluster(this, 'Cluster', { - vpc, - masterUser: { - masterUsername: 'admin', - }, - }); - - /// here - } -} diff --git a/packages/monocdk/rosetta/default.ts-fixture b/packages/monocdk/rosetta/default.ts-fixture deleted file mode 100644 index 61a973840f007..0000000000000 --- a/packages/monocdk/rosetta/default.ts-fixture +++ /dev/null @@ -1,29 +0,0 @@ -// Fixture with packages imported, but nothing else -import { Construct, CfnOutput, Stage, Stack, StackProps, StageProps } from '@aws-cdk/core'; -import cdk = require('@aws-cdk/core'); -import codepipeline = require('@aws-cdk/aws-codepipeline'); -import cpactions = require('@aws-cdk/aws-codepipeline-actions'); -import codebuild = require('@aws-cdk/aws-codebuild'); -import codecommit = require('@aws-cdk/aws-codecommit'); -import dynamodb = require('@aws-cdk/aws-dynamodb'); -import ecr = require('@aws-cdk/aws-ecr'); -import ec2 = require('@aws-cdk/aws-ec2'); -import iam = require('@aws-cdk/aws-iam'); -import pipelines = require('@aws-cdk/pipelines'); -import secretsmanager = require('@aws-cdk/aws-secretsmanager'); -import sns = require('@aws-cdk/aws-sns'); -import subscriptions = require('@aws-cdk/aws-sns-subscriptions'); -import s3 = require('@aws-cdk/aws-s3'); - -class MyApplicationStage extends Stage { - constructor(scope: Construct, id: string, props?: StageProps) { - super(scope, id, props); - } -} - -class Fixture extends Stack { - constructor(scope: Construct, id: string) { - super(scope, id); - /// here - } -} diff --git a/packages/monocdk/rosetta/init.ts-fixture b/packages/monocdk/rosetta/init.ts-fixture deleted file mode 100644 index ce18625a2744b..0000000000000 --- a/packages/monocdk/rosetta/init.ts-fixture +++ /dev/null @@ -1,3 +0,0 @@ -import { Template } from '@aws-cdk/assertions'; - -/// here \ No newline at end of file diff --git a/packages/monocdk/rosetta/migrate-opensearch.ts-fixture b/packages/monocdk/rosetta/migrate-opensearch.ts-fixture deleted file mode 100644 index bb93c1d40f369..0000000000000 --- a/packages/monocdk/rosetta/migrate-opensearch.ts-fixture +++ /dev/null @@ -1,16 +0,0 @@ -import * as cdk from '@aws-cdk/core'; -import * as es from '@aws-cdk/aws-elasticsearch'; -import * as iam from '@aws-cdk/aws-iam'; -import * as opensearch from '@aws-cdk/aws-opensearchservice'; - -declare const role: iam.IRole; -declare const elasticsearchVersion: es.ElasticsearchVersion; -declare const openSearchVersion: opensearch.EngineVersion; - -class Fixture extends cdk.Construct { - constructor(scope: cdk.Construct, id: string) { - super(scope, id); - - /// here - } -} diff --git a/packages/monocdk/rosetta/portfolio-product.ts-fixture b/packages/monocdk/rosetta/portfolio-product.ts-fixture deleted file mode 100644 index 20a1db30bf3ee..0000000000000 --- a/packages/monocdk/rosetta/portfolio-product.ts-fixture +++ /dev/null @@ -1,28 +0,0 @@ -// Fixture with packages imported, but nothing else -import { Construct, Stack } from '@aws-cdk/core'; -import * as servicecatalog from '@aws-cdk/aws-servicecatalog'; - -class Fixture extends Stack { - constructor(scope: Construct, id: string) { - super(scope, id); - - const portfolio = new servicecatalog.Portfolio(this, "MyFirstPortfolio", { - displayName: "MyFirstPortfolio", - providerName: "MyTeam", - }); - - const product = new servicecatalog.CloudFormationProduct(this, 'MyFirstProduct', { - productName: "My Product", - owner: "Product Owner", - productVersions: [ - { - productVersionName: "v1", - cloudFormationTemplate: servicecatalog.CloudFormationTemplate.fromUrl( - 'https://raw.githubusercontent.com/awslabs/aws-cloudformation-templates/master/aws/services/ServiceCatalog/Product.yaml'), - }, - ] - }); - - /// here - } -} diff --git a/packages/monocdk/rosetta/with-bucket.ts-fixture b/packages/monocdk/rosetta/with-bucket.ts-fixture deleted file mode 100644 index d0851cff49639..0000000000000 --- a/packages/monocdk/rosetta/with-bucket.ts-fixture +++ /dev/null @@ -1,13 +0,0 @@ -// Fixture with a bucket already created -import { Construct, Stack } from '@aws-cdk/core'; -import { DeliveryStream, DestinationBindOptions, DestinationConfig, IDestination } from '@aws-cdk/aws-kinesisfirehose'; -import * as s3 from '@aws-cdk/aws-s3'; -declare const bucket: s3.Bucket; - -class Fixture extends Stack { - constructor(scope: Construct, id: string) { - super(scope, id); - - /// here - } -} diff --git a/packages/monocdk/rosetta/with-channel.ts-fixture b/packages/monocdk/rosetta/with-channel.ts-fixture deleted file mode 100644 index 44da118b81afa..0000000000000 --- a/packages/monocdk/rosetta/with-channel.ts-fixture +++ /dev/null @@ -1,15 +0,0 @@ -// Fixture with packages imported, but nothing else -import { Duration, Stack } from '@aws-cdk/core'; -import { Construct } from 'constructs'; -import * as ivs from '@aws-cdk/aws-ivs'; - -class Fixture extends Stack { - constructor(scope: Construct, id: string) { - super(scope, id); - - const myChannelArn = 'arn:aws:ivs:us-west-2:123456789012:channel/abcdABCDefgh'; - const myChannel = ivs.Channel.fromChannelArn(this, 'Channel', myChannelArn); - - /// here - } -} \ No newline at end of file diff --git a/packages/monocdk/rosetta/with-cluster.ts-fixture b/packages/monocdk/rosetta/with-cluster.ts-fixture deleted file mode 100644 index c638d8b4d04fa..0000000000000 --- a/packages/monocdk/rosetta/with-cluster.ts-fixture +++ /dev/null @@ -1,19 +0,0 @@ -import { Duration, Stack } from '@aws-cdk/core'; -import { Construct } from 'constructs'; -import * as ec2 from '@aws-cdk/aws-ec2'; -import * as neptune from '@aws-cdk/aws-neptune'; - -class Fixture extends Stack { - constructor(scope: Construct, id: string) { - super(scope, id); - - const vpc = new ec2.Vpc(this, 'VPC', { maxAzs: 2 }); - - const cluster = new neptune.DatabaseCluster(this, 'Database', { - vpc, - instanceType: neptune.InstanceType.R5_LARGE, - }); - - /// here - } -} \ No newline at end of file diff --git a/packages/monocdk/rosetta/with-delivery-stream.ts-fixture b/packages/monocdk/rosetta/with-delivery-stream.ts-fixture deleted file mode 100644 index c7b75b20d2c1b..0000000000000 --- a/packages/monocdk/rosetta/with-delivery-stream.ts-fixture +++ /dev/null @@ -1,12 +0,0 @@ -// Fixture with a delivery stream already created -import { Construct, Stack } from '@aws-cdk/core'; -import { DeliveryStream, DestinationBindOptions, DestinationConfig, IDestination } from '@aws-cdk/aws-kinesisfirehose'; -declare const deliveryStream: DeliveryStream; - -class Fixture extends Stack { - constructor(scope: Construct, id: string) { - super(scope, id); - - /// here - } -} diff --git a/packages/monocdk/rosetta/with-destination.ts-fixture b/packages/monocdk/rosetta/with-destination.ts-fixture deleted file mode 100644 index 37d78bf7a43d3..0000000000000 --- a/packages/monocdk/rosetta/with-destination.ts-fixture +++ /dev/null @@ -1,12 +0,0 @@ -// Fixture with a destination already created -import { Construct, Stack } from '@aws-cdk/core'; -import { DeliveryStream, DestinationBindOptions, DestinationConfig, IDestination } from '@aws-cdk/aws-kinesisfirehose'; -declare const destination: IDestination; - -class Fixture extends Stack { - constructor(scope: Construct, id: string) { - super(scope, id); - - /// here - } -} diff --git a/packages/monocdk/rosetta/with-filesystem-instance.ts-fixture b/packages/monocdk/rosetta/with-filesystem-instance.ts-fixture deleted file mode 100644 index 092b572afa726..0000000000000 --- a/packages/monocdk/rosetta/with-filesystem-instance.ts-fixture +++ /dev/null @@ -1,30 +0,0 @@ -// Fixture with file system and an EC2 instance created in a VPC -import { Stack } from '@aws-cdk/core'; -import { Construct } from 'constructs'; -import * as efs from '@aws-cdk/aws-efs'; -import * as ec2 from '@aws-cdk/aws-ec2'; - -class Fixture extends Stack { - constructor(scope: Construct, id: string) { - super(scope, id); - - const vpc = new ec2.Vpc(this, 'VPC'); - - const fileSystem = new efs.FileSystem(this, 'FileSystem', { - vpc, - }); - - const instance = new ec2.Instance(this, 'instance', { - instanceType: ec2.InstanceType.of(ec2.InstanceClass.T2, ec2.InstanceSize.LARGE), - machineImage: new ec2.AmazonLinuxImage({ - generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2 - }), - vpc, - vpcSubnets: { - subnetType: ec2.SubnetType.PUBLIC, - } - }); - - /// here - } -} diff --git a/packages/monocdk/rosetta/with-lambda-trigger.ts-fixture b/packages/monocdk/rosetta/with-lambda-trigger.ts-fixture deleted file mode 100644 index de9aa90eedfc2..0000000000000 --- a/packages/monocdk/rosetta/with-lambda-trigger.ts-fixture +++ /dev/null @@ -1,26 +0,0 @@ -// Fixture with packages imported, but nothing else -import { Stack } from '@aws-cdk/core'; -import { Construct } from 'constructs'; -import * as cognito from '@aws-cdk/aws-cognito'; -import * as iam from '@aws-cdk/aws-iam'; -import * as lambda from '@aws-cdk/aws-lambda'; - -class Fixture extends Stack { - constructor(scope: Construct, id: string) { - super(scope, id); - - const postAuthFn = new lambda.Function(this, 'postAuthFn', { - code: lambda.Code.fromInline('post authentication'), - runtime: lambda.Runtime.NODEJS_12_X, - handler: 'index.handler', - }); - - const userpool = new cognito.UserPool(this, 'myuserpool', { - lambdaTriggers: { - postAuthentication: postAuthFn, - }, - }); - - /// here - } -} diff --git a/packages/monocdk/rosetta/with-objects.ts-fixture b/packages/monocdk/rosetta/with-objects.ts-fixture deleted file mode 100644 index 1251aad728423..0000000000000 --- a/packages/monocdk/rosetta/with-objects.ts-fixture +++ /dev/null @@ -1,49 +0,0 @@ -// Fixture with packages imported, but nothing else -import { Construct, Stack } from '@aws-cdk/core'; -import appsync = require('@aws-cdk/aws-appsync'); -const pluralize = require('pluralize'); - -const args = { - after: appsync.GraphqlType.string(), - first: appsync.GraphqlType.int(), - before: appsync.GraphqlType.string(), - last: appsync.GraphqlType.int(), -}; - -const Node = new appsync.InterfaceType('Node', { - definition: { id: appsync.GraphqlType.string() } -}); - -const FilmNode = new appsync.ObjectType('FilmNode', { - interfaceTypes: [Node], - definition: { filmName: appsync.GraphqlType.string() } -}); - -function generateEdgeAndConnection(base: appsync.ObjectType) { - const edge = new appsync.ObjectType(`${base.name}Edge`, { - definition: { node: base.attribute(), cursor: appsync.GraphqlType.string() } - }); - const connection = new appsync.ObjectType(`${base.name}Connection`, { - definition: { - edges: edge.attribute({ isList: true }), - [pluralize(base.name)]: base.attribute({ isList: true }), - totalCount: appsync.GraphqlType.int(), - } - }); - return { edge: edge, connection: connection }; -} - -const demo = new appsync.ObjectType('Demo', { - definition: { - id: appsync.GraphqlType.string({ isRequired: true }), - version: appsync.GraphqlType.string({ isRequired: true }), - }, -}); - -class Fixture extends Stack { - constructor(scope: Construct, id: string) { - super(scope, id); - - /// here - } -} diff --git a/packages/monocdk/rosetta/with-plan.ts-fixture b/packages/monocdk/rosetta/with-plan.ts-fixture deleted file mode 100644 index 8dbfd6ac72c89..0000000000000 --- a/packages/monocdk/rosetta/with-plan.ts-fixture +++ /dev/null @@ -1,16 +0,0 @@ -// Fixture with packages imported, but nothing else -import { Duration, RemovalPolicy, Stack } from '@aws-cdk/core'; -import { Construct } from 'constructs'; -import * as backup from '@aws-cdk/aws-backup'; -import * as dynamodb from '@aws-cdk/aws-dynamodb'; -import * as events from '@aws-cdk/aws-events'; - -class Fixture extends Stack { - constructor(scope: Construct, id: string) { - super(scope, id); - - const plan = backup.BackupPlan.dailyWeeklyMonthly5YearRetention(this, 'Plan'); - - /// here - } -} diff --git a/packages/monocdk/rosetta/with-vpc.ts-fixture b/packages/monocdk/rosetta/with-vpc.ts-fixture deleted file mode 100644 index dd8e539f8cf9f..0000000000000 --- a/packages/monocdk/rosetta/with-vpc.ts-fixture +++ /dev/null @@ -1,13 +0,0 @@ -// Fixture with packages imported and a VPC created -import { Construct, Stack } from '@aws-cdk/core'; -import ec2 = require('@aws-cdk/aws-ec2'); - -class Fixture extends Stack { - constructor(scope: Construct, id: string) { - super(scope, id); - - const vpc = new ec2.Vpc(this, 'VPC'); - - /// here - } -} diff --git a/packages/monocdk/rosetta/withRepoAndKinesisStream.ts-fixture b/packages/monocdk/rosetta/withRepoAndKinesisStream.ts-fixture deleted file mode 100644 index 115e1ece7e254..0000000000000 --- a/packages/monocdk/rosetta/withRepoAndKinesisStream.ts-fixture +++ /dev/null @@ -1,23 +0,0 @@ -// Fixture with packages imported, but nothing else -import { Duration, RemovalPolicy, Stack } from '@aws-cdk/core'; -import { Construct } from 'constructs'; - -import * as targets from '@aws-cdk/aws-events-targets'; -import * as events from '@aws-cdk/aws-events'; -import * as kinesis from '@aws-cdk/aws-kinesis'; -import * as codecommit from '@aws-cdk/aws-codecommit'; -import * as cdk from '@aws-cdk/core'; - -class Fixture extends Stack { - constructor(scope: Construct, id: string) { - super(scope, id); - - const repository = new codecommit.Repository(this, 'MyRepo', { - repositoryName: 'aws-cdk-events', - }); - - const stream = new kinesis.Stream(this, 'MyStream'); - - /// here - } -} diff --git a/packages/monocdk/rosetta/withRepoAndSqsQueue.ts-fixture b/packages/monocdk/rosetta/withRepoAndSqsQueue.ts-fixture deleted file mode 100644 index 98d029d8d8283..0000000000000 --- a/packages/monocdk/rosetta/withRepoAndSqsQueue.ts-fixture +++ /dev/null @@ -1,23 +0,0 @@ -// Fixture with packages imported, but nothing else -import { Duration, RemovalPolicy, Stack } from '@aws-cdk/core'; -import { Construct } from 'constructs'; - -import * as targets from '@aws-cdk/aws-events-targets'; -import * as events from '@aws-cdk/aws-events'; -import * as sqs from '@aws-cdk/aws-sqs'; -import * as codecommit from '@aws-cdk/aws-codecommit'; -import * as cdk from '@aws-cdk/core'; - -class Fixture extends Stack { - constructor(scope: Construct, id: string) { - super(scope, id); - - const repository = new codecommit.Repository(this, 'MyRepo', { - repositoryName: 'aws-cdk-events', - }); - - const queue = new sqs.Queue(this, 'MyQueue'); - - /// here - } -} diff --git a/packages/monocdk/rosetta/withRepoAndTopic.ts-fixture b/packages/monocdk/rosetta/withRepoAndTopic.ts-fixture deleted file mode 100644 index 30c1f29cc331b..0000000000000 --- a/packages/monocdk/rosetta/withRepoAndTopic.ts-fixture +++ /dev/null @@ -1,23 +0,0 @@ -// Fixture with packages imported, but nothing else -import { Duration, RemovalPolicy, Stack } from '@aws-cdk/core'; -import { Construct } from 'constructs'; - -import * as targets from '@aws-cdk/aws-events-targets'; -import * as events from '@aws-cdk/aws-events'; -import * as sns from '@aws-cdk/aws-sns'; -import * as codecommit from '@aws-cdk/aws-codecommit'; -import * as cdk from '@aws-cdk/core'; - -class Fixture extends Stack { - constructor(scope: Construct, id: string) { - super(scope, id); - - const repository = new codecommit.Repository(this, 'MyRepo', { - repositoryName: 'aws-cdk-events', - }); - - const topic = new sns.Topic(this, 'MyTopic'); - - /// here - } -} From e374cadaefb9ac95756b5a4642dbe4494671ce13 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Wed, 10 Nov 2021 14:41:18 +0100 Subject: [PATCH 11/23] chore(core): make examples compile (#17432) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/core/README.md | 12 +++++++- packages/@aws-cdk/core/lib/assets.ts | 4 +-- packages/@aws-cdk/core/lib/bundling.ts | 6 ++-- .../@aws-cdk/core/lib/construct-compat.ts | 2 +- .../custom-resource-provider.ts | 29 ++++++++++++++----- packages/@aws-cdk/core/lib/nested-stack.ts | 4 +-- packages/@aws-cdk/core/lib/removal-policy.ts | 6 ++-- packages/@aws-cdk/core/lib/stack.ts | 10 ++++--- packages/@aws-cdk/core/package.json | 9 +++++- .../@aws-cdk/core/rosetta/default.ts-fixture | 17 +++++++++-- packages/aws-cdk-lib/README.md | 12 +++++++- 11 files changed, 85 insertions(+), 26 deletions(-) diff --git a/packages/@aws-cdk/core/README.md b/packages/@aws-cdk/core/README.md index 611467116459e..d6b58901838c2 100644 --- a/packages/@aws-cdk/core/README.md +++ b/packages/@aws-cdk/core/README.md @@ -237,6 +237,8 @@ this purpose. use the region and account of the stack you're calling it on: ```ts +declare const stack: Stack; + // Builds "arn::lambda:::function:MyFunction" stack.formatArn({ service: 'lambda', @@ -252,6 +254,8 @@ but in case of a deploy-time value be aware that the result will be another deploy-time value which cannot be inspected in the CDK application. ```ts +declare const stack: Stack; + // Extracts the function name out of an AWS Lambda Function ARN const arnComponents = stack.parseArn(arn, ':'); const functionName = arnComponents.resourceName; @@ -383,7 +387,11 @@ examples ensures that only a single SNS topic is defined: function getOrCreate(scope: Construct): sns.Topic { const stack = Stack.of(scope); const uniqueid = 'GloballyUniqueIdForSingleton'; // For example, a UUID from `uuidgen` - return stack.node.tryFindChild(uniqueid) as sns.Topic ?? new sns.Topic(stack, uniqueid); + const existing = stack.node.tryFindChild(uniqueid); + if (existing) { + return existing as sns.Topic; + } + return new sns.Topic(stack, uniqueid); } ``` @@ -816,6 +824,8 @@ since the top-level key is an unresolved token. The call to `findInMap` will ret `{ "Fn::FindInMap": [ "RegionTable", { "Ref": "AWS::Region" }, "regionName" ] }`. ```ts +declare const regionTable: CfnMapping; + regionTable.findInMap(Aws.REGION, 'regionName'); ``` diff --git a/packages/@aws-cdk/core/lib/assets.ts b/packages/@aws-cdk/core/lib/assets.ts index 7cd9b9c973f9f..3732cc4fc19b8 100644 --- a/packages/@aws-cdk/core/lib/assets.ts +++ b/packages/@aws-cdk/core/lib/assets.ts @@ -247,14 +247,14 @@ export interface FileAssetLocation { /** * The HTTP URL of this asset on Amazon S3. * - * @example https://s3-us-east-1.amazonaws.com/mybucket/myobject + * Example value: `https://s3-us-east-1.amazonaws.com/mybucket/myobject` */ readonly httpUrl: string; /** * The S3 URL of this asset on Amazon S3. * - * @example s3://mybucket/myobject + * Example value: `s3://mybucket/myobject` */ readonly s3ObjectUrl: string; diff --git a/packages/@aws-cdk/core/lib/bundling.ts b/packages/@aws-cdk/core/lib/bundling.ts index 1ce4633354511..f83a6bc6e6237 100644 --- a/packages/@aws-cdk/core/lib/bundling.ts +++ b/packages/@aws-cdk/core/lib/bundling.ts @@ -16,7 +16,7 @@ export interface BundlingOptions { /** * The entrypoint to run in the Docker container. * - * @example ['/bin/sh', '-c'] + * Example value: `['/bin/sh', '-c']` * * @see https://docs.docker.com/engine/reference/builder/#entrypoint * @@ -27,7 +27,7 @@ export interface BundlingOptions { /** * The command to run in the Docker container. * - * @example ['npm', 'install'] + * Example value: `['npm', 'install']` * * @see https://docs.docker.com/engine/reference/run/ * @@ -447,7 +447,7 @@ export interface DockerBuildOptions { /** * Set platform if server is multi-platform capable. _Requires Docker Engine API v1.38+_. * - * @example 'linux/amd64' + * Example value: `linux/amd64` * * @default - no platform specified */ diff --git a/packages/@aws-cdk/core/lib/construct-compat.ts b/packages/@aws-cdk/core/lib/construct-compat.ts index cc5921fb73465..da969b9b6c02a 100644 --- a/packages/@aws-cdk/core/lib/construct-compat.ts +++ b/packages/@aws-cdk/core/lib/construct-compat.ts @@ -343,7 +343,7 @@ export class ConstructNode { * will be excluded from the calculation. In those cases constructs in the * same tree may have the same addreess. * - * @example c83a2846e506bcc5f10682b564084bca2d275709ee + * Example value: `c83a2846e506bcc5f10682b564084bca2d275709ee` */ public get addr(): string { return this._actualNode.addr; } diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/custom-resource-provider.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/custom-resource-provider.ts index 06f257fa18ff3..832cf6774704b 100644 --- a/packages/@aws-cdk/core/lib/custom-resource-provider/custom-resource-provider.ts +++ b/packages/@aws-cdk/core/lib/custom-resource-provider/custom-resource-provider.ts @@ -36,12 +36,23 @@ export interface CustomResourceProviderProps { * A set of IAM policy statements to include in the inline policy of the * provider's lambda function. * + * **Please note**: these are direct IAM JSON policy blobs, *not* `iam.PolicyStatement` + * objects like you will see in the rest of the CDK. + * * @default - no additional inline policy * * @example - * - * [{ Effect: 'Allow', Action: 's3:PutObject*', Resource: '*' }] - * + * const provider = CustomResourceProvider.getOrCreateProvider(this, 'Custom::MyCustomResourceType', { + * codeDirectory: `${__dirname}/my-handler`, + * runtime: CustomResourceProviderRuntime.NODEJS_12_X, + * policyStatements: [ + * { + * Effect: 'Allow', + * Action: 's3:PutObject*', + * Resource: '*', + * } + * ], + * }); */ readonly policyStatements?: any[]; @@ -144,11 +155,15 @@ export class CustomResourceProvider extends CoreConstruct { * `serviceToken` when defining a custom resource. * * @example - * new CustomResource(this, 'MyCustomResource', { - * // ... - * serviceToken: myProvider.serviceToken, // <--- here - * }) + * declare const myProvider: CustomResourceProvider; * + * new CustomResource(this, 'MyCustomResource', { + * serviceToken: myProvider.serviceToken, + * properties: { + * myPropertyOne: 'one', + * myPropertyTwo: 'two', + * }, + * }); */ public readonly serviceToken: string; diff --git a/packages/@aws-cdk/core/lib/nested-stack.ts b/packages/@aws-cdk/core/lib/nested-stack.ts index fd8c47f6a0731..82a123fb9243b 100644 --- a/packages/@aws-cdk/core/lib/nested-stack.ts +++ b/packages/@aws-cdk/core/lib/nested-stack.ts @@ -155,8 +155,8 @@ export class NestedStack extends Stack { * - If this is referenced from the parent stack, it will return a token that parses the name from the stack ID. * - If this is referenced from the context of the nested stack, it will return `{ "Ref": "AWS::StackName" }` * + * Example value: `mystack-mynestedstack-sggfrhxhum7w` * @attribute - * @example mystack-mynestedstack-sggfrhxhum7w */ public get stackName() { return this._contextualStackName; @@ -169,8 +169,8 @@ export class NestedStack extends Stack { * - If this is referenced from the parent stack, it will return `{ "Ref": "LogicalIdOfNestedStackResource" }`. * - If this is referenced from the context of the nested stack, it will return `{ "Ref": "AWS::StackId" }` * + * Example value: `arn:aws:cloudformation:us-east-2:123456789012:stack/mystack-mynestedstack-sggfrhxhum7w/f449b250-b969-11e0-a185-5081d0136786` * @attribute - * @example "arn:aws:cloudformation:us-east-2:123456789012:stack/mystack-mynestedstack-sggfrhxhum7w/f449b250-b969-11e0-a185-5081d0136786" */ public get stackId() { return this._contextualStackId; diff --git a/packages/@aws-cdk/core/lib/removal-policy.ts b/packages/@aws-cdk/core/lib/removal-policy.ts index d815967fa2bf0..f5249949f99ab 100644 --- a/packages/@aws-cdk/core/lib/removal-policy.ts +++ b/packages/@aws-cdk/core/lib/removal-policy.ts @@ -19,8 +19,10 @@ * as shown in the following example: * * ```ts - * const cfnBucket = bucket.node.findChild('Resource') as cdk.CfnResource; - * cfnBucket.applyRemovalPolicy(cdk.RemovalPolicy.DESTROY); + * declare const bucket: s3.Bucket; + * + * const cfnBucket = bucket.node.findChild('Resource') as CfnResource; + * cfnBucket.applyRemovalPolicy(RemovalPolicy.DESTROY); * ``` */ export enum RemovalPolicy { diff --git a/packages/@aws-cdk/core/lib/stack.ts b/packages/@aws-cdk/core/lib/stack.ts index ee80e56334101..6645805519b81 100644 --- a/packages/@aws-cdk/core/lib/stack.ts +++ b/packages/@aws-cdk/core/lib/stack.ts @@ -280,7 +280,7 @@ export class Stack extends CoreConstruct implements ITaggable { * The name of the CloudFormation template file emitted to the output * directory during synthesis. * - * @example 'MyStack.template.json' + * Example value: `MyStack.template.json` */ public readonly templateFile: string; @@ -711,11 +711,13 @@ export class Stack extends CoreConstruct implements ITaggable { * * Duplicate values are removed when stack is synthesized. * - * @example stack.addTransform('AWS::Serverless-2016-10-31') - * * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/transform-section-structure.html - * * @param transform The transform to add + * + * @example + * declare const stack: Stack; + * + * stack.addTransform('AWS::Serverless-2016-10-31') */ public addTransform(transform: string) { if (!this.templateOptions.transforms) { diff --git a/packages/@aws-cdk/core/package.json b/packages/@aws-cdk/core/package.json index a14759ad2d345..730a7057cd1af 100644 --- a/packages/@aws-cdk/core/package.json +++ b/packages/@aws-cdk/core/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/core/rosetta/default.ts-fixture b/packages/@aws-cdk/core/rosetta/default.ts-fixture index 558cc09b1c049..26a25736acb17 100644 --- a/packages/@aws-cdk/core/rosetta/default.ts-fixture +++ b/packages/@aws-cdk/core/rosetta/default.ts-fixture @@ -27,6 +27,7 @@ import { Duration, Fn, IConstruct, + RemovalPolicy, SecretValue, Size, SizeRoundingBehavior, @@ -47,15 +48,27 @@ declare const functionProps: lambda.FunctionProps; declare const isCompleteHandler: lambda.Function; declare const myBucket: s3.IBucket; declare const myFunction: lambda.IFunction; -declare const myProvider: CustomResourceProvider; declare const myTopic: sns.ITopic; declare const onEventHandler: lambda.Function; declare const resourceProps: CfnResourceProps; -declare const stack: Stack; declare class MyStack extends Stack {} declare class YourStack extends Stack {} +class StackThatProvidesABucket extends Stack { + public readonly bucket!: s3.IBucket; +} + +interface StackThatExpectsABucketProps extends StackProps { + readonly bucket: s3.IBucket; +} + +class StackThatExpectsABucket extends Stack { + constructor(scope: Construct, id: string, props: StackThatExpectsABucketProps) { + super(scope, id, props); + } +} + class fixture$construct extends Construct { public constructor(scope: Construct, id: string) { super(scope, id); diff --git a/packages/aws-cdk-lib/README.md b/packages/aws-cdk-lib/README.md index 254a58738b26d..1de8bb1a9fb41 100644 --- a/packages/aws-cdk-lib/README.md +++ b/packages/aws-cdk-lib/README.md @@ -270,6 +270,8 @@ this purpose. use the region and account of the stack you're calling it on: ```ts +declare const stack: Stack; + // Builds "arn::lambda:::function:MyFunction" stack.formatArn({ service: 'lambda', @@ -285,6 +287,8 @@ but in case of a deploy-time value be aware that the result will be another deploy-time value which cannot be inspected in the CDK application. ```ts +declare const stack: Stack; + // Extracts the function name out of an AWS Lambda Function ARN const arnComponents = stack.parseArn(arn, ':'); const functionName = arnComponents.resourceName; @@ -416,7 +420,11 @@ examples ensures that only a single SNS topic is defined: function getOrCreate(scope: Construct): sns.Topic { const stack = Stack.of(scope); const uniqueid = 'GloballyUniqueIdForSingleton'; // For example, a UUID from `uuidgen` - return stack.node.tryFindChild(uniqueid) as sns.Topic ?? new sns.Topic(stack, uniqueid); + const existing = stack.node.tryFindChild(uniqueid); + if (existing) { + return existing as sns.Topic; + } + return new sns.Topic(stack, uniqueid); } ``` @@ -849,6 +857,8 @@ since the top-level key is an unresolved token. The call to `findInMap` will ret `{ "Fn::FindInMap": [ "RegionTable", { "Ref": "AWS::Region" }, "regionName" ] }`. ```ts +declare const regionTable: CfnMapping; + regionTable.findInMap(Aws.REGION, 'regionName'); ``` From 1afc56cd2bd0f77b6b7a3f41d95b6a7a732b14cc Mon Sep 17 00:00:00 2001 From: AWS CDK Automation <43080478+aws-cdk-automation@users.noreply.github.com> Date: Wed, 10 Nov 2021 20:03:54 +0530 Subject: [PATCH 12/23] chore: npm-check-updates && yarn upgrade (#17453) Ran npm-check-updates and yarn upgrade to keep the `yarn.lock` file up-to-date. --- packages/@aws-cdk/assets/package.json | 2 +- .../package.json | 2 +- .../aws-global-table-coordinator/package.json | 2 +- packages/@aws-cdk/core/package.json | 2 +- packages/aws-cdk/package.json | 2 +- packages/awslint/package.json | 2 +- tools/@aws-cdk/cdk-build-tools/package.json | 2 +- tools/@aws-cdk/pkglint/package.json | 2 +- yarn.lock | 78 +++++++++---------- 9 files changed, 47 insertions(+), 47 deletions(-) diff --git a/packages/@aws-cdk/assets/package.json b/packages/@aws-cdk/assets/package.json index 53d52c49cb4b4..c099d2edc4c02 100644 --- a/packages/@aws-cdk/assets/package.json +++ b/packages/@aws-cdk/assets/package.json @@ -78,7 +78,7 @@ "aws-cdk": "0.0.0", "jest": "^27.3.1", "sinon": "^9.2.4", - "ts-mock-imports": "^1.3.7" + "ts-mock-imports": "^1.3.8" }, "dependencies": { "@aws-cdk/core": "0.0.0", diff --git a/packages/@aws-cdk/aws-certificatemanager/lambda-packages/dns_validated_certificate_handler/package.json b/packages/@aws-cdk/aws-certificatemanager/lambda-packages/dns_validated_certificate_handler/package.json index 7574746f68fcb..f9bba8be5463a 100644 --- a/packages/@aws-cdk/aws-certificatemanager/lambda-packages/dns_validated_certificate_handler/package.json +++ b/packages/@aws-cdk/aws-certificatemanager/lambda-packages/dns_validated_certificate_handler/package.json @@ -36,7 +36,7 @@ "aws-sdk-mock": "^5.4.0", "eslint": "^7.32.0", "eslint-config-standard": "^14.1.1", - "eslint-plugin-import": "^2.25.2", + "eslint-plugin-import": "^2.25.3", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^4.3.1", "eslint-plugin-standard": "^4.1.0", diff --git a/packages/@aws-cdk/aws-dynamodb-global/lambda-packages/aws-global-table-coordinator/package.json b/packages/@aws-cdk/aws-dynamodb-global/lambda-packages/aws-global-table-coordinator/package.json index c3f061a93ca14..e6d555f5ae442 100644 --- a/packages/@aws-cdk/aws-dynamodb-global/lambda-packages/aws-global-table-coordinator/package.json +++ b/packages/@aws-cdk/aws-dynamodb-global/lambda-packages/aws-global-table-coordinator/package.json @@ -33,7 +33,7 @@ "aws-sdk-mock": "^5.4.0", "eslint": "^7.32.0", "eslint-config-standard": "^14.1.1", - "eslint-plugin-import": "^2.25.2", + "eslint-plugin-import": "^2.25.3", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^4.3.1", "eslint-plugin-standard": "^4.1.0", diff --git a/packages/@aws-cdk/core/package.json b/packages/@aws-cdk/core/package.json index 730a7057cd1af..abedd17448a1b 100644 --- a/packages/@aws-cdk/core/package.json +++ b/packages/@aws-cdk/core/package.json @@ -189,7 +189,7 @@ "jest": "^27.3.1", "lodash": "^4.17.21", "sinon": "^9.2.4", - "ts-mock-imports": "^1.3.7" + "ts-mock-imports": "^1.3.8" }, "dependencies": { "@aws-cdk/cloud-assembly-schema": "0.0.0", diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index 33300a5663da3..043445db7801c 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -63,7 +63,7 @@ "@aws-cdk/pkglint": "0.0.0", "sinon": "^9.2.4", "ts-jest": "^27.0.7", - "ts-mock-imports": "^1.3.7", + "ts-mock-imports": "^1.3.8", "xml-js": "^1.6.11" }, "dependencies": { diff --git a/packages/awslint/package.json b/packages/awslint/package.json index 4ee4e93482402..d06849b47485a 100644 --- a/packages/awslint/package.json +++ b/packages/awslint/package.json @@ -37,7 +37,7 @@ "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^2.5.0", "@aws-cdk/eslint-plugin": "0.0.0", - "eslint-plugin-import": "^2.25.2", + "eslint-plugin-import": "^2.25.3", "eslint-plugin-jest": "^24.7.0", "jest": "^27.3.1" }, diff --git a/tools/@aws-cdk/cdk-build-tools/package.json b/tools/@aws-cdk/cdk-build-tools/package.json index d1e47434baced..5c5fdb620d894 100644 --- a/tools/@aws-cdk/cdk-build-tools/package.json +++ b/tools/@aws-cdk/cdk-build-tools/package.json @@ -51,7 +51,7 @@ "eslint": "^7.32.0", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^2.5.0", - "eslint-plugin-import": "^2.25.2", + "eslint-plugin-import": "^2.25.3", "eslint-plugin-jest": "^24.7.0", "fs-extra": "^9.1.0", "jest": "^27.3.1", diff --git a/tools/@aws-cdk/pkglint/package.json b/tools/@aws-cdk/pkglint/package.json index c9725365adfc6..7bb152feb4287 100644 --- a/tools/@aws-cdk/pkglint/package.json +++ b/tools/@aws-cdk/pkglint/package.json @@ -47,7 +47,7 @@ "@typescript-eslint/parser": "^4.33.0", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^2.5.0", - "eslint-plugin-import": "^2.25.2", + "eslint-plugin-import": "^2.25.3", "eslint-plugin-jest": "^24.7.0", "eslint": "^7.32.0", "jest": "^27.3.1", diff --git a/yarn.lock b/yarn.lock index 0f851ebe5ab5b..f4312ed26d658 100644 --- a/yarn.lock +++ b/yarn.lock @@ -75,13 +75,13 @@ source-map "^0.5.0" "@babel/helper-compilation-targets@^7.16.0": - version "7.16.0" - resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.0.tgz#01d615762e796c17952c29e3ede9d6de07d235a8" - integrity sha512-S7iaOT1SYlqK0sQaCi21RX4+13hmdmnxIEAnQUB/eh7GeAnRjOUgTYpLkUOiRXzD+yog1JxP0qyAQZ7ZxVxLVg== + version "7.16.3" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.3.tgz#5b480cd13f68363df6ec4dc8ac8e2da11363cbf0" + integrity sha512-vKsoSQAyBmxS35JUOOt+07cLc6Nk/2ljLIHwmq2/NM6hdioUaqEXq/S+nXvbvXbZkNDlWOymPanJGOc4CBjSJA== dependencies: "@babel/compat-data" "^7.16.0" "@babel/helper-validator-option" "^7.14.5" - browserslist "^4.16.6" + browserslist "^4.17.5" semver "^6.3.0" "@babel/helper-function-name@^7.16.0": @@ -182,12 +182,12 @@ integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== "@babel/helpers@^7.16.0": - version "7.16.0" - resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.0.tgz#875519c979c232f41adfbd43a3b0398c2e388183" - integrity sha512-dVRM0StFMdKlkt7cVcGgwD8UMaBfWJHl3A83Yfs8GQ3MO0LHIIIMvK7Fa0RGOGUQ10qikLaX6D7o5htcQWgTMQ== + version "7.16.3" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.3.tgz#27fc64f40b996e7074dc73128c3e5c3e7f55c43c" + integrity sha512-Xn8IhDlBPhvYTvgewPKawhADichOsbkZuzN7qz2BusOM0brChsyXMDJvldWaYMMUNiCQdQzNEioXTp3sC8Nt8w== dependencies: "@babel/template" "^7.16.0" - "@babel/traverse" "^7.16.0" + "@babel/traverse" "^7.16.3" "@babel/types" "^7.16.0" "@babel/highlight@^7.10.4", "@babel/highlight@^7.16.0": @@ -199,10 +199,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.0", "@babel/parser@^7.7.2": - version "7.16.2" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.16.2.tgz#3723cd5c8d8773eef96ce57ea1d9b7faaccd12ac" - integrity sha512-RUVpT0G2h6rOZwqLDTrKk7ksNv7YpAilTnYe1/Q+eDjxEceRMKVWbCsX7t8h6C1qCFi/1Y8WZjcEPBAFG27GPw== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.0", "@babel/parser@^7.16.3", "@babel/parser@^7.7.2": + version "7.16.3" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.16.3.tgz#271bafcb811080905a119222edbc17909c82261d" + integrity sha512-dcNwU1O4sx57ClvLBVFbEgx0UZWfd0JQX5X6fxFRCLHelFBGXFfSz6Y0FAq2PEwUqlqLkdVjVr4VASEOuUnLJw== "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -304,17 +304,17 @@ "@babel/parser" "^7.16.0" "@babel/types" "^7.16.0" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.16.0", "@babel/traverse@^7.7.2": - version "7.16.0" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.0.tgz#965df6c6bfc0a958c1e739284d3c9fa4a6e3c45b" - integrity sha512-qQ84jIs1aRQxaGaxSysII9TuDaguZ5yVrEuC0BN2vcPlalwfLovVmCjbFDPECPXcYM/wLvNFfp8uDOliLxIoUQ== +"@babel/traverse@^7.1.0", "@babel/traverse@^7.16.0", "@babel/traverse@^7.16.3", "@babel/traverse@^7.7.2": + version "7.16.3" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.3.tgz#f63e8a938cc1b780f66d9ed3c54f532ca2d14787" + integrity sha512-eolumr1vVMjqevCpwVO99yN/LoGL0EyHiLO5I043aYQvwOJ9eR5UsZSClHVCzfhBduMAsSzgA/6AyqPjNayJag== dependencies: "@babel/code-frame" "^7.16.0" "@babel/generator" "^7.16.0" "@babel/helper-function-name" "^7.16.0" "@babel/helper-hoist-variables" "^7.16.0" "@babel/helper-split-export-declaration" "^7.16.0" - "@babel/parser" "^7.16.0" + "@babel/parser" "^7.16.3" "@babel/types" "^7.16.0" debug "^4.1.0" globals "^11.1.0" @@ -2293,9 +2293,9 @@ aws-sdk-mock@^5.4.0: traverse "^0.6.6" aws-sdk@^2.596.0, aws-sdk@^2.848.0, aws-sdk@^2.928.0, aws-sdk@^2.979.0: - version "2.1024.0" - resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1024.0.tgz#c69ae613a546e56643c921d4aae36d7f8d6f44bc" - integrity sha512-FgGvRtxTzgU7iBXG/+hCGqdE2U2gF/NqVDQsTBjrLIbOMiNNheL8uzxcmIKKZ49lFlWwVkM0HNmpEmA5hiLinw== + version "2.1025.0" + resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1025.0.tgz#566f62efc80d6d4cc6e893774a2f15ac774a93fc" + integrity sha512-1AR2xIHcbIWj5y3fh9JHd2fLgiGqpn9Ww+8y9kZDnrsIousJkR6L+QkG0mRhChu/AjpFVQ44fiTBoE4J88Dqyw== dependencies: buffer "4.9.2" events "1.1.1" @@ -2446,7 +2446,7 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browserslist@^4.16.6: +browserslist@^4.17.5: version "4.17.6" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.17.6.tgz#c76be33e7786b497f66cad25a73756c8b938985d" integrity sha512-uPgz3vyRTlEiCv4ee9KlsKgo2V6qPk7Jsn0KAn2OBqbqKo3iNcPEC1Ti6J4dwnz+aIRfEEEuOzC9IBk8tXUomw== @@ -3506,9 +3506,9 @@ ecc-jsbn@~0.1.1: safer-buffer "^2.1.0" electron-to-chromium@^1.3.886: - version "1.3.892" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.892.tgz#0e3f5bb1de577e2e5a6dffd5a4b278c4a735cd39" - integrity sha512-YDW4yIjdfMnbRoBjRZ/aNQYmT6JgQFLwmTSDRJMQdrY4MByEzppdXp3rnJ0g4LBWcsYTUvwKKClYN1ofZ0COOQ== + version "1.3.893" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.893.tgz#9d804c68953b05ede35409dba0d73dd54c077b4d" + integrity sha512-ChtwF7qB03INq1SyMpue08wc6cve+ktj2UC/Y7se9vB+JryfzziJeYwsgb8jLaCA5GMkHCdn5M62PfSMWhifZg== emittery@^0.8.1: version "0.8.1" @@ -3808,7 +3808,7 @@ eslint-import-resolver-typescript@^2.5.0: resolve "^1.20.0" tsconfig-paths "^3.9.0" -eslint-module-utils@^2.7.0: +eslint-module-utils@^2.7.1: version "2.7.1" resolved "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.1.tgz#b435001c9f8dd4ab7f6d0efcae4b9696d4c24b7c" integrity sha512-fjoetBXQZq2tSTWZ9yWVl2KuFrTZZH3V+9iD1V1RfpDgxzJR+mPd/KZmMiA8gbPqdBzpNiEHOuT7IYEWxrH0zQ== @@ -3825,19 +3825,19 @@ eslint-plugin-es@^3.0.0: eslint-utils "^2.0.0" regexpp "^3.0.0" -eslint-plugin-import@^2.25.2: - version "2.25.2" - resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.2.tgz#b3b9160efddb702fc1636659e71ba1d10adbe9e9" - integrity sha512-qCwQr9TYfoBHOFcVGKY9C9unq05uOxxdklmBXLVvcwo68y5Hta6/GzCZEMx2zQiu0woKNEER0LE7ZgaOfBU14g== +eslint-plugin-import@^2.25.3: + version "2.25.3" + resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.3.tgz#a554b5f66e08fb4f6dc99221866e57cfff824766" + integrity sha512-RzAVbby+72IB3iOEL8clzPLzL3wpDrlwjsTBAQXgyp5SeTqqY+0bFubwuo+y/HLhNZcXV4XqTBO4LGsfyHIDXg== dependencies: array-includes "^3.1.4" array.prototype.flat "^1.2.5" debug "^2.6.9" doctrine "^2.1.0" eslint-import-resolver-node "^0.3.6" - eslint-module-utils "^2.7.0" + eslint-module-utils "^2.7.1" has "^1.0.3" - is-core-module "^2.7.0" + is-core-module "^2.8.0" is-glob "^4.0.3" minimatch "^3.0.4" object.values "^1.1.5" @@ -4246,9 +4246,9 @@ flatted@^2.0.1: integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== flatted@^3.1.0: - version "3.2.2" - resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz#64bfed5cb68fe3ca78b3eb214ad97b63bedce561" - integrity sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA== + version "3.2.4" + resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz#28d9969ea90661b5134259f312ab6aa7929ac5e2" + integrity sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw== follow-redirects@^1.11.0, follow-redirects@^1.14.0: version "1.14.5" @@ -4940,7 +4940,7 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" -is-core-module@^2.2.0, is-core-module@^2.5.0, is-core-module@^2.7.0: +is-core-module@^2.2.0, is-core-module@^2.5.0, is-core-module@^2.8.0: version "2.8.0" resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz#0321336c3d0925e497fd97f5d95cb114a5ccd548" integrity sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw== @@ -8713,10 +8713,10 @@ ts-jest@^27.0.7: semver "7.x" yargs-parser "20.x" -ts-mock-imports@^1.3.7: - version "1.3.7" - resolved "https://registry.npmjs.org/ts-mock-imports/-/ts-mock-imports-1.3.7.tgz#8c3210a641f40fd5cadbd1c9c88574b51df59bde" - integrity sha512-zy4B3QSGaOhjaX9j0h9YKwM1oHG4Kd1KIUJBeXlXIQrFnATNLgh4+NyRcaAHsPeqwe3TWeRtHXkNXPxySEKk3w== +ts-mock-imports@^1.3.8: + version "1.3.8" + resolved "https://registry.npmjs.org/ts-mock-imports/-/ts-mock-imports-1.3.8.tgz#6b26887c651effe947ea91f928338d1899146fb9" + integrity sha512-A5n0iEg4zh2/qToo54XOa/wT31fAI0B8DHYU4RDcA6HIddZQPRkTsYri3Hl69+OSLjOKWjyP3/vYOIp3SAIZXg== ts-node@^10.2.1: version "10.4.0" From 1d30d829b62af066f51a4447a4b528a843e78e2e Mon Sep 17 00:00:00 2001 From: kaizen3031593 <36202692+kaizen3031593@users.noreply.github.com> Date: Wed, 10 Nov 2021 12:33:53 -0500 Subject: [PATCH 13/23] chore(ubergen): make rosetta fixtures submodule aware (#17438) Currently ubergen bundles all fixtures into the same `rosetta` folder. This means that all fixtures in files called `default.ts-fixture` replace each other as they are found and vie to be the last `default.ts-fixture` standing in the `rosetta` folder. This causes unintended compiliation failures for `yarn rosetta:extract --compile` in projects that rely on ubergen, since they will be copied into the wrong `default.ts-fixture`. The solution here in jsii is to find fixtures based on submodules: https://github.com/aws/jsii/pull/3131. This PR updates ubergen to construct the necessary subfolders in `rosetta` so that the expected submodule fixtures can be found. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- tools/@aws-cdk/ubergen/bin/ubergen.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/@aws-cdk/ubergen/bin/ubergen.ts b/tools/@aws-cdk/ubergen/bin/ubergen.ts index 772e6b358b403..c32a6a431de17 100644 --- a/tools/@aws-cdk/ubergen/bin/ubergen.ts +++ b/tools/@aws-cdk/ubergen/bin/ubergen.ts @@ -254,8 +254,9 @@ async function combineRosettaFixtures(libraries: readonly LibraryReference[]) { for (const library of libraries) { const packageRosettaDir = path.join(library.root, 'rosetta'); + const uberRosettaTargetDir = library.shortName === 'core' ? uberRosettaDir : path.join(uberRosettaDir, library.shortName.replace(/-/g, '_')); if (await fs.pathExists(packageRosettaDir)) { - await fs.copy(packageRosettaDir, uberRosettaDir, { + await fs.copy(packageRosettaDir, uberRosettaTargetDir, { overwrite: true, recursive: true, }); From af61b7f2fec17d4f817e78db21d09d471d8e2baf Mon Sep 17 00:00:00 2001 From: Jonathan Goldwasser Date: Wed, 10 Nov 2021 21:15:36 +0100 Subject: [PATCH 14/23] fix(cli): skip bundling for the 'watch' command (#17455) Add `watch` to the list of commands that require bundling. Closes #17391 ---- *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/settings.ts | 4 +++- packages/aws-cdk/test/settings.test.ts | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/packages/aws-cdk/lib/settings.ts b/packages/aws-cdk/lib/settings.ts index 6182ecc26e0c3..38723bffc0bc3 100644 --- a/packages/aws-cdk/lib/settings.ts +++ b/packages/aws-cdk/lib/settings.ts @@ -30,6 +30,7 @@ export enum Command { METADATA = 'metadata', INIT = 'init', VERSION = 'version', + WATCH = 'watch', } const BUNDLING_COMMANDS = [ @@ -37,6 +38,7 @@ const BUNDLING_COMMANDS = [ Command.DIFF, Command.SYNTH, Command.SYNTHESIZE, + Command.WATCH, ]; export type Arguments = { @@ -251,7 +253,7 @@ export class Settings { // Determine bundling stacks let bundlingStacks: string[]; if (BUNDLING_COMMANDS.includes(argv._[0])) { - // If we deploy, diff or synth a list of stacks exclusively we skip + // If we deploy, diff, synth or watch a list of stacks exclusively we skip // bundling for all other stacks. bundlingStacks = argv.exclusively ? argv.STACKS ?? ['*'] diff --git a/packages/aws-cdk/test/settings.test.ts b/packages/aws-cdk/test/settings.test.ts index 0f283b386006b..aef16e6bac946 100644 --- a/packages/aws-cdk/test/settings.test.ts +++ b/packages/aws-cdk/test/settings.test.ts @@ -100,6 +100,16 @@ test('bundling stacks defaults to * for deploy', () => { expect(settings.get(['bundlingStacks'])).toEqual(['*']); }); +test('bundling stacks defaults to * for watch', () => { + // GIVEN + const settings = Settings.fromCommandLineArguments({ + _: [Command.WATCH], + }); + + // THEN + expect(settings.get(['bundlingStacks'])).toEqual(['*']); +}); + test('bundling stacks with deploy exclusively', () => { // GIVEN const settings = Settings.fromCommandLineArguments({ @@ -112,6 +122,18 @@ test('bundling stacks with deploy exclusively', () => { expect(settings.get(['bundlingStacks'])).toEqual(['cool-stack']); }); +test('bundling stacks with watch exclusively', () => { + // GIVEN + const settings = Settings.fromCommandLineArguments({ + _: [Command.WATCH], + exclusively: true, + STACKS: ['cool-stack'], + }); + + // THEN + expect(settings.get(['bundlingStacks'])).toEqual(['cool-stack']); +}); + test('should include outputs-file in settings', () => { // GIVEN const settings = Settings.fromCommandLineArguments({ From 3570b172510c395fd1308c552442e11abc9c4124 Mon Sep 17 00:00:00 2001 From: Tatsuya Yamamoto Date: Thu, 11 Nov 2021 06:00:50 +0900 Subject: [PATCH 15/23] chore(iot-actions): clarify the test difference (#17451) This PR refactor the test that I committed earlier, and is based on the following comment. - https://github.com/aws/aws-cdk/pull/17307#discussion_r745890426 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../cloudwatch-logs-action.test.ts | 24 ++----------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/cloudwatch-logs-action.test.ts b/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/cloudwatch-logs-action.test.ts index 3b6ecd2d57fbc..4499cdd35d6f1 100644 --- a/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/cloudwatch-logs-action.test.ts +++ b/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/cloudwatch-logs-action.test.ts @@ -1,4 +1,4 @@ -import { Template } from '@aws-cdk/assertions'; +import { Template, Match } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import * as iot from '@aws-cdk/aws-iot'; import * as logs from '@aws-cdk/aws-logs'; @@ -95,32 +95,12 @@ test('can set role', () => { Template.fromStack(stack).hasResourceProperties('AWS::IoT::TopicRule', { TopicRulePayload: { Actions: [ - { - CloudwatchLogs: { - LogGroupName: 'my-log-group', - RoleArn: 'arn:aws:iam::123456789012:role/ForTest', - }, - }, + Match.objectLike({ CloudwatchLogs: { RoleArn: 'arn:aws:iam::123456789012:role/ForTest' } }), ], }, }); Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { - PolicyDocument: { - Statement: [ - { - Action: ['logs:CreateLogStream', 'logs:PutLogEvents'], - Effect: 'Allow', - Resource: 'arn:aws:logs:us-east-1:123456789012:log-group:my-log-group:*', - }, - { - Action: 'logs:DescribeLogStreams', - Effect: 'Allow', - Resource: 'arn:aws:logs:us-east-1:123456789012:log-group:my-log-group:*', - }, - ], - Version: '2012-10-17', - }, PolicyName: 'MyRolePolicy64AB00A5', Roles: ['ForTest'], }); From 49b87dbfe5a37abad8880e0325f7aa8218705407 Mon Sep 17 00:00:00 2001 From: Tatsuya Yamamoto Date: Thu, 11 Nov 2021 06:46:51 +0900 Subject: [PATCH 16/23] feat(iot): add Action to put objects in S3 Buckets (#17307) I'm trying to implement aws-iot L2 Constructs. This PR is one of steps after following PR: - https://github.com/aws/aws-cdk/pull/16681#issuecomment-942233029 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- package.json | 4 + packages/@aws-cdk/aws-iot-actions/NOTICE | 30 ++++ packages/@aws-cdk/aws-iot-actions/README.md | 55 +++++++ .../lib/cloudwatch-logs-action.ts | 9 +- .../lib/common-action-props.ts | 13 ++ .../@aws-cdk/aws-iot-actions/lib/index.ts | 2 + .../lib/s3-put-object-action.ts | 67 ++++++++ .../@aws-cdk/aws-iot-actions/package.json | 6 + .../integ.s3-put-object-action.expected.json | 86 ++++++++++ .../integ.s3-put-object-action.ts | 32 ++++ .../s3-put-object-action.test.ts | 148 ++++++++++++++++++ 11 files changed, 445 insertions(+), 7 deletions(-) create mode 100644 packages/@aws-cdk/aws-iot-actions/lib/common-action-props.ts create mode 100644 packages/@aws-cdk/aws-iot-actions/lib/s3-put-object-action.ts create mode 100644 packages/@aws-cdk/aws-iot-actions/test/s3-put-object/integ.s3-put-object-action.expected.json create mode 100644 packages/@aws-cdk/aws-iot-actions/test/s3-put-object/integ.s3-put-object-action.ts create mode 100644 packages/@aws-cdk/aws-iot-actions/test/s3-put-object/s3-put-object-action.test.ts diff --git a/package.json b/package.json index aea497b0788f1..2dcf60dfa02c0 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,8 @@ "@aws-cdk/assertions/fs-extra/**", "@aws-cdk/aws-amplify-alpha/yaml", "@aws-cdk/aws-amplify-alpha/yaml/**", + "@aws-cdk/aws-iot-actions-alpha/case", + "@aws-cdk/aws-iot-actions-alpha/case/**", "@aws-cdk/aws-amplify/yaml", "@aws-cdk/aws-amplify/yaml/**", "@aws-cdk/aws-codebuild/yaml", @@ -91,6 +93,8 @@ "@aws-cdk/aws-eks/yaml/**", "@aws-cdk/aws-events-targets/aws-sdk", "@aws-cdk/aws-events-targets/aws-sdk/**", + "@aws-cdk/aws-iot-actions/case", + "@aws-cdk/aws-iot-actions/case/**", "@aws-cdk/aws-s3-deployment/case", "@aws-cdk/aws-s3-deployment/case/**", "@aws-cdk/cloud-assembly-schema/jsonschema", diff --git a/packages/@aws-cdk/aws-iot-actions/NOTICE b/packages/@aws-cdk/aws-iot-actions/NOTICE index 5fc3826926b5b..39cd25bf899ae 100644 --- a/packages/@aws-cdk/aws-iot-actions/NOTICE +++ b/packages/@aws-cdk/aws-iot-actions/NOTICE @@ -1,2 +1,32 @@ AWS Cloud Development Kit (AWS CDK) Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +------------------------------------------------------------------------------- + +The AWS CDK includes the following third-party software/licensing: + +** case - https://www.npmjs.com/package/case +Copyright (c) 2013 Nathan Bubna + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +---------------- diff --git a/packages/@aws-cdk/aws-iot-actions/README.md b/packages/@aws-cdk/aws-iot-actions/README.md index b18182a80a9ad..f9043d86bd447 100644 --- a/packages/@aws-cdk/aws-iot-actions/README.md +++ b/packages/@aws-cdk/aws-iot-actions/README.md @@ -22,6 +22,8 @@ supported AWS Services. Instances of these classes should be passed to Currently supported are: - Invoke a Lambda function +- Put objects to a S3 bucket +- Put logs to CloudWatch Logs ## Invoke a Lambda function @@ -49,6 +51,59 @@ new iot.TopicRule(this, 'TopicRule', { }); ``` +## Put objects to a S3 bucket + +The code snippet below creates an AWS IoT Rule that put objects to a S3 bucket +when it is triggered. + +```ts +import * as iot from '@aws-cdk/aws-iot'; +import * as actions from '@aws-cdk/aws-iot-actions'; +import * as s3 from '@aws-cdk/aws-s3'; + +const bucket = new s3.Bucket(this, 'MyBucket'); + +new iot.TopicRule(this, 'TopicRule', { + sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id FROM 'device/+/data'"), + actions: [new actions.S3PutObjectAction(bucket)], +}); +``` + +The property `key` of `S3PutObjectAction` is given the value `${topic()}/${timestamp()}` by default. This `${topic()}` +and `${timestamp()}` is called Substitution templates. For more information see +[this documentation](https://docs.aws.amazon.com/iot/latest/developerguide/iot-substitution-templates.html). +In above sample, `${topic()}` is replaced by a given MQTT topic as `device/001/data`. And `${timestamp()}` is replaced +by the number of the current timestamp in milliseconds as `1636289461203`. So if the MQTT broker receives an MQTT topic +`device/001/data` on `2021-11-07T00:00:00.000Z`, the S3 bucket object will be put to `device/001/data/1636243200000`. + +You can also set specific `key` as following: + +```ts +new iot.TopicRule(this, 'TopicRule', { + sql: iot.IotSql.fromStringAsVer20160323( + "SELECT topic(2) as device_id, year, month, day FROM 'device/+/data'", + ), + actions: [ + new actions.S3PutObjectAction(bucket, { + key: '${year}/${month}/${day}/${topic(2)}', + }), + ], +}); +``` + +If you wanna set access control to the S3 bucket object, you can specify `accessControl` as following: + +```ts +new iot.TopicRule(this, 'TopicRule', { + sql: iot.IotSql.fromStringAsVer20160323("SELECT * FROM 'device/+/data'"), + actions: [ + new actions.S3PutObjectAction(bucket, { + accessControl: s3.BucketAccessControl.PUBLIC_READ, + }), + ], +}); +``` + ## Put logs to CloudWatch Logs The code snippet below creates an AWS IoT Rule that put logs to CloudWatch Logs diff --git a/packages/@aws-cdk/aws-iot-actions/lib/cloudwatch-logs-action.ts b/packages/@aws-cdk/aws-iot-actions/lib/cloudwatch-logs-action.ts index dda14de887774..fb8f2779f32e7 100644 --- a/packages/@aws-cdk/aws-iot-actions/lib/cloudwatch-logs-action.ts +++ b/packages/@aws-cdk/aws-iot-actions/lib/cloudwatch-logs-action.ts @@ -1,18 +1,13 @@ import * as iam from '@aws-cdk/aws-iam'; import * as iot from '@aws-cdk/aws-iot'; import * as logs from '@aws-cdk/aws-logs'; +import { CommonActionProps } from './common-action-props'; import { singletonActionRole } from './private/role'; /** * Configuration properties of an action for CloudWatch Logs. */ -export interface CloudWatchLogsActionProps { - /** - * The IAM role that allows access to the CloudWatch log group. - * - * @default a new role will be created - */ - readonly role?: iam.IRole; +export interface CloudWatchLogsActionProps extends CommonActionProps { } /** diff --git a/packages/@aws-cdk/aws-iot-actions/lib/common-action-props.ts b/packages/@aws-cdk/aws-iot-actions/lib/common-action-props.ts new file mode 100644 index 0000000000000..5a9b52d8b5f27 --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions/lib/common-action-props.ts @@ -0,0 +1,13 @@ +import * as iam from '@aws-cdk/aws-iam'; + +/** + * Common properties shared by Actions it access to AWS service. + */ +export interface CommonActionProps { + /** + * The IAM role that allows access to AWS service. + * + * @default a new role will be created + */ + readonly role?: iam.IRole; +} diff --git a/packages/@aws-cdk/aws-iot-actions/lib/index.ts b/packages/@aws-cdk/aws-iot-actions/lib/index.ts index ef917fd0e2181..88521265228d4 100644 --- a/packages/@aws-cdk/aws-iot-actions/lib/index.ts +++ b/packages/@aws-cdk/aws-iot-actions/lib/index.ts @@ -1,2 +1,4 @@ export * from './cloudwatch-logs-action'; +export * from './common-action-props'; export * from './lambda-function-action'; +export * from './s3-put-object-action'; diff --git a/packages/@aws-cdk/aws-iot-actions/lib/s3-put-object-action.ts b/packages/@aws-cdk/aws-iot-actions/lib/s3-put-object-action.ts new file mode 100644 index 0000000000000..f690bf813a922 --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions/lib/s3-put-object-action.ts @@ -0,0 +1,67 @@ +import * as iam from '@aws-cdk/aws-iam'; +import * as iot from '@aws-cdk/aws-iot'; +import * as s3 from '@aws-cdk/aws-s3'; +import { kebab as toKebabCase } from 'case'; +import { CommonActionProps } from './common-action-props'; +import { singletonActionRole } from './private/role'; + +/** + * Configuration properties of an action for s3. + */ +export interface S3PutObjectActionProps extends CommonActionProps { + /** + * The Amazon S3 canned ACL that controls access to the object identified by the object key. + * @see https://docs.aws.amazon.com/AmazonS3/latest/userguide/acl-overview.html#canned-acl + * + * @default None + */ + readonly accessControl?: s3.BucketAccessControl; + + /** + * The path to the file where the data is written. + * + * Supports substitution templates. + * @see https://docs.aws.amazon.com/iot/latest/developerguide/iot-substitution-templates.html + * + * @default '${topic()}/${timestamp()}' + */ + readonly key?: string; +} + +/** + * The action to write the data from an MQTT message to an Amazon S3 bucket. + */ +export class S3PutObjectAction implements iot.IAction { + private readonly accessControl?: string; + private readonly key?: string; + private readonly role?: iam.IRole; + + /** + * @param bucket The Amazon S3 bucket to which to write data. + * @param props Optional properties to not use default + */ + constructor(private readonly bucket: s3.IBucket, props: S3PutObjectActionProps = {}) { + this.accessControl = props.accessControl; + this.key = props.key; + this.role = props.role; + } + + bind(rule: iot.ITopicRule): iot.ActionConfig { + const role = this.role ?? singletonActionRole(rule); + role.addToPrincipalPolicy(new iam.PolicyStatement({ + actions: ['s3:PutObject'], + resources: [this.bucket.arnForObjects('*')], + })); + + return { + configuration: { + s3: { + bucketName: this.bucket.bucketName, + cannedAcl: this.accessControl && toKebabCase(this.accessControl.toString()), + key: this.key ?? '${topic()}/${timestamp()}', + roleArn: role.roleArn, + }, + }, + }; + } +} diff --git a/packages/@aws-cdk/aws-iot-actions/package.json b/packages/@aws-cdk/aws-iot-actions/package.json index fb40db84577c3..ca5ca2bf1b1f5 100644 --- a/packages/@aws-cdk/aws-iot-actions/package.json +++ b/packages/@aws-cdk/aws-iot-actions/package.json @@ -83,7 +83,9 @@ "@aws-cdk/aws-iot": "0.0.0", "@aws-cdk/aws-lambda": "0.0.0", "@aws-cdk/aws-logs": "0.0.0", + "@aws-cdk/aws-s3": "0.0.0", "@aws-cdk/core": "0.0.0", + "case": "1.6.3", "constructs": "^3.3.69" }, "homepage": "https://github.com/aws/aws-cdk", @@ -92,9 +94,13 @@ "@aws-cdk/aws-iot": "0.0.0", "@aws-cdk/aws-lambda": "0.0.0", "@aws-cdk/aws-logs": "0.0.0", + "@aws-cdk/aws-s3": "0.0.0", "@aws-cdk/core": "0.0.0", "constructs": "^3.3.69" }, + "bundledDependencies": [ + "case" + ], "engines": { "node": ">= 10.13.0 <13 || >=13.7.0" }, diff --git a/packages/@aws-cdk/aws-iot-actions/test/s3-put-object/integ.s3-put-object-action.expected.json b/packages/@aws-cdk/aws-iot-actions/test/s3-put-object/integ.s3-put-object-action.expected.json new file mode 100644 index 0000000000000..4e530f04da2c1 --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions/test/s3-put-object/integ.s3-put-object-action.expected.json @@ -0,0 +1,86 @@ +{ + "Resources": { + "TopicRule40A4EA44": { + "Type": "AWS::IoT::TopicRule", + "Properties": { + "TopicRulePayload": { + "Actions": [ + { + "S3": { + "BucketName": { + "Ref": "MyBucketF68F3FF0" + }, + "CannedAcl": "bucket-owner-full-control", + "Key": "${year}/${month}/${day}/${topic(2)}", + "RoleArn": { + "Fn::GetAtt": [ + "TopicRuleTopicRuleActionRole246C4F77", + "Arn" + ] + } + } + } + ], + "AwsIotSqlVersion": "2016-03-23", + "Sql": "SELECT topic(2) as device_id, year, month, day FROM 'device/+/data'" + } + } + }, + "TopicRuleTopicRuleActionRole246C4F77": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "iot.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "TopicRuleTopicRuleActionRoleDefaultPolicy99ADD687": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:PutObject", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "TopicRuleTopicRuleActionRoleDefaultPolicy99ADD687", + "Roles": [ + { + "Ref": "TopicRuleTopicRuleActionRole246C4F77" + } + ] + } + }, + "MyBucketF68F3FF0": { + "Type": "AWS::S3::Bucket", + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-iot-actions/test/s3-put-object/integ.s3-put-object-action.ts b/packages/@aws-cdk/aws-iot-actions/test/s3-put-object/integ.s3-put-object-action.ts new file mode 100644 index 0000000000000..9e100e0254eaf --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions/test/s3-put-object/integ.s3-put-object-action.ts @@ -0,0 +1,32 @@ +/// !cdk-integ pragma:ignore-assets +import * as iot from '@aws-cdk/aws-iot'; +import * as s3 from '@aws-cdk/aws-s3'; +import * as cdk from '@aws-cdk/core'; +import * as actions from '../../lib'; + +const app = new cdk.App(); + +class TestStack extends cdk.Stack { + constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + const topicRule = new iot.TopicRule(this, 'TopicRule', { + sql: iot.IotSql.fromStringAsVer20160323( + "SELECT topic(2) as device_id, year, month, day FROM 'device/+/data'", + ), + }); + + const bucket = new s3.Bucket(this, 'MyBucket', { + removalPolicy: cdk.RemovalPolicy.DESTROY, + }); + topicRule.addAction( + new actions.S3PutObjectAction(bucket, { + key: '${year}/${month}/${day}/${topic(2)}', + accessControl: s3.BucketAccessControl.BUCKET_OWNER_FULL_CONTROL, + }), + ); + } +} + +new TestStack(app, 'test-stack'); +app.synth(); diff --git a/packages/@aws-cdk/aws-iot-actions/test/s3-put-object/s3-put-object-action.test.ts b/packages/@aws-cdk/aws-iot-actions/test/s3-put-object/s3-put-object-action.test.ts new file mode 100644 index 0000000000000..567bd59d05083 --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions/test/s3-put-object/s3-put-object-action.test.ts @@ -0,0 +1,148 @@ +import { Template, Match } from '@aws-cdk/assertions'; +import * as iam from '@aws-cdk/aws-iam'; +import * as iot from '@aws-cdk/aws-iot'; +import * as s3 from '@aws-cdk/aws-s3'; +import * as cdk from '@aws-cdk/core'; +import * as actions from '../../lib'; + +test('Default s3 action', () => { + // GIVEN + const stack = new cdk.Stack(); + const topicRule = new iot.TopicRule(stack, 'MyTopicRule', { + sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id FROM 'device/+/data'"), + }); + const bucket = s3.Bucket.fromBucketArn(stack, 'MyBucket', 'arn:aws:s3::123456789012:test-bucket'); + + // WHEN + topicRule.addAction( + new actions.S3PutObjectAction(bucket), + ); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IoT::TopicRule', { + TopicRulePayload: { + Actions: [ + { + S3: { + BucketName: 'test-bucket', + Key: '${topic()}/${timestamp()}', + RoleArn: { + 'Fn::GetAtt': ['MyTopicRuleTopicRuleActionRoleCE2D05DA', 'Arn'], + }, + }, + }, + ], + }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: 'sts:AssumeRole', + Effect: 'Allow', + Principal: { + Service: 'iot.amazonaws.com', + }, + }, + ], + Version: '2012-10-17', + }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 's3:PutObject', + Effect: 'Allow', + Resource: 'arn:aws:s3::123456789012:test-bucket/*', + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'MyTopicRuleTopicRuleActionRoleDefaultPolicy54A701F7', + Roles: [ + { Ref: 'MyTopicRuleTopicRuleActionRoleCE2D05DA' }, + ], + }); +}); + +test('can set key of bucket', () => { + // GIVEN + const stack = new cdk.Stack(); + const topicRule = new iot.TopicRule(stack, 'MyTopicRule', { + sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id FROM 'device/+/data'"), + }); + const bucket = s3.Bucket.fromBucketArn(stack, 'MyBucket', 'arn:aws:s3::123456789012:test-bucket'); + + // WHEN + topicRule.addAction( + new actions.S3PutObjectAction(bucket, { + key: 'test-key', + }), + ); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IoT::TopicRule', { + TopicRulePayload: { + Actions: [ + Match.objectLike({ S3: { Key: 'test-key' } }), + ], + }, + }); +}); + +test('can set canned ACL and it convert to kebab case', () => { + // GIVEN + const stack = new cdk.Stack(); + const topicRule = new iot.TopicRule(stack, 'MyTopicRule', { + sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id FROM 'device/+/data'"), + }); + const bucket = s3.Bucket.fromBucketArn(stack, 'MyBucket', 'arn:aws:s3::123456789012:test-bucket'); + + // WHEN + topicRule.addAction( + new actions.S3PutObjectAction(bucket, { + accessControl: s3.BucketAccessControl.BUCKET_OWNER_FULL_CONTROL, + }), + ); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IoT::TopicRule', { + TopicRulePayload: { + Actions: [ + Match.objectLike({ S3: { CannedAcl: 'bucket-owner-full-control' } }), + ], + }, + }); +}); + +test('can set role', () => { + // GIVEN + const stack = new cdk.Stack(); + const topicRule = new iot.TopicRule(stack, 'MyTopicRule', { + sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id FROM 'device/+/data'"), + }); + const bucket = s3.Bucket.fromBucketArn(stack, 'MyBucket', 'arn:aws:s3::123456789012:test-bucket'); + const role = iam.Role.fromRoleArn(stack, 'MyRole', 'arn:aws:iam::123456789012:role/ForTest'); + + // WHEN + topicRule.addAction( + new actions.S3PutObjectAction(bucket, { role }), + ); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IoT::TopicRule', { + TopicRulePayload: { + Actions: [ + Match.objectLike({ S3: { RoleArn: 'arn:aws:iam::123456789012:role/ForTest' } }), + ], + }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyName: 'MyRolePolicy64AB00A5', + Roles: ['ForTest'], + }); +}); From 4ba40dcf275bbed0dbcca4cf6cf295edde5e9894 Mon Sep 17 00:00:00 2001 From: Daniel Mil <84205762+mildaniel@users.noreply.github.com> Date: Thu, 11 Nov 2021 01:26:46 -0800 Subject: [PATCH 17/23] fix(NestedStack): add asset metadata to NestedStack resources for local tooling (#17343) ---- Reference issue #14593 Building on this initial PR: https://github.com/aws/aws-cdk/pull/1433 We're looking to add asset metadata to the NestedStack resource. The implementation is similar to this one [design/code-asset-metadata.md](https://github.com/aws/aws-cdk/pull/design/code-asset-metadata.md). This will allow SAM CLI to support CDK-synthed templates nested function resources. *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/core/lib/nested-stack.ts | 15 ++++++++++++++ packages/@aws-cdk/core/test/stack.test.ts | 23 ++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/packages/@aws-cdk/core/lib/nested-stack.ts b/packages/@aws-cdk/core/lib/nested-stack.ts index 82a123fb9243b..347731894d794 100644 --- a/packages/@aws-cdk/core/lib/nested-stack.ts +++ b/packages/@aws-cdk/core/lib/nested-stack.ts @@ -1,4 +1,5 @@ import * as crypto from 'crypto'; +import * as cxapi from '@aws-cdk/cx-api'; import { Construct, Node } from 'constructs'; import { FileAssetPackaging } from './assets'; import { Fn } from './cfn-fn'; @@ -213,6 +214,8 @@ export class NestedStack extends Stack { fileName: this.templateFile, }); + this.addResourceMetadata(this.resource, 'TemplateURL'); + // if bucketName/objectKey are cfn parameters from a stack other than the parent stack, they will // be resolved as cross-stack references like any other (see "multi" tests). this._templateUrl = `https://s3.${this._parentStack.region}.${this._parentStack.urlSuffix}/${templateLocation.bucketName}/${templateLocation.objectKey}`; @@ -230,6 +233,18 @@ export class NestedStack extends Stack { }, }); } + + private addResourceMetadata(resource: CfnResource, resourceProperty: string) { + if (!this.node.tryGetContext(cxapi.ASSET_RESOURCE_METADATA_ENABLED_CONTEXT)) { + return; // not enabled + } + + // tell tools such as SAM CLI that the "TemplateURL" property of this resource + // points to the nested stack template for local emulation + resource.cfnOptions.metadata = resource.cfnOptions.metadata || { }; + resource.cfnOptions.metadata[cxapi.ASSET_RESOURCE_METADATA_PATH_KEY] = this.templateFile; + resource.cfnOptions.metadata[cxapi.ASSET_RESOURCE_METADATA_PROPERTY_KEY] = resourceProperty; + } } /** diff --git a/packages/@aws-cdk/core/test/stack.test.ts b/packages/@aws-cdk/core/test/stack.test.ts index 7c67f545d4b87..cd7186fea9f7d 100644 --- a/packages/@aws-cdk/core/test/stack.test.ts +++ b/packages/@aws-cdk/core/test/stack.test.ts @@ -661,6 +661,29 @@ describe('stack', () => { })); }); + test('asset metadata added to NestedStack resource that contains asset path and property', () => { + const app = new App(); + + // WHEN + const parentStack = new Stack(app, 'parent'); + parentStack.node.setContext(cxapi.ASSET_RESOURCE_METADATA_ENABLED_CONTEXT, true); + const childStack = new NestedStack(parentStack, 'child'); + new CfnResource(childStack, 'ChildResource', { type: 'Resource::Child' }); + + const assembly = app.synth(); + expect(assembly.getStackByName(parentStack.stackName).template).toEqual(expect.objectContaining({ + Resources: { + childNestedStackchildNestedStackResource7408D03F: expect.objectContaining({ + Metadata: { + 'aws:asset:path': 'parentchild13F9359B.nested.template.json', + 'aws:asset:property': 'TemplateURL', + }, + }), + }, + })); + + }); + test('cross-stack reference (substack references parent stack)', () => { // GIVEN const app = new App(); From 59005483ea1224a147db479471f541e2efb9ba23 Mon Sep 17 00:00:00 2001 From: JoLo <54506108+jolo-dev@users.noreply.github.com> Date: Thu, 11 Nov 2021 11:13:48 +0100 Subject: [PATCH 18/23] fix(ssm): fix service principals for all regions since ap-east-1 (#17047) fixes #16188 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/region-info/lib/default.ts | 1 + packages/@aws-cdk/region-info/test/default.test.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/region-info/lib/default.ts b/packages/@aws-cdk/region-info/lib/default.ts index abd1001678bf2..c0ae0cc2b28d5 100644 --- a/packages/@aws-cdk/region-info/lib/default.ts +++ b/packages/@aws-cdk/region-info/lib/default.ts @@ -82,6 +82,7 @@ export class Default { // Services with a regional principal case 'states': + case 'ssm': return `${service}.${region}.amazonaws.com`; // Services with a partitional principal diff --git a/packages/@aws-cdk/region-info/test/default.test.ts b/packages/@aws-cdk/region-info/test/default.test.ts index b39952842d75a..1e7c1b166c9b6 100644 --- a/packages/@aws-cdk/region-info/test/default.test.ts +++ b/packages/@aws-cdk/region-info/test/default.test.ts @@ -5,7 +5,7 @@ const urlSuffix = '.nowhere.null'; describe('servicePrincipal', () => { for (const suffix of ['', '.amazonaws.com', '.amazonaws.com.cn']) { - for (const service of ['states']) { + for (const service of ['states', 'ssm']) { test(`${service}${suffix}`, () => { expect(Default.servicePrincipal(`${service}${suffix}`, region, urlSuffix)).toBe(`${service}.${region}.amazonaws.com`); }); From 841cf990001dd64605873a65b8a155e37fc4541f Mon Sep 17 00:00:00 2001 From: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> Date: Thu, 11 Nov 2021 03:04:49 -0800 Subject: [PATCH 19/23] fix(apigateway): SAM CLI asset metadata missing from SpecRestApi (#17293) Adds Assets metadata to RestApi resource in case if AssetApiDefinition is used. This Metadata will enable SAM CLI to find local assets used by RestApi in the template. It follows the same design in document [design/code-asset-metadata.md](https://github.com/aws/aws-cdk/pull/design/code-asset-metadata.md) Fixes #14593 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-apigateway/lib/api-definition.ts | 27 +++++++++++++++++++ .../@aws-cdk/aws-apigateway/lib/restapi.ts | 3 +++ .../test/api-definition.test.ts | 19 +++++++++++++ ...integ.request-authorizer.lit.expected.json | 6 ++--- .../integ.request-authorizer.lit.ts | 2 +- ...eg.token-authorizer-iam-role.expected.json | 4 +-- .../integ.token-authorizer-iam-role.ts | 2 +- .../integ.token-authorizer.lit.expected.json | 6 ++--- .../authorizers/integ.token-authorizer.lit.ts | 2 +- .../test/integ.cors.expected.json | 4 +-- .../aws-apigateway/test/integ.cors.ts | 2 +- ...pi.latebound-deploymentstage.expected.json | 4 +-- ...eg.lambda-api.latebound-deploymentstage.ts | 2 +- .../test/integ.restapi.books.expected.json | 12 ++++----- .../test/integ.restapi.books.ts | 6 ++--- .../test/integ.restapi.expected.json | 4 +-- .../integ.restapi.multistack.expected.json | 6 ++--- .../test/integ.restapi.multistack.ts | 2 +- .../test/integ.restapi.multiuse.expected.json | 4 +-- .../test/integ.restapi.multiuse.ts | 2 +- .../aws-apigateway/test/integ.restapi.ts | 2 +- .../integ.restapi.vpc-endpoint.expected.json | 18 ++++++------- 22 files changed, 94 insertions(+), 45 deletions(-) diff --git a/packages/@aws-cdk/aws-apigateway/lib/api-definition.ts b/packages/@aws-cdk/aws-apigateway/lib/api-definition.ts index 850087920f152..96b9a5aced9e1 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/api-definition.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/api-definition.ts @@ -3,6 +3,10 @@ import * as s3_assets from '@aws-cdk/aws-s3-assets'; // keep this import separate from other imports to reduce chance for merge conflicts with v2-main // eslint-disable-next-line no-duplicate-imports, import/order +import * as cxapi from '@aws-cdk/cx-api'; +import { Node } from 'constructs'; +import { CfnRestApi } from './apigateway.generated'; +import { IRestApi } from './restapi'; import { Construct } from '@aws-cdk/core'; /** @@ -82,6 +86,15 @@ export abstract class ApiDefinition { * assume it's initialized. You may just use it as a construct scope. */ public abstract bind(scope: Construct): ApiDefinitionConfig; + + /** + * Called after the CFN RestApi resource has been created to allow the Api + * Definition to bind to it. Specifically it's required to allow assets to add + * metadata for tooling like SAM CLI to be able to find their origins. + */ + public bindAfterCreate(_scope: Construct, _restApi: IRestApi) { + return; + } } /** @@ -198,4 +211,18 @@ export class AssetApiDefinition extends ApiDefinition { }, }; } + + public bindAfterCreate(scope: Construct, restApi: IRestApi) { + if (!scope.node.tryGetContext(cxapi.ASSET_RESOURCE_METADATA_ENABLED_CONTEXT)) { + return; // not enabled + } + + if (!this.asset) { + throw new Error('bindToResource() must be called after bind()'); + } + + const child = Node.of(restApi).defaultChild as CfnRestApi; + child.addMetadata(cxapi.ASSET_RESOURCE_METADATA_PATH_KEY, this.asset.assetPath); + child.addMetadata(cxapi.ASSET_RESOURCE_METADATA_PROPERTY_KEY, 'BodyS3Location'); + } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigateway/lib/restapi.ts b/packages/@aws-cdk/aws-apigateway/lib/restapi.ts index e213ddad7f22f..7cfebff08c9e0 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/restapi.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/restapi.ts @@ -626,6 +626,9 @@ export class SpecRestApi extends RestApiBase { endpointConfiguration: this._configureEndpoints(props), parameters: props.parameters, }); + + props.apiDefinition.bindAfterCreate(this, this); + this.node.defaultChild = resource; this.restApiId = resource.ref; this.restApiRootResourceId = resource.attrRootResourceId; diff --git a/packages/@aws-cdk/aws-apigateway/test/api-definition.test.ts b/packages/@aws-cdk/aws-apigateway/test/api-definition.test.ts index 709900b36c71d..c4af056038451 100644 --- a/packages/@aws-cdk/aws-apigateway/test/api-definition.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/api-definition.test.ts @@ -1,7 +1,9 @@ import '@aws-cdk/assert-internal/jest'; import * as path from 'path'; +import { ResourcePart } from '@aws-cdk/assert-internal'; import * as s3 from '@aws-cdk/aws-s3'; import * as cdk from '@aws-cdk/core'; +import * as cxapi from '@aws-cdk/cx-api'; import * as apigw from '../lib'; describe('api definition', () => { @@ -73,6 +75,23 @@ describe('api definition', () => { expect(synthesized.assets.length).toEqual(1); }); + + test('asset metadata added to RestApi resource that contains Asset Api Definition', () => { + const stack = new cdk.Stack(); + stack.node.setContext(cxapi.ASSET_RESOURCE_METADATA_ENABLED_CONTEXT, true); + const assetApiDefinition = apigw.ApiDefinition.fromAsset(path.join(__dirname, 'sample-definition.yaml')); + new apigw.SpecRestApi(stack, 'API', { + apiDefinition: assetApiDefinition, + }); + + expect(stack).toHaveResource('AWS::ApiGateway::RestApi', { + Metadata: { + 'aws:asset:path': 'asset.68497ac876de4e963fc8f7b5f1b28844c18ecc95e3f7c6e9e0bf250e03c037fb.yaml', + 'aws:asset:property': 'BodyS3Location', + }, + }, ResourcePart.CompleteDefinition); + + }); }); describe('apigateway.ApiDefinition.fromBucket', () => { diff --git a/packages/@aws-cdk/aws-apigateway/test/authorizers/integ.request-authorizer.lit.expected.json b/packages/@aws-cdk/aws-apigateway/test/authorizers/integ.request-authorizer.lit.expected.json index e976e6426e0a5..e59d8e02743fc 100644 --- a/packages/@aws-cdk/aws-apigateway/test/authorizers/integ.request-authorizer.lit.expected.json +++ b/packages/@aws-cdk/aws-apigateway/test/authorizers/integ.request-authorizer.lit.expected.json @@ -72,14 +72,14 @@ ] } }, - "Handler": "index.handler", "Role": { "Fn::GetAtt": [ "MyAuthorizerFunctionServiceRole8A34C19E", "Arn" ] }, - "Runtime": "nodejs10.x" + "Handler": "index.handler", + "Runtime": "nodejs14.x" }, "DependsOn": [ "MyAuthorizerFunctionServiceRole8A34C19E" @@ -313,4 +313,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigateway/test/authorizers/integ.request-authorizer.lit.ts b/packages/@aws-cdk/aws-apigateway/test/authorizers/integ.request-authorizer.lit.ts index d8160e3be6f49..066badbe4e0bb 100644 --- a/packages/@aws-cdk/aws-apigateway/test/authorizers/integ.request-authorizer.lit.ts +++ b/packages/@aws-cdk/aws-apigateway/test/authorizers/integ.request-authorizer.lit.ts @@ -15,7 +15,7 @@ const app = new App(); const stack = new Stack(app, 'RequestAuthorizerInteg'); const authorizerFn = new lambda.Function(stack, 'MyAuthorizerFunction', { - runtime: lambda.Runtime.NODEJS_10_X, + runtime: lambda.Runtime.NODEJS_14_X, handler: 'index.handler', code: lambda.AssetCode.fromAsset(path.join(__dirname, 'integ.request-authorizer.handler')), }); diff --git a/packages/@aws-cdk/aws-apigateway/test/authorizers/integ.token-authorizer-iam-role.expected.json b/packages/@aws-cdk/aws-apigateway/test/authorizers/integ.token-authorizer-iam-role.expected.json index 1f3a157fc36b9..27e2f89e8f631 100644 --- a/packages/@aws-cdk/aws-apigateway/test/authorizers/integ.token-authorizer-iam-role.expected.json +++ b/packages/@aws-cdk/aws-apigateway/test/authorizers/integ.token-authorizer-iam-role.expected.json @@ -72,14 +72,14 @@ ] } }, - "Handler": "index.handler", "Role": { "Fn::GetAtt": [ "MyAuthorizerFunctionServiceRole8A34C19E", "Arn" ] }, - "Runtime": "nodejs10.x" + "Handler": "index.handler", + "Runtime": "nodejs14.x" }, "DependsOn": [ "MyAuthorizerFunctionServiceRole8A34C19E" diff --git a/packages/@aws-cdk/aws-apigateway/test/authorizers/integ.token-authorizer-iam-role.ts b/packages/@aws-cdk/aws-apigateway/test/authorizers/integ.token-authorizer-iam-role.ts index 47d05cf481009..5890d03f9bc3a 100644 --- a/packages/@aws-cdk/aws-apigateway/test/authorizers/integ.token-authorizer-iam-role.ts +++ b/packages/@aws-cdk/aws-apigateway/test/authorizers/integ.token-authorizer-iam-role.ts @@ -16,7 +16,7 @@ const app = new App(); const stack = new Stack(app, 'TokenAuthorizerIAMRoleInteg'); const authorizerFn = new lambda.Function(stack, 'MyAuthorizerFunction', { - runtime: lambda.Runtime.NODEJS_10_X, + runtime: lambda.Runtime.NODEJS_14_X, handler: 'index.handler', code: lambda.AssetCode.fromAsset(path.join(__dirname, 'integ.token-authorizer.handler')), }); diff --git a/packages/@aws-cdk/aws-apigateway/test/authorizers/integ.token-authorizer.lit.expected.json b/packages/@aws-cdk/aws-apigateway/test/authorizers/integ.token-authorizer.lit.expected.json index bb4c493fac04b..8a56511175436 100644 --- a/packages/@aws-cdk/aws-apigateway/test/authorizers/integ.token-authorizer.lit.expected.json +++ b/packages/@aws-cdk/aws-apigateway/test/authorizers/integ.token-authorizer.lit.expected.json @@ -72,14 +72,14 @@ ] } }, - "Handler": "index.handler", "Role": { "Fn::GetAtt": [ "MyAuthorizerFunctionServiceRole8A34C19E", "Arn" ] }, - "Runtime": "nodejs10.x" + "Handler": "index.handler", + "Runtime": "nodejs14.x" }, "DependsOn": [ "MyAuthorizerFunctionServiceRole8A34C19E" @@ -313,4 +313,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigateway/test/authorizers/integ.token-authorizer.lit.ts b/packages/@aws-cdk/aws-apigateway/test/authorizers/integ.token-authorizer.lit.ts index e62e476e8cd4a..655fa91962b1a 100644 --- a/packages/@aws-cdk/aws-apigateway/test/authorizers/integ.token-authorizer.lit.ts +++ b/packages/@aws-cdk/aws-apigateway/test/authorizers/integ.token-authorizer.lit.ts @@ -15,7 +15,7 @@ const app = new App(); const stack = new Stack(app, 'TokenAuthorizerInteg'); const authorizerFn = new lambda.Function(stack, 'MyAuthorizerFunction', { - runtime: lambda.Runtime.NODEJS_10_X, + runtime: lambda.Runtime.NODEJS_14_X, handler: 'index.handler', code: lambda.AssetCode.fromAsset(path.join(__dirname, 'integ.token-authorizer.handler')), }); diff --git a/packages/@aws-cdk/aws-apigateway/test/integ.cors.expected.json b/packages/@aws-cdk/aws-apigateway/test/integ.cors.expected.json index 6d17b2e53232e..146d5220f7540 100644 --- a/packages/@aws-cdk/aws-apigateway/test/integ.cors.expected.json +++ b/packages/@aws-cdk/aws-apigateway/test/integ.cors.expected.json @@ -564,14 +564,14 @@ ] } }, - "Handler": "index.handler", "Role": { "Fn::GetAtt": [ "handlerServiceRole187D5A5A", "Arn" ] }, - "Runtime": "nodejs10.x" + "Handler": "index.handler", + "Runtime": "nodejs14.x" }, "DependsOn": [ "handlerServiceRole187D5A5A" diff --git a/packages/@aws-cdk/aws-apigateway/test/integ.cors.ts b/packages/@aws-cdk/aws-apigateway/test/integ.cors.ts index 5b3fc8bdc36c2..e5617f4c9b202 100644 --- a/packages/@aws-cdk/aws-apigateway/test/integ.cors.ts +++ b/packages/@aws-cdk/aws-apigateway/test/integ.cors.ts @@ -12,7 +12,7 @@ class TestStack extends Stack { const api = new apigw.RestApi(this, 'cors-api-test'); const handler = new lambda.Function(this, 'handler', { - runtime: lambda.Runtime.NODEJS_10_X, + runtime: lambda.Runtime.NODEJS_14_X, handler: 'index.handler', code: lambda.Code.fromAsset(path.join(__dirname, 'integ.cors.handler')), }); diff --git a/packages/@aws-cdk/aws-apigateway/test/integ.lambda-api.latebound-deploymentstage.expected.json b/packages/@aws-cdk/aws-apigateway/test/integ.lambda-api.latebound-deploymentstage.expected.json index 17dd7ccf222e8..8cf5ef7c0e552 100644 --- a/packages/@aws-cdk/aws-apigateway/test/integ.lambda-api.latebound-deploymentstage.expected.json +++ b/packages/@aws-cdk/aws-apigateway/test/integ.lambda-api.latebound-deploymentstage.expected.json @@ -37,14 +37,14 @@ "Code": { "ZipFile": "foo" }, - "Handler": "index.handler", "Role": { "Fn::GetAtt": [ "myfnServiceRole7822DC24", "Arn" ] }, - "Runtime": "nodejs10.x" + "Handler": "index.handler", + "Runtime": "nodejs14.x" }, "DependsOn": [ "myfnServiceRole7822DC24" diff --git a/packages/@aws-cdk/aws-apigateway/test/integ.lambda-api.latebound-deploymentstage.ts b/packages/@aws-cdk/aws-apigateway/test/integ.lambda-api.latebound-deploymentstage.ts index d6176fdb78301..615c934f918fe 100644 --- a/packages/@aws-cdk/aws-apigateway/test/integ.lambda-api.latebound-deploymentstage.ts +++ b/packages/@aws-cdk/aws-apigateway/test/integ.lambda-api.latebound-deploymentstage.ts @@ -9,7 +9,7 @@ class LateBoundDeploymentStageStack extends Stack { const fn = new Function(this, 'myfn', { code: Code.fromInline('foo'), - runtime: Runtime.NODEJS_10_X, + runtime: Runtime.NODEJS_14_X, handler: 'index.handler', }); diff --git a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.books.expected.json b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.books.expected.json index 91af30b6ef8d4..a77027807057d 100644 --- a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.books.expected.json +++ b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.books.expected.json @@ -37,14 +37,14 @@ "Code": { "ZipFile": "exports.handler = function echoHandlerCode(event, _, callback) {\n return callback(undefined, {\n isBase64Encoded: false,\n statusCode: 200,\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(event),\n });\n}" }, - "Handler": "index.handler", "Role": { "Fn::GetAtt": [ "BooksHandlerServiceRole5B6A8847", "Arn" ] }, - "Runtime": "nodejs10.x" + "Handler": "index.handler", + "Runtime": "nodejs14.x" }, "DependsOn": [ "BooksHandlerServiceRole5B6A8847" @@ -87,14 +87,14 @@ "Code": { "ZipFile": "exports.handler = function echoHandlerCode(event, _, callback) {\n return callback(undefined, {\n isBase64Encoded: false,\n statusCode: 200,\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(event),\n });\n}" }, - "Handler": "index.handler", "Role": { "Fn::GetAtt": [ "BookHandlerServiceRole894768AD", "Arn" ] }, - "Runtime": "nodejs10.x" + "Handler": "index.handler", + "Runtime": "nodejs14.x" }, "DependsOn": [ "BookHandlerServiceRole894768AD" @@ -137,14 +137,14 @@ "Code": { "ZipFile": "exports.handler = function helloCode(_event, _context, callback) {\n return callback(undefined, {\n statusCode: 200,\n body: 'hello, world!',\n });\n}" }, - "Handler": "index.handler", "Role": { "Fn::GetAtt": [ "HelloServiceRole1E55EA16", "Arn" ] }, - "Runtime": "nodejs10.x" + "Handler": "index.handler", + "Runtime": "nodejs14.x" }, "DependsOn": [ "HelloServiceRole1E55EA16" diff --git a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.books.ts b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.books.ts index 9c647c83dcebd..0ec001e3d1a3a 100644 --- a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.books.ts +++ b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.books.ts @@ -7,19 +7,19 @@ class BookStack extends cdk.Stack { super(scope, id); const booksHandler = new apigw.LambdaIntegration(new lambda.Function(this, 'BooksHandler', { - runtime: lambda.Runtime.NODEJS_10_X, + runtime: lambda.Runtime.NODEJS_14_X, handler: 'index.handler', code: lambda.Code.fromInline(`exports.handler = ${echoHandlerCode}`), })); const bookHandler = new apigw.LambdaIntegration(new lambda.Function(this, 'BookHandler', { - runtime: lambda.Runtime.NODEJS_10_X, + runtime: lambda.Runtime.NODEJS_14_X, handler: 'index.handler', code: lambda.Code.fromInline(`exports.handler = ${echoHandlerCode}`), })); const hello = new apigw.LambdaIntegration(new lambda.Function(this, 'Hello', { - runtime: lambda.Runtime.NODEJS_10_X, + runtime: lambda.Runtime.NODEJS_14_X, handler: 'index.handler', code: lambda.Code.fromInline(`exports.handler = ${helloCode}`), })); diff --git a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.expected.json b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.expected.json index a0fb6357db3c7..606c5f2098a0d 100644 --- a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.expected.json +++ b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.expected.json @@ -651,14 +651,14 @@ "Code": { "ZipFile": "exports.handler = function handlerCode(event, _, callback) {\n return callback(undefined, {\n isBase64Encoded: false,\n statusCode: 200,\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(event),\n });\n }" }, - "Handler": "index.handler", "Role": { "Fn::GetAtt": [ "MyHandlerServiceRoleFFA06653", "Arn" ] }, - "Runtime": "nodejs10.x" + "Handler": "index.handler", + "Runtime": "nodejs14.x" }, "DependsOn": [ "MyHandlerServiceRoleFFA06653" diff --git a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.multistack.expected.json b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.multistack.expected.json index 5aa57732955bc..198d0e80231ae 100644 --- a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.multistack.expected.json +++ b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.multistack.expected.json @@ -38,15 +38,15 @@ "Code": { "ZipFile": "exports.handler = async function(event) {\n return {\n 'headers': { 'Content-Type': 'text/plain' },\n 'statusCode': 200\n }\n }" }, - "Handler": "index.handler", "Role": { "Fn::GetAtt": [ "firstLambdaServiceRoleB6408C31", "Arn" ] }, - "Runtime": "nodejs10.x", - "FunctionName": "FirstLambda" + "FunctionName": "FirstLambda", + "Handler": "index.handler", + "Runtime": "nodejs14.x" }, "DependsOn": [ "firstLambdaServiceRoleB6408C31" diff --git a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.multistack.ts b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.multistack.ts index 0ac4e01241eba..75b6e219613de 100644 --- a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.multistack.ts +++ b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.multistack.ts @@ -20,7 +20,7 @@ class FirstStack extends cdk.Stack { } }`), handler: 'index.handler', - runtime: lambda.Runtime.NODEJS_10_X, + runtime: lambda.Runtime.NODEJS_14_X, }); } } diff --git a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.multiuse.expected.json b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.multiuse.expected.json index 6a7cea680ef60..0ed3cb19c6aad 100644 --- a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.multiuse.expected.json +++ b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.multiuse.expected.json @@ -37,14 +37,14 @@ "Code": { "ZipFile": "exports.handler = function helloCode(_event, _context, callback) {\n return callback(undefined, {\n statusCode: 200,\n body: 'hello, world!',\n });\n}" }, - "Handler": "index.handler", "Role": { "Fn::GetAtt": [ "HelloServiceRole1E55EA16", "Arn" ] }, - "Runtime": "nodejs10.x" + "Handler": "index.handler", + "Runtime": "nodejs14.x" }, "DependsOn": [ "HelloServiceRole1E55EA16" diff --git a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.multiuse.ts b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.multiuse.ts index a248bf9041158..612bd0e83b963 100644 --- a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.multiuse.ts +++ b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.multiuse.ts @@ -7,7 +7,7 @@ class MultiStack extends cdk.Stack { super(scope, id); const hello = new apigw.LambdaIntegration(new lambda.Function(this, 'Hello', { - runtime: lambda.Runtime.NODEJS_10_X, + runtime: lambda.Runtime.NODEJS_14_X, handler: 'index.handler', code: lambda.Code.fromInline(`exports.handler = ${helloCode}`), })); diff --git a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.ts b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.ts index 10e3d357108d7..537ec3031d58a 100644 --- a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.ts +++ b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.ts @@ -23,7 +23,7 @@ class Test extends cdk.Stack { }); const handler = new lambda.Function(this, 'MyHandler', { - runtime: lambda.Runtime.NODEJS_10_X, + runtime: lambda.Runtime.NODEJS_14_X, code: lambda.Code.fromInline(`exports.handler = ${handlerCode}`), handler: 'index.handler', }); diff --git a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.vpc-endpoint.expected.json b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.vpc-endpoint.expected.json index 9051ff580c010..4279ff70893aa 100644 --- a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.vpc-endpoint.expected.json +++ b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.vpc-endpoint.expected.json @@ -95,15 +95,15 @@ "MyVpcPublicSubnet1NATGatewayAD3400C1": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "MyVpcPublicSubnet1SubnetF6608456" + }, "AllocationId": { "Fn::GetAtt": [ "MyVpcPublicSubnet1EIP096967CB", "AllocationId" ] }, - "SubnetId": { - "Ref": "MyVpcPublicSubnet1SubnetF6608456" - }, "Tags": [ { "Key": "Name", @@ -192,15 +192,15 @@ "MyVpcPublicSubnet2NATGateway91BFBEC9": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "MyVpcPublicSubnet2Subnet492B6BFB" + }, "AllocationId": { "Fn::GetAtt": [ "MyVpcPublicSubnet2EIP8CCBA239", "AllocationId" ] }, - "SubnetId": { - "Ref": "MyVpcPublicSubnet2Subnet492B6BFB" - }, "Tags": [ { "Key": "Name", @@ -289,15 +289,15 @@ "MyVpcPublicSubnet3NATGatewayD4B50EBE": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "MyVpcPublicSubnet3Subnet57EEE236" + }, "AllocationId": { "Fn::GetAtt": [ "MyVpcPublicSubnet3EIPC5ACADAB", "AllocationId" ] }, - "SubnetId": { - "Ref": "MyVpcPublicSubnet3Subnet57EEE236" - }, "Tags": [ { "Key": "Name", From 3cd8d481906fc4e3abdd1211908844e5b8bd2509 Mon Sep 17 00:00:00 2001 From: Cory Hall <43035978+corymhall@users.noreply.github.com> Date: Thu, 11 Nov 2021 10:46:22 -0500 Subject: [PATCH 20/23] fix(sns-subscriptions): enable cross region subscriptions to sqs and lambda (#17273) fixing cross region subscriptions to SQS and Lambda. This PR handles a couple different scenarios. This only applies to SNS topics that are instanceof sns.Topic, it does not apply to imported topics (sns.ITopic). The current behavior for imported topics will remain the same. 1. SNS Topic and subscriber (sqs or lambda) are created in separate stacks with explicit environment. In this case if the `region` is different between the two stacks then the topic region will be provided in the subscription. 2. SNS Topic and subscriber (sqs or lambda) are created in separate stacks, and _both_ are env agnostic (no explicit region is provided) In this case a region is not specified in the subscription resource, which means it is assumed that they are both created in the same region. The alternatives are to either throw an error telling the user to specify a region, or to create a CfnOutput with the topic region. Since it should be a rare occurrence for a user to be deploying a CDK app with multiple env agnostic stacks that are meant for different environments, I think the best option is to assume same region. 3. SNS Topic and subscriber (sqs or lambda) are created in separate stacks, and _one_ of them are env agnostic (no explicit region is provided) In this case if the SNS stack has an explicit environment then we will provide that in the subscription resource (assume that it is cross region). If the SNS stack is env agnostic then we will do nothing (assume they are created in the same region). fixes #7044,#13707 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-sns-subscriptions/lib/lambda.ts | 16 +- .../@aws-cdk/aws-sns-subscriptions/lib/sqs.ts | 16 +- ...nteg.sns-lambda-cross-region.expected.json | 116 +++ .../test/integ.sns-lambda-cross-region.ts | 35 + ...teg.sns-sqs-cross-region.lit.expected.json | 90 +++ .../test/integ.sns-sqs-cross-region.lit.ts | 25 + .../aws-sns-subscriptions/test/subs.test.ts | 688 +++++++++++++++++- 7 files changed, 983 insertions(+), 3 deletions(-) create mode 100644 packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-lambda-cross-region.expected.json create mode 100644 packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-lambda-cross-region.ts create mode 100644 packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-sqs-cross-region.lit.expected.json create mode 100644 packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-sqs-cross-region.lit.ts diff --git a/packages/@aws-cdk/aws-sns-subscriptions/lib/lambda.ts b/packages/@aws-cdk/aws-sns-subscriptions/lib/lambda.ts index aa7581653d5ba..58c7a2aceb16b 100644 --- a/packages/@aws-cdk/aws-sns-subscriptions/lib/lambda.ts +++ b/packages/@aws-cdk/aws-sns-subscriptions/lib/lambda.ts @@ -1,7 +1,7 @@ import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; import * as sns from '@aws-cdk/aws-sns'; -import { Names, Stack } from '@aws-cdk/core'; +import { Names, Stack, Token } from '@aws-cdk/core'; import { SubscriptionProps } from './subscription'; // keep this import separate from other imports to reduce chance for merge conflicts with v2-main @@ -36,6 +36,12 @@ export class LambdaSubscription implements sns.ITopicSubscription { principal: new iam.ServicePrincipal('sns.amazonaws.com'), }); + // if the topic and function are created in different stacks + // then we need to make sure the topic is created first + if (topic instanceof sns.Topic && topic.stack !== this.fn.stack) { + this.fn.stack.addDependency(topic.stack); + } + return { subscriberScope: this.fn, subscriberId: topic.node.id, @@ -50,6 +56,14 @@ export class LambdaSubscription implements sns.ITopicSubscription { private regionFromArn(topic: sns.ITopic): string | undefined { // no need to specify `region` for topics defined within the same stack. if (topic instanceof sns.Topic) { + if (topic.stack !== this.fn.stack) { + // only if we know the region, will not work for + // env agnostic stacks + if (!Token.isUnresolved(topic.stack.region) && + (topic.stack.region !== this.fn.stack.region)) { + return topic.stack.region; + } + } return undefined; } return Stack.of(topic).parseArn(topic.topicArn).region; diff --git a/packages/@aws-cdk/aws-sns-subscriptions/lib/sqs.ts b/packages/@aws-cdk/aws-sns-subscriptions/lib/sqs.ts index 6cf89ebc53c60..8bbb77927381f 100644 --- a/packages/@aws-cdk/aws-sns-subscriptions/lib/sqs.ts +++ b/packages/@aws-cdk/aws-sns-subscriptions/lib/sqs.ts @@ -1,7 +1,7 @@ import * as iam from '@aws-cdk/aws-iam'; import * as sns from '@aws-cdk/aws-sns'; import * as sqs from '@aws-cdk/aws-sqs'; -import { Names, Stack } from '@aws-cdk/core'; +import { Names, Stack, Token } from '@aws-cdk/core'; import { SubscriptionProps } from './subscription'; // keep this import separate from other imports to reduce chance for merge conflicts with v2-main @@ -61,6 +61,12 @@ export class SqsSubscription implements sns.ITopicSubscription { })); } + // if the topic and queue are created in different stacks + // then we need to make sure the topic is created first + if (topic instanceof sns.Topic && topic.stack !== this.queue.stack) { + this.queue.stack.addDependency(topic.stack); + } + return { subscriberScope: this.queue, subscriberId: Names.nodeUniqueId(topic.node), @@ -76,6 +82,14 @@ export class SqsSubscription implements sns.ITopicSubscription { private regionFromArn(topic: sns.ITopic): string | undefined { // no need to specify `region` for topics defined within the same stack if (topic instanceof sns.Topic) { + if (topic.stack !== this.queue.stack) { + // only if we know the region, will not work for + // env agnostic stacks + if (!Token.isUnresolved(topic.stack.region) && + (topic.stack.region !== this.queue.stack.region)) { + return topic.stack.region; + } + } return undefined; } return Stack.of(topic).parseArn(topic.topicArn).region; diff --git a/packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-lambda-cross-region.expected.json b/packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-lambda-cross-region.expected.json new file mode 100644 index 0000000000000..de5216b565954 --- /dev/null +++ b/packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-lambda-cross-region.expected.json @@ -0,0 +1,116 @@ +[ + { + "Resources": { + "MyTopic86869434": { + "Type": "AWS::SNS::Topic", + "Properties": { + "TopicName": "topicstackopicstackmytopicc43e67afb24f28bb94f9" + } + } + } + }, + { + "Resources": { + "EchoServiceRoleBE28060B": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "Echo11F3FB29": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "exports.handler = function handler(event, _context, callback) {\n /* eslint-disable no-console */\n console.log('====================================================');\n console.log(JSON.stringify(event, undefined, 2));\n console.log('====================================================');\n return callback(undefined, event);\n}" + }, + "Role": { + "Fn::GetAtt": [ + "EchoServiceRoleBE28060B", + "Arn" + ] + }, + "Handler": "index.handler", + "Runtime": "nodejs10.x" + }, + "DependsOn": [ + "EchoServiceRoleBE28060B" + ] + }, + "EchoAllowInvokeTopicStackMyTopicC43E67AF32CF6EFA": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "Echo11F3FB29", + "Arn" + ] + }, + "Principal": "sns.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":sns:us-east-1:12345678:topicstackopicstackmytopicc43e67afb24f28bb94f9" + ] + ] + } + } + }, + "EchoMyTopic4CB8819E": { + "Type": "AWS::SNS::Subscription", + "Properties": { + "Protocol": "lambda", + "TopicArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":sns:us-east-1:12345678:topicstackopicstackmytopicc43e67afb24f28bb94f9" + ] + ] + }, + "Endpoint": { + "Fn::GetAtt": [ + "Echo11F3FB29", + "Arn" + ] + }, + "Region": "us-east-1" + } + } + } + } +] \ No newline at end of file diff --git a/packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-lambda-cross-region.ts b/packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-lambda-cross-region.ts new file mode 100644 index 0000000000000..cfec9592e3dba --- /dev/null +++ b/packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-lambda-cross-region.ts @@ -0,0 +1,35 @@ +import * as lambda from '@aws-cdk/aws-lambda'; +import * as sns from '@aws-cdk/aws-sns'; +import * as cdk from '@aws-cdk/core'; +import * as subs from '../lib'; + +/// !cdk-integ * +const app = new cdk.App(); + +const topicStack = new cdk.Stack(app, 'TopicStack', { + env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: 'us-east-1' }, +}); +const topic = new sns.Topic(topicStack, 'MyTopic', { + topicName: cdk.PhysicalName.GENERATE_IF_NEEDED, +}); + +const functionStack = new cdk.Stack(app, 'FunctionStack', { + env: { region: 'us-east-2' }, +}); +const fction = new lambda.Function(functionStack, 'Echo', { + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_10_X, + code: lambda.Code.fromInline(`exports.handler = ${handler.toString()}`), +}); + +topic.addSubscription(new subs.LambdaSubscription(fction)); + +app.synth(); + +function handler(event: any, _context: any, callback: any) { + /* eslint-disable no-console */ + console.log('===================================================='); + console.log(JSON.stringify(event, undefined, 2)); + console.log('===================================================='); + return callback(undefined, event); +} diff --git a/packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-sqs-cross-region.lit.expected.json b/packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-sqs-cross-region.lit.expected.json new file mode 100644 index 0000000000000..5bbffb5e31628 --- /dev/null +++ b/packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-sqs-cross-region.lit.expected.json @@ -0,0 +1,90 @@ +[ + { + "Resources": { + "MyTopic86869434": { + "Type": "AWS::SNS::Topic", + "Properties": { + "TopicName": "topicstackopicstackmytopicc43e67afb24f28bb94f9" + } + } + } + }, + { + "Resources": { + "MyQueueE6CA6235": { + "Type": "AWS::SQS::Queue", + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "MyQueuePolicy6BBEDDAC": { + "Type": "AWS::SQS::QueuePolicy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sqs:SendMessage", + "Condition": { + "ArnEquals": { + "aws:SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":sns:us-east-1:12345678:topicstackopicstackmytopicc43e67afb24f28bb94f9" + ] + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "sns.amazonaws.com" + }, + "Resource": { + "Fn::GetAtt": [ + "MyQueueE6CA6235", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "Queues": [ + { + "Ref": "MyQueueE6CA6235" + } + ] + } + }, + "MyQueueTopicStackMyTopicC43E67AFC8DC8B4A": { + "Type": "AWS::SNS::Subscription", + "Properties": { + "Protocol": "sqs", + "TopicArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":sns:us-east-1:12345678:topicstackopicstackmytopicc43e67afb24f28bb94f9" + ] + ] + }, + "Endpoint": { + "Fn::GetAtt": [ + "MyQueueE6CA6235", + "Arn" + ] + }, + "Region": "us-east-1" + } + } + } + } +] \ No newline at end of file diff --git a/packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-sqs-cross-region.lit.ts b/packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-sqs-cross-region.lit.ts new file mode 100644 index 0000000000000..ca53a70194e03 --- /dev/null +++ b/packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-sqs-cross-region.lit.ts @@ -0,0 +1,25 @@ +import * as sns from '@aws-cdk/aws-sns'; +import * as sqs from '@aws-cdk/aws-sqs'; +import * as cdk from '@aws-cdk/core'; +import * as subs from '../lib'; + +/// !cdk-integ * +const app = new cdk.App(); + +/// !show +const topicStack = new cdk.Stack(app, 'TopicStack', { + env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: 'us-east-1' }, +}); +const topic = new sns.Topic(topicStack, 'MyTopic', { + topicName: cdk.PhysicalName.GENERATE_IF_NEEDED, +}); + +const queueStack = new cdk.Stack(app, 'QueueStack', { + env: { region: 'us-east-2' }, +}); +const queue = new sqs.Queue(queueStack, 'MyQueue'); + +topic.addSubscription(new subs.SqsSubscription(queue)); +/// !hide + +app.synth(); diff --git a/packages/@aws-cdk/aws-sns-subscriptions/test/subs.test.ts b/packages/@aws-cdk/aws-sns-subscriptions/test/subs.test.ts index 8be564b5a9188..671937a3ed01e 100644 --- a/packages/@aws-cdk/aws-sns-subscriptions/test/subs.test.ts +++ b/packages/@aws-cdk/aws-sns-subscriptions/test/subs.test.ts @@ -3,7 +3,7 @@ import * as kms from '@aws-cdk/aws-kms'; import * as lambda from '@aws-cdk/aws-lambda'; import * as sns from '@aws-cdk/aws-sns'; import * as sqs from '@aws-cdk/aws-sqs'; -import { CfnParameter, Duration, RemovalPolicy, Stack, Token } from '@aws-cdk/core'; +import { App, CfnParameter, Duration, RemovalPolicy, Stack, Token } from '@aws-cdk/core'; import * as subs from '../lib'; /* eslint-disable quote-props */ @@ -308,6 +308,455 @@ test('queue subscription', () => { }); }); +test('queue subscription cross region', () => { + const app = new App(); + const topicStack = new Stack(app, 'TopicStack', { + env: { + account: '11111111111', + region: 'us-east-1', + }, + }); + const queueStack = new Stack(app, 'QueueStack', { + env: { + account: '11111111111', + region: 'us-east-2', + }, + }); + + const topic1 = new sns.Topic(topicStack, 'Topic', { + topicName: 'topicName', + displayName: 'displayName', + }); + + const queue = new sqs.Queue(queueStack, 'MyQueue'); + + topic1.addSubscription(new subs.SqsSubscription(queue)); + + expect(topicStack).toMatchTemplate({ + 'Resources': { + 'TopicBFC7AF6E': { + 'Type': 'AWS::SNS::Topic', + 'Properties': { + 'DisplayName': 'displayName', + 'TopicName': 'topicName', + }, + }, + }, + }); + + expect(queueStack).toMatchTemplate({ + 'Resources': { + 'MyQueueE6CA6235': { + 'Type': 'AWS::SQS::Queue', + 'UpdateReplacePolicy': 'Delete', + 'DeletionPolicy': 'Delete', + }, + 'MyQueuePolicy6BBEDDAC': { + 'Type': 'AWS::SQS::QueuePolicy', + 'Properties': { + 'PolicyDocument': { + 'Statement': [ + { + 'Action': 'sqs:SendMessage', + 'Condition': { + 'ArnEquals': { + 'aws:SourceArn': { + 'Fn::Join': [ + '', + [ + 'arn:', + { + 'Ref': 'AWS::Partition', + }, + ':sns:us-east-1:11111111111:topicName', + ], + ], + }, + }, + }, + 'Effect': 'Allow', + 'Principal': { + 'Service': 'sns.amazonaws.com', + }, + 'Resource': { + 'Fn::GetAtt': [ + 'MyQueueE6CA6235', + 'Arn', + ], + }, + }, + ], + 'Version': '2012-10-17', + }, + 'Queues': [ + { + 'Ref': 'MyQueueE6CA6235', + }, + ], + }, + }, + 'MyQueueTopicStackTopicFBF76EB349BDFA94': { + 'Type': 'AWS::SNS::Subscription', + 'Properties': { + 'Protocol': 'sqs', + 'TopicArn': { + 'Fn::Join': [ + '', + [ + 'arn:', + { + 'Ref': 'AWS::Partition', + }, + ':sns:us-east-1:11111111111:topicName', + ], + ], + }, + 'Endpoint': { + 'Fn::GetAtt': [ + 'MyQueueE6CA6235', + 'Arn', + ], + }, + 'Region': 'us-east-1', + }, + }, + }, + }); +}); + +test('queue subscription cross region, env agnostic', () => { + const app = new App(); + const topicStack = new Stack(app, 'TopicStack', {}); + const queueStack = new Stack(app, 'QueueStack', {}); + + const topic1 = new sns.Topic(topicStack, 'Topic', { + topicName: 'topicName', + displayName: 'displayName', + }); + + const queue = new sqs.Queue(queueStack, 'MyQueue'); + + topic1.addSubscription(new subs.SqsSubscription(queue)); + + expect(topicStack).toMatchTemplate({ + 'Resources': { + 'TopicBFC7AF6E': { + 'Type': 'AWS::SNS::Topic', + 'Properties': { + 'DisplayName': 'displayName', + 'TopicName': 'topicName', + }, + }, + }, + 'Outputs': { + 'ExportsOutputRefTopicBFC7AF6ECB4A357A': { + 'Value': { + 'Ref': 'TopicBFC7AF6E', + }, + 'Export': { + 'Name': 'TopicStack:ExportsOutputRefTopicBFC7AF6ECB4A357A', + }, + }, + }, + }); + + expect(queueStack).toMatchTemplate({ + 'Resources': { + 'MyQueueE6CA6235': { + 'Type': 'AWS::SQS::Queue', + 'UpdateReplacePolicy': 'Delete', + 'DeletionPolicy': 'Delete', + }, + 'MyQueuePolicy6BBEDDAC': { + 'Type': 'AWS::SQS::QueuePolicy', + 'Properties': { + 'PolicyDocument': { + 'Statement': [ + { + 'Action': 'sqs:SendMessage', + 'Condition': { + 'ArnEquals': { + 'aws:SourceArn': { + 'Fn::ImportValue': 'TopicStack:ExportsOutputRefTopicBFC7AF6ECB4A357A', + }, + }, + }, + 'Effect': 'Allow', + 'Principal': { + 'Service': 'sns.amazonaws.com', + }, + 'Resource': { + 'Fn::GetAtt': [ + 'MyQueueE6CA6235', + 'Arn', + ], + }, + }, + ], + 'Version': '2012-10-17', + }, + 'Queues': [ + { + 'Ref': 'MyQueueE6CA6235', + }, + ], + }, + }, + 'MyQueueTopicStackTopicFBF76EB349BDFA94': { + 'Type': 'AWS::SNS::Subscription', + 'Properties': { + 'Protocol': 'sqs', + 'TopicArn': { + 'Fn::ImportValue': 'TopicStack:ExportsOutputRefTopicBFC7AF6ECB4A357A', + }, + 'Endpoint': { + 'Fn::GetAtt': [ + 'MyQueueE6CA6235', + 'Arn', + ], + }, + }, + }, + }, + }); +}); + +test('queue subscription cross region, topic env agnostic', () => { + const app = new App(); + const topicStack = new Stack(app, 'TopicStack', {}); + const queueStack = new Stack(app, 'QueueStack', { + env: { + account: '11111111111', + region: 'us-east-1', + }, + }); + + const topic1 = new sns.Topic(topicStack, 'Topic', { + topicName: 'topicName', + displayName: 'displayName', + }); + + const queue = new sqs.Queue(queueStack, 'MyQueue'); + + topic1.addSubscription(new subs.SqsSubscription(queue)); + + expect(topicStack).toMatchTemplate({ + 'Resources': { + 'TopicBFC7AF6E': { + 'Type': 'AWS::SNS::Topic', + 'Properties': { + 'DisplayName': 'displayName', + 'TopicName': 'topicName', + }, + }, + }, + }); + + expect(queueStack).toMatchTemplate({ + 'Resources': { + 'MyQueueE6CA6235': { + 'Type': 'AWS::SQS::Queue', + 'UpdateReplacePolicy': 'Delete', + 'DeletionPolicy': 'Delete', + }, + 'MyQueuePolicy6BBEDDAC': { + 'Type': 'AWS::SQS::QueuePolicy', + 'Properties': { + 'PolicyDocument': { + 'Statement': [ + { + 'Action': 'sqs:SendMessage', + 'Condition': { + 'ArnEquals': { + 'aws:SourceArn': { + 'Fn::Join': [ + '', + [ + 'arn:', + { + 'Ref': 'AWS::Partition', + }, + ':sns:', + { + 'Ref': 'AWS::Region', + }, + ':', + { + 'Ref': 'AWS::AccountId', + }, + ':topicName', + ], + ], + }, + }, + }, + 'Effect': 'Allow', + 'Principal': { + 'Service': 'sns.amazonaws.com', + }, + 'Resource': { + 'Fn::GetAtt': [ + 'MyQueueE6CA6235', + 'Arn', + ], + }, + }, + ], + 'Version': '2012-10-17', + }, + 'Queues': [ + { + 'Ref': 'MyQueueE6CA6235', + }, + ], + }, + }, + 'MyQueueTopicStackTopicFBF76EB349BDFA94': { + 'Type': 'AWS::SNS::Subscription', + 'Properties': { + 'Protocol': 'sqs', + 'TopicArn': { + 'Fn::Join': [ + '', + [ + 'arn:', + { + 'Ref': 'AWS::Partition', + }, + ':sns:', + { + 'Ref': 'AWS::Region', + }, + ':', + { + 'Ref': 'AWS::AccountId', + }, + ':topicName', + ], + ], + }, + 'Endpoint': { + 'Fn::GetAtt': [ + 'MyQueueE6CA6235', + 'Arn', + ], + }, + }, + }, + }, + }); +}); + +test('queue subscription cross region, queue env agnostic', () => { + const app = new App(); + const topicStack = new Stack(app, 'TopicStack', { + env: { + account: '11111111111', + region: 'us-east-1', + }, + }); + const queueStack = new Stack(app, 'QueueStack', {}); + + const topic1 = new sns.Topic(topicStack, 'Topic', { + topicName: 'topicName', + displayName: 'displayName', + }); + + const queue = new sqs.Queue(queueStack, 'MyQueue'); + + topic1.addSubscription(new subs.SqsSubscription(queue)); + + expect(topicStack).toMatchTemplate({ + 'Resources': { + 'TopicBFC7AF6E': { + 'Type': 'AWS::SNS::Topic', + 'Properties': { + 'DisplayName': 'displayName', + 'TopicName': 'topicName', + }, + }, + }, + }); + + expect(queueStack).toMatchTemplate({ + 'Resources': { + 'MyQueueE6CA6235': { + 'Type': 'AWS::SQS::Queue', + 'UpdateReplacePolicy': 'Delete', + 'DeletionPolicy': 'Delete', + }, + 'MyQueuePolicy6BBEDDAC': { + 'Type': 'AWS::SQS::QueuePolicy', + 'Properties': { + 'PolicyDocument': { + 'Statement': [ + { + 'Action': 'sqs:SendMessage', + 'Condition': { + 'ArnEquals': { + 'aws:SourceArn': { + 'Fn::Join': [ + '', + [ + 'arn:', + { + 'Ref': 'AWS::Partition', + }, + ':sns:us-east-1:11111111111:topicName', + ], + ], + }, + }, + }, + 'Effect': 'Allow', + 'Principal': { + 'Service': 'sns.amazonaws.com', + }, + 'Resource': { + 'Fn::GetAtt': [ + 'MyQueueE6CA6235', + 'Arn', + ], + }, + }, + ], + 'Version': '2012-10-17', + }, + 'Queues': [ + { + 'Ref': 'MyQueueE6CA6235', + }, + ], + }, + }, + 'MyQueueTopicStackTopicFBF76EB349BDFA94': { + 'Type': 'AWS::SNS::Subscription', + 'Properties': { + 'Protocol': 'sqs', + 'TopicArn': { + 'Fn::Join': [ + '', + [ + 'arn:', + { + 'Ref': 'AWS::Partition', + }, + ':sns:us-east-1:11111111111:topicName', + ], + ], + }, + 'Endpoint': { + 'Fn::GetAtt': [ + 'MyQueueE6CA6235', + 'Arn', + ], + }, + 'Region': 'us-east-1', + }, + }, + }, + }); +}); test('queue subscription with user provided dlq', () => { const queue = new sqs.Queue(stack, 'MyQueue'); const dlQueue = new sqs.Queue(stack, 'DeadLetterQueue', { @@ -712,6 +1161,243 @@ test('lambda subscription', () => { }); }); +test('lambda subscription, cross region env agnostic', () => { + const app = new App(); + const topicStack = new Stack(app, 'TopicStack', {}); + const lambdaStack = new Stack(app, 'LambdaStack', {}); + + const topic1 = new sns.Topic(topicStack, 'Topic', { + topicName: 'topicName', + displayName: 'displayName', + }); + const fction = new lambda.Function(lambdaStack, 'MyFunc', { + runtime: lambda.Runtime.NODEJS_10_X, + handler: 'index.handler', + code: lambda.Code.fromInline('exports.handler = function(e, c, cb) { return cb() }'), + }); + + topic1.addSubscription(new subs.LambdaSubscription(fction)); + + expect(lambdaStack).toMatchTemplate({ + 'Resources': { + 'MyFuncServiceRole54065130': { + 'Type': 'AWS::IAM::Role', + 'Properties': { + 'AssumeRolePolicyDocument': { + 'Statement': [ + { + 'Action': 'sts:AssumeRole', + 'Effect': 'Allow', + 'Principal': { + 'Service': 'lambda.amazonaws.com', + }, + }, + ], + 'Version': '2012-10-17', + }, + 'ManagedPolicyArns': [ + { + 'Fn::Join': [ + '', + [ + 'arn:', + { + 'Ref': 'AWS::Partition', + }, + ':iam::aws:policy/service-role/AWSLambdaBasicExecutionRole', + ], + ], + }, + ], + }, + }, + 'MyFunc8A243A2C': { + 'Type': 'AWS::Lambda::Function', + 'Properties': { + 'Code': { + 'ZipFile': 'exports.handler = function(e, c, cb) { return cb() }', + }, + 'Role': { + 'Fn::GetAtt': [ + 'MyFuncServiceRole54065130', + 'Arn', + ], + }, + 'Handler': 'index.handler', + 'Runtime': 'nodejs10.x', + }, + 'DependsOn': [ + 'MyFuncServiceRole54065130', + ], + }, + 'MyFuncAllowInvokeTopicStackTopicFBF76EB3D4A699EF': { + 'Type': 'AWS::Lambda::Permission', + 'Properties': { + 'Action': 'lambda:InvokeFunction', + 'FunctionName': { + 'Fn::GetAtt': [ + 'MyFunc8A243A2C', + 'Arn', + ], + }, + 'Principal': 'sns.amazonaws.com', + 'SourceArn': { + 'Fn::ImportValue': 'TopicStack:ExportsOutputRefTopicBFC7AF6ECB4A357A', + }, + }, + }, + 'MyFuncTopic3B7C24C5': { + 'Type': 'AWS::SNS::Subscription', + 'Properties': { + 'Protocol': 'lambda', + 'TopicArn': { + 'Fn::ImportValue': 'TopicStack:ExportsOutputRefTopicBFC7AF6ECB4A357A', + }, + 'Endpoint': { + 'Fn::GetAtt': [ + 'MyFunc8A243A2C', + 'Arn', + ], + }, + }, + }, + }, + }); +}); + +test('lambda subscription, cross region', () => { + const app = new App(); + const topicStack = new Stack(app, 'TopicStack', { + env: { + account: '11111111111', + region: 'us-east-1', + }, + }); + const lambdaStack = new Stack(app, 'LambdaStack', { + env: { + account: '11111111111', + region: 'us-east-2', + }, + }); + + const topic1 = new sns.Topic(topicStack, 'Topic', { + topicName: 'topicName', + displayName: 'displayName', + }); + const fction = new lambda.Function(lambdaStack, 'MyFunc', { + runtime: lambda.Runtime.NODEJS_10_X, + handler: 'index.handler', + code: lambda.Code.fromInline('exports.handler = function(e, c, cb) { return cb() }'), + }); + + topic1.addSubscription(new subs.LambdaSubscription(fction)); + + expect(lambdaStack).toMatchTemplate({ + 'Resources': { + 'MyFuncServiceRole54065130': { + 'Type': 'AWS::IAM::Role', + 'Properties': { + 'AssumeRolePolicyDocument': { + 'Statement': [ + { + 'Action': 'sts:AssumeRole', + 'Effect': 'Allow', + 'Principal': { + 'Service': 'lambda.amazonaws.com', + }, + }, + ], + 'Version': '2012-10-17', + }, + 'ManagedPolicyArns': [ + { + 'Fn::Join': [ + '', + [ + 'arn:', + { + 'Ref': 'AWS::Partition', + }, + ':iam::aws:policy/service-role/AWSLambdaBasicExecutionRole', + ], + ], + }, + ], + }, + }, + 'MyFunc8A243A2C': { + 'Type': 'AWS::Lambda::Function', + 'Properties': { + 'Code': { + 'ZipFile': 'exports.handler = function(e, c, cb) { return cb() }', + }, + 'Role': { + 'Fn::GetAtt': [ + 'MyFuncServiceRole54065130', + 'Arn', + ], + }, + 'Handler': 'index.handler', + 'Runtime': 'nodejs10.x', + }, + 'DependsOn': [ + 'MyFuncServiceRole54065130', + ], + }, + 'MyFuncAllowInvokeTopicStackTopicFBF76EB3D4A699EF': { + 'Type': 'AWS::Lambda::Permission', + 'Properties': { + 'Action': 'lambda:InvokeFunction', + 'FunctionName': { + 'Fn::GetAtt': [ + 'MyFunc8A243A2C', + 'Arn', + ], + }, + 'Principal': 'sns.amazonaws.com', + 'SourceArn': { + 'Fn::Join': [ + '', + [ + 'arn:', + { + 'Ref': 'AWS::Partition', + }, + ':sns:us-east-1:11111111111:topicName', + ], + ], + }, + }, + }, + 'MyFuncTopic3B7C24C5': { + 'Type': 'AWS::SNS::Subscription', + 'Properties': { + 'Protocol': 'lambda', + 'TopicArn': { + 'Fn::Join': [ + '', + [ + 'arn:', + { + 'Ref': 'AWS::Partition', + }, + ':sns:us-east-1:11111111111:topicName', + ], + ], + }, + 'Endpoint': { + 'Fn::GetAtt': [ + 'MyFunc8A243A2C', + 'Arn', + ], + }, + 'Region': 'us-east-1', + }, + }, + }, + }); +}); + test('email subscription', () => { topic.addSubscription(new subs.EmailSubscription('foo@bar.com')); From 0885394d6e128745ba1c3784c2c468bcfdd56b50 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Thu, 11 Nov 2021 17:24:39 +0100 Subject: [PATCH 21/23] chore(integ): clearly show versions being tested (#17469) Three changes: - Clearly show all versions involved in regression tests (framework, CLI and tests). - Stop printing all extracted files, it's very distracting. - Also copy over the `helper` directory from previous tests. Otherwise, any backwards incompatible changes we make in the tests themselves are going to break the tests. Not an issue right now, but it might be in the future. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/aws-cdk/test/integ/test-cli-regression.bash | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/aws-cdk/test/integ/test-cli-regression.bash b/packages/aws-cdk/test/integ/test-cli-regression.bash index 12cbe81791d65..1770ee269d87f 100644 --- a/packages/aws-cdk/test/integ/test-cli-regression.bash +++ b/packages/aws-cdk/test/integ/test-cli-regression.bash @@ -54,12 +54,13 @@ function run_regression_against_framework_version() { echo "Downloading aws-cdk ${PREVIOUS_VERSION} tarball from npm" npm pack aws-cdk@${PREVIOUS_VERSION} - tar -zxvf aws-cdk-${PREVIOUS_VERSION}.tgz + tar -zxf aws-cdk-${PREVIOUS_VERSION}.tgz rm -rf ${integ_under_test} echo "Copying integration tests of version ${PREVIOUS_VERSION} to ${integ_under_test} (dont worry, its gitignored)" cp -r ${temp_dir}/package/test/integ/cli "${integ_under_test}" + cp -r ${temp_dir}/package/test/integ/helpers "${integ_under_test}" patch_dir="${integdir}/cli-regression-patches/v${PREVIOUS_VERSION}" # delete possibly stale junit.xml file @@ -73,5 +74,10 @@ function run_regression_against_framework_version() { # the framework version to use is determined by the caller as the first argument. # its a variable name indirection. - FRAMEWORK_VERSION=${!FRAMEWORK_VERSION_IDENTIFIER} ${integ_under_test}/test.sh + export FRAMEWORK_VERSION=${!FRAMEWORK_VERSION_IDENTIFIER} + + # Show the versions we settled on + echo "♈️ Regression testing [cli $(cdk --version)] against [framework ${FRAMEWORK_VERSION}] using [tests ${PREVIOUS_VERSION}}]" + + ${integ_under_test}/test.sh } From 8fa293a79fc8957410637dfd3a4de2069dead36b Mon Sep 17 00:00:00 2001 From: pnathan-vr <85522651+pnathan-vr@users.noreply.github.com> Date: Thu, 11 Nov 2021 12:03:30 -0500 Subject: [PATCH 22/23] feat(eks): Allow passing of custom IAM role to Kube Ctl Lambda (#17196) Adds the parameter `kubectlLambdaRole?` to the EKS `Cluster` construct. This IAM role gets passed to the cluster's `KubeCtlProvider` nested stack's lambda handler for running KubeCtl commands against the cluster. --- packages/@aws-cdk/aws-eks/README.md | 2 + packages/@aws-cdk/aws-eks/lib/cluster.ts | 44 +++++++++++++++++++ .../@aws-cdk/aws-eks/lib/kubectl-provider.ts | 1 + .../@aws-cdk/aws-eks/test/cluster.test.ts | 36 +++++++++++++++ 4 files changed, 83 insertions(+) diff --git a/packages/@aws-cdk/aws-eks/README.md b/packages/@aws-cdk/aws-eks/README.md index e1d78b774450d..493168fa2689a 100644 --- a/packages/@aws-cdk/aws-eks/README.md +++ b/packages/@aws-cdk/aws-eks/README.md @@ -537,6 +537,8 @@ Breaking this down, it means that if the endpoint exposes private access (via `E If the endpoint does not expose private access (via `EndpointAccess.PUBLIC`) **or** the VPC does not contain private subnets, the function will not be provisioned within the VPC. +If your use-case requires control over the IAM role that the KubeCtl Handler assumes, a custom role can be passed through the ClusterProps (as `kubectlLambdaRole`) of the EKS Cluster construct. + #### Cluster Handler The `ClusterHandler` is a set of Lambda functions (`onEventHandler`, `isCompleteHandler`) responsible for interacting with the EKS API in order to control the cluster lifecycle. To provision these functions inside the VPC, set the `placeClusterHandlerInVpc` property to `true`. This will place the functions inside the private subnets of the VPC based on the selection strategy specified in the [`vpcSubnets`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-eks.Cluster.html#vpcsubnetsspan-classapi-icon-api-icon-experimental-titlethis-api-element-is-experimental-it-may-change-without-noticespan) property. diff --git a/packages/@aws-cdk/aws-eks/lib/cluster.ts b/packages/@aws-cdk/aws-eks/lib/cluster.ts index 2c762ab666327..3f78399b6a8e8 100644 --- a/packages/@aws-cdk/aws-eks/lib/cluster.ts +++ b/packages/@aws-cdk/aws-eks/lib/cluster.ts @@ -117,6 +117,15 @@ export interface ICluster extends IResource, ec2.IConnectable { */ readonly kubectlPrivateSubnets?: ec2.ISubnet[]; + /** + * An IAM role that can perform kubectl operations against this cluster. + * + * The role should be mapped to the `system:masters` Kubernetes RBAC role. + * + * This role is directly passed to the lambda handler that sends Kube Ctl commands to the cluster. + */ + readonly kubectlLambdaRole?: iam.IRole; + /** * An AWS Lambda layer that includes `kubectl`, `helm` and the `aws` CLI. * @@ -271,6 +280,18 @@ export interface ClusterAttributes { */ readonly kubectlRoleArn?: string; + /** + * An IAM role that can perform kubectl operations against this cluster. + * + * The role should be mapped to the `system:masters` Kubernetes RBAC role. + * + * This role is directly passed to the lambda handler that sends Kube Ctl commands + * to the cluster. + * @default - if not specified, the default role created by a lambda function will + * be used. + */ + readonly kubectlLambdaRole?: iam.IRole; + /** * Environment variables to use when running `kubectl` against this cluster. * @default - no additional variables @@ -702,6 +723,14 @@ export interface ClusterProps extends ClusterOptions { * @default NODEGROUP */ readonly defaultCapacityType?: DefaultCapacityType; + + + /** + * The IAM role to pass to the Kubectl Lambda Handler. + * + * @default - Default Lambda IAM Execution Role + */ + readonly kubectlLambdaRole?: iam.IRole; } /** @@ -771,6 +800,7 @@ abstract class ClusterBase extends Resource implements ICluster { public abstract readonly clusterSecurityGroup: ec2.ISecurityGroup; public abstract readonly clusterEncryptionConfigKeyArn: string; public abstract readonly kubectlRole?: iam.IRole; + public abstract readonly kubectlLambdaRole?: iam.IRole; public abstract readonly kubectlEnvironment?: { [key: string]: string }; public abstract readonly kubectlSecurityGroup?: ec2.ISecurityGroup; public abstract readonly kubectlPrivateSubnets?: ec2.ISubnet[]; @@ -1077,6 +1107,18 @@ export class Cluster extends ClusterBase { */ public readonly kubectlRole?: iam.IRole; + /** + * An IAM role that can perform kubectl operations against this cluster. + * + * The role should be mapped to the `system:masters` Kubernetes RBAC role. + * + * This role is directly passed to the lambda handler that sends Kube Ctl commands to the cluster. + * @default - if not specified, the default role created by a lambda function will + * be used. + */ + + public readonly kubectlLambdaRole?: iam.IRole; + /** * Custom environment variables when running `kubectl` against this cluster. */ @@ -1195,6 +1237,7 @@ export class Cluster extends ClusterBase { this.prune = props.prune ?? true; this.vpc = props.vpc || new ec2.Vpc(this, 'DefaultVpc'); this.version = props.version; + this.kubectlLambdaRole = props.kubectlLambdaRole ? props.kubectlLambdaRole : undefined; this.tagSubnets(); @@ -1867,6 +1910,7 @@ class ImportedCluster extends ClusterBase { public readonly clusterArn: string; public readonly connections = new ec2.Connections(); public readonly kubectlRole?: iam.IRole; + public readonly kubectlLambdaRole?: iam.IRole; public readonly kubectlEnvironment?: { [key: string]: string; } | undefined; public readonly kubectlSecurityGroup?: ec2.ISecurityGroup | undefined; public readonly kubectlPrivateSubnets?: ec2.ISubnet[] | undefined; diff --git a/packages/@aws-cdk/aws-eks/lib/kubectl-provider.ts b/packages/@aws-cdk/aws-eks/lib/kubectl-provider.ts index b5bd8ed51b876..0e5db3c6a51e3 100644 --- a/packages/@aws-cdk/aws-eks/lib/kubectl-provider.ts +++ b/packages/@aws-cdk/aws-eks/lib/kubectl-provider.ts @@ -77,6 +77,7 @@ export class KubectlProvider extends NestedStack { description: 'onEvent handler for EKS kubectl resource provider', memorySize, environment: cluster.kubectlEnvironment, + role: cluster.kubectlLambdaRole ? cluster.kubectlLambdaRole : undefined, // defined only when using private access vpc: cluster.kubectlPrivateSubnets ? cluster.vpc : undefined, diff --git a/packages/@aws-cdk/aws-eks/test/cluster.test.ts b/packages/@aws-cdk/aws-eks/test/cluster.test.ts index 63a17d9c5d64d..b8b9c91042f32 100644 --- a/packages/@aws-cdk/aws-eks/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-eks/test/cluster.test.ts @@ -2202,6 +2202,42 @@ describe('cluster', () => { }, }); + }); + + test('kubectl provider passes iam role environment to kube ctl lambda', () => { + + const { stack } = testFixture(); + + const kubectlRole = new iam.Role(stack, 'KubectlIamRole', { + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'), + }); + + // using _ syntax to silence warning about _cluster not being used, when it is + const cluster = new eks.Cluster(stack, 'Cluster1', { + version: CLUSTER_VERSION, + prune: false, + endpointAccess: eks.EndpointAccess.PRIVATE, + kubectlLambdaRole: kubectlRole, + }); + + cluster.addManifest('resource', { + kind: 'ConfigMap', + apiVersion: 'v1', + data: { + hello: 'world', + }, + metadata: { + name: 'config-map', + }, + }); + + // the kubectl provider is inside a nested stack. + const nested = stack.node.tryFindChild('@aws-cdk/aws-eks.KubectlProvider') as cdk.NestedStack; + expect(nested).toHaveResourceLike('AWS::Lambda::Function', { + Role: { + Ref: 'referencetoStackKubectlIamRole02F8947EArn', + }, + }); }); From f7c3217a731804f014526e10b414a9e7f27d575b Mon Sep 17 00:00:00 2001 From: Ibrahim Cesar Date: Thu, 11 Nov 2021 14:50:36 -0300 Subject: [PATCH 23/23] fix(redshift): tableNameSuffix evaluation (#17213) - Check if the generateSuffix is explicitly 'true'; - Closes #17064 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-redshift/lib/private/database-query-provider/table.ts | 2 +- packages/@aws-cdk/aws-redshift/lib/private/handler-props.ts | 2 +- packages/@aws-cdk/aws-redshift/lib/table.ts | 2 +- .../aws-redshift/test/database-query-provider/table.test.ts | 4 ++-- .../@aws-cdk/aws-redshift/test/integ.database.expected.json | 4 ++-- packages/@aws-cdk/aws-redshift/test/table.test.ts | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/@aws-cdk/aws-redshift/lib/private/database-query-provider/table.ts b/packages/@aws-cdk/aws-redshift/lib/private/database-query-provider/table.ts index a2e2a4dc4bee9..bc0c1d44971ff 100644 --- a/packages/@aws-cdk/aws-redshift/lib/private/database-query-provider/table.ts +++ b/packages/@aws-cdk/aws-redshift/lib/private/database-query-provider/table.ts @@ -6,7 +6,7 @@ import { ClusterProps, executeStatement } from './util'; export async function handler(props: TableHandlerProps & ClusterProps, event: AWSLambda.CloudFormationCustomResourceEvent) { const tableNamePrefix = props.tableName.prefix; - const tableNameSuffix = props.tableName.generateSuffix ? `${event.RequestId.substring(0, 8)}` : ''; + const tableNameSuffix = props.tableName.generateSuffix === 'true' ? `${event.RequestId.substring(0, 8)}` : ''; const tableColumns = props.tableColumns; const clusterProps = props; diff --git a/packages/@aws-cdk/aws-redshift/lib/private/handler-props.ts b/packages/@aws-cdk/aws-redshift/lib/private/handler-props.ts index b00cc667a2ced..0cc1c49066bf7 100644 --- a/packages/@aws-cdk/aws-redshift/lib/private/handler-props.ts +++ b/packages/@aws-cdk/aws-redshift/lib/private/handler-props.ts @@ -15,7 +15,7 @@ export interface UserHandlerProps { export interface TableHandlerProps { readonly tableName: { readonly prefix: string; - readonly generateSuffix: boolean; + readonly generateSuffix: string; }; readonly tableColumns: Column[]; } diff --git a/packages/@aws-cdk/aws-redshift/lib/table.ts b/packages/@aws-cdk/aws-redshift/lib/table.ts index 337abdedd00a1..a2d4904f0361a 100644 --- a/packages/@aws-cdk/aws-redshift/lib/table.ts +++ b/packages/@aws-cdk/aws-redshift/lib/table.ts @@ -194,7 +194,7 @@ export class Table extends TableBase { properties: { tableName: { prefix: props.tableName ?? cdk.Names.uniqueId(this), - generateSuffix: !props.tableName, + generateSuffix: !props.tableName ? 'true' : 'false', }, tableColumns: this.tableColumns, }, diff --git a/packages/@aws-cdk/aws-redshift/test/database-query-provider/table.test.ts b/packages/@aws-cdk/aws-redshift/test/database-query-provider/table.test.ts index 956efca1ab81f..40456bcf6d7ca 100644 --- a/packages/@aws-cdk/aws-redshift/test/database-query-provider/table.test.ts +++ b/packages/@aws-cdk/aws-redshift/test/database-query-provider/table.test.ts @@ -10,7 +10,7 @@ const physicalResourceId = 'PhysicalResourceId'; const resourceProperties = { tableName: { prefix: tableNamePrefix, - generateSuffix: true, + generateSuffix: 'true', }, tableColumns, clusterName, @@ -64,7 +64,7 @@ describe('create', () => { ...resourceProperties, tableName: { ...resourceProperties.tableName, - generateSuffix: false, + generateSuffix: 'false', }, }; diff --git a/packages/@aws-cdk/aws-redshift/test/integ.database.expected.json b/packages/@aws-cdk/aws-redshift/test/integ.database.expected.json index 4cfb1faea5118..f38a1d5b3818e 100644 --- a/packages/@aws-cdk/aws-redshift/test/integ.database.expected.json +++ b/packages/@aws-cdk/aws-redshift/test/integ.database.expected.json @@ -1369,7 +1369,7 @@ "databaseName": "my_db", "tableName": { "prefix": "awscdkredshiftclusterdatabaseTable24923533", - "generateSuffix": true + "generateSuffix": "true" }, "tableColumns": [ { @@ -1412,4 +1412,4 @@ "Description": "Artifact hash for asset \"daeb79e3cee39c9b902dc0d5c780223e227ed573ea60976252947adab5fb2be1\"" } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-redshift/test/table.test.ts b/packages/@aws-cdk/aws-redshift/test/table.test.ts index 97f66b57042f5..dda05eb9cc063 100644 --- a/packages/@aws-cdk/aws-redshift/test/table.test.ts +++ b/packages/@aws-cdk/aws-redshift/test/table.test.ts @@ -40,7 +40,7 @@ describe('cluster table', () => { Template.fromStack(stack).hasResourceProperties('Custom::RedshiftDatabaseQuery', { tableName: { prefix: 'Table', - generateSuffix: true, + generateSuffix: 'true', }, tableColumns, }); @@ -67,7 +67,7 @@ describe('cluster table', () => { Template.fromStack(stack).hasResourceProperties('Custom::RedshiftDatabaseQuery', { tableName: { prefix: tableName, - generateSuffix: false, + generateSuffix: 'false', }, }); });