From 6182d33fdd3b4714e1888305a2e1c689a10e38ea Mon Sep 17 00:00:00 2001 From: Eli Polonsky Date: Fri, 23 Sep 2022 12:34:04 +0300 Subject: [PATCH 01/24] fix: cannot use values that return an instance of a deprecated class for non TS / JS language (#22204) See https://github.com/aws/jsii/releases/tag/v1.68.0 Fixes https://github.com/aws/aws-cdk/issues/21902 ---- ### All Submissions: * [ ] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- package.json | 8 +- packages/aws-cdk/THIRD_PARTY_LICENSES | 2 +- packages/aws-cdk/package.json | 2 +- packages/awslint/package.json | 4 +- packages/cdk-dasm/package.json | 2 +- tools/@aws-cdk/cdk-build-tools/package.json | 6 +- tools/@aws-cdk/cfn2ts/package.json | 2 +- yarn.lock | 110 ++++++++++++++++---- 8 files changed, 102 insertions(+), 34 deletions(-) diff --git a/package.json b/package.json index 811bde81c8e7b..6478a32d1a89c 100644 --- a/package.json +++ b/package.json @@ -23,10 +23,10 @@ "fs-extra": "^9.1.0", "graceful-fs": "^4.2.10", "jest-junit": "^13.2.0", - "jsii-diff": "^1.67.0", - "jsii-pacmak": "^1.67.0", - "jsii-reflect": "^1.67.0", - "jsii-rosetta": "^1.67.0", + "jsii-diff": "^1.68.0", + "jsii-pacmak": "^1.68.0", + "jsii-reflect": "^1.68.0", + "jsii-rosetta": "^1.68.0", "lerna": "^4.0.0", "patch-package": "^6.4.7", "semver": "^6.3.0", diff --git a/packages/aws-cdk/THIRD_PARTY_LICENSES b/packages/aws-cdk/THIRD_PARTY_LICENSES index bed42c038f8f8..1bc62f44d98f3 100644 --- a/packages/aws-cdk/THIRD_PARTY_LICENSES +++ b/packages/aws-cdk/THIRD_PARTY_LICENSES @@ -1,6 +1,6 @@ The aws-cdk package includes the following third-party software/licensing: -** @jsii/check-node@1.67.0 - https://www.npmjs.com/package/@jsii/check-node/v/1.67.0 | Apache-2.0 +** @jsii/check-node@1.68.0 - https://www.npmjs.com/package/@jsii/check-node/v/1.68.0 | Apache-2.0 jsii Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index fca38edf6c646..2865fd762751d 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -95,7 +95,7 @@ "@aws-cdk/cloudformation-diff": "0.0.0", "@aws-cdk/cx-api": "0.0.0", "@aws-cdk/region-info": "0.0.0", - "@jsii/check-node": "1.67.0", + "@jsii/check-node": "1.68.0", "archiver": "^5.3.1", "aws-sdk": "^2.1093.0", "camelcase": "^6.3.0", diff --git a/packages/awslint/package.json b/packages/awslint/package.json index 3187720da8d96..405e61168a7e9 100644 --- a/packages/awslint/package.json +++ b/packages/awslint/package.json @@ -18,11 +18,11 @@ "awslint": "bin/awslint" }, "dependencies": { - "@jsii/spec": "^1.67.0", + "@jsii/spec": "^1.68.0", "camelcase": "^6.3.0", "chalk": "^4", "fs-extra": "^9.1.0", - "jsii-reflect": "^1.67.0", + "jsii-reflect": "^1.68.0", "yargs": "^16.2.0" }, "devDependencies": { diff --git a/packages/cdk-dasm/package.json b/packages/cdk-dasm/package.json index 3f067e24df31e..a2c88d462e926 100644 --- a/packages/cdk-dasm/package.json +++ b/packages/cdk-dasm/package.json @@ -30,7 +30,7 @@ }, "license": "Apache-2.0", "dependencies": { - "codemaker": "^1.67.0", + "codemaker": "^1.68.0", "yaml": "1.10.2" }, "devDependencies": { diff --git a/tools/@aws-cdk/cdk-build-tools/package.json b/tools/@aws-cdk/cdk-build-tools/package.json index 53459630a5954..073a44121dd3a 100644 --- a/tools/@aws-cdk/cdk-build-tools/package.json +++ b/tools/@aws-cdk/cdk-build-tools/package.json @@ -57,9 +57,9 @@ "fs-extra": "^9.1.0", "jest": "^27.5.1", "jest-junit": "^13.2.0", - "jsii": "^1.67.0", - "jsii-pacmak": "^1.67.0", - "jsii-reflect": "^1.67.0", + "jsii": "^1.68.0", + "jsii-pacmak": "^1.68.0", + "jsii-reflect": "^1.68.0", "markdownlint-cli": "^0.32.2", "nyc": "^15.1.0", "semver": "^7.3.7", diff --git a/tools/@aws-cdk/cfn2ts/package.json b/tools/@aws-cdk/cfn2ts/package.json index 2d37e97a1e597..e2069fc811422 100644 --- a/tools/@aws-cdk/cfn2ts/package.json +++ b/tools/@aws-cdk/cfn2ts/package.json @@ -32,7 +32,7 @@ "license": "Apache-2.0", "dependencies": { "@aws-cdk/cfnspec": "0.0.0", - "codemaker": "^1.67.0", + "codemaker": "^1.68.0", "fast-json-patch": "^3.1.1", "fs-extra": "^9.1.0", "yargs": "^16.2.0" diff --git a/yarn.lock b/yarn.lock index 2875c76eba2fa..498dd71f123a1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -669,6 +669,14 @@ chalk "^4.1.2" semver "^7.3.7" +"@jsii/check-node@1.68.0": + version "1.68.0" + resolved "https://registry.npmjs.org/@jsii/check-node/-/check-node-1.68.0.tgz#2ee6fe9a98b3427e290b9d88f25c4b8778e036ff" + integrity sha512-fezDjqILn4YDt70eojXxWCcE7P5JZknhTeMY7shhp03BZ+S6dMn51mH1x3bdlhyeSTDCU4sxy5zA7C68dGkuNg== + dependencies: + chalk "^4.1.2" + semver "^7.3.7" + "@jsii/spec@1.67.0", "@jsii/spec@^1.67.0": version "1.67.0" resolved "https://registry.npmjs.org/@jsii/spec/-/spec-1.67.0.tgz#d318739b4e8e82118e505721917e86b51fb1585b" @@ -676,6 +684,13 @@ dependencies: ajv "^8.11.0" +"@jsii/spec@1.68.0", "@jsii/spec@^1.68.0": + version "1.68.0" + resolved "https://registry.npmjs.org/@jsii/spec/-/spec-1.68.0.tgz#3108f176a5ab0d85b2474e533cb4b9558069d238" + integrity sha512-PO5JdBZupZXY7YC14pUJfGf6Xq85kdp6ONoudetv9nhUBx4cbmPYY527OjqTNkpLBWFEugQidgXe2W9WLZ8t7A== + dependencies: + ajv "^8.11.0" + "@lerna/add@4.0.0": version "4.0.0" resolved "https://registry.npmjs.org/@lerna/add/-/add-4.0.0.tgz#c36f57d132502a57b9e7058d1548b7a565ef183f" @@ -3193,10 +3208,10 @@ co@^4.6.0: resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== -codemaker@^1.67.0: - version "1.67.0" - resolved "https://registry.npmjs.org/codemaker/-/codemaker-1.67.0.tgz#adba45ef5058219cbd84e7b4bd70dfa60b98c3a2" - integrity sha512-ChqnwbXIDIq8EAju+OMOnuzJ5MQWXvFaf4PMZo2RLbLiqUfx4+ATv/i1y0rY5uZ7nvKC8lRfwmbRsgsbWQHAhA== +codemaker@^1.68.0: + version "1.68.0" + resolved "https://registry.npmjs.org/codemaker/-/codemaker-1.68.0.tgz#db4c0be1bb0085a78fed25eb1c420206e0e33dd8" + integrity sha512-NB1h7lrd5qDi4qkdHlRVGihvn/motbaK/NUo5yvq0TOlZqhG2kg47wLuXSbpCvaWVTTPtg9Y0tEbgIxxQACMxg== dependencies: camelcase "^6.3.0" decamelize "^5.0.1" @@ -4778,7 +4793,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^3.2.11, fast-glob@^3.2.9: +fast-glob@^3.2.11, fast-glob@^3.2.12, fast-glob@^3.2.9: version "3.2.12" resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== @@ -6695,32 +6710,32 @@ jsesc@^2.5.1: resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== -jsii-diff@^1.67.0: - version "1.67.0" - resolved "https://registry.npmjs.org/jsii-diff/-/jsii-diff-1.67.0.tgz#b6b516f8c5da9c4d6a50024fbc8fa61ef7b388d8" - integrity sha512-dB9GgYk+g5k7Kbusyun7bgCkbh6xwVomZC8jf6KfauIcWFPeacfe3ZtArUnJ3OvJqId7UJEvfiUQjllxn2KRNQ== +jsii-diff@^1.68.0: + version "1.68.0" + resolved "https://registry.npmjs.org/jsii-diff/-/jsii-diff-1.68.0.tgz#0418ed54c6485b1b9f24b500b8d3f131dfad4f00" + integrity sha512-o4xKy9/1kfyMS0WV9n41zl5ACNoQVk/8tDUUt+9phnbXjhQBR2PKbrGVor1cXQBeOpLknxFsMwZyCMnHxYeI6g== dependencies: - "@jsii/check-node" "1.67.0" - "@jsii/spec" "^1.67.0" + "@jsii/check-node" "1.68.0" + "@jsii/spec" "^1.68.0" fs-extra "^10.1.0" - jsii-reflect "^1.67.0" + jsii-reflect "^1.68.0" log4js "^6.6.1" yargs "^16.2.0" -jsii-pacmak@^1.67.0: - version "1.67.0" - resolved "https://registry.npmjs.org/jsii-pacmak/-/jsii-pacmak-1.67.0.tgz#5b37bcd1e1b6cc56f59a3d0f8b8e9fd40a86bcbf" - integrity sha512-C5Lva1F4tBtZWboQwd3YFG8C7F7/IVhOq8rKHorV2tH6CX6i+L7UqdVdqTdkvBY0o1C+Jvbdca5J3E5O235eDw== +jsii-pacmak@^1.68.0: + version "1.68.0" + resolved "https://registry.npmjs.org/jsii-pacmak/-/jsii-pacmak-1.68.0.tgz#aec4b01bb62ee74669950839da19937baa1909cc" + integrity sha512-eW9Mgv5SstlQfuKdd5PNqleXLD5LzgW/e1wHWXAY4W3+kO7cPvDFqVQVogz5CXA4xj2f7uZ1Wcrpinb/nPe43Q== dependencies: - "@jsii/check-node" "1.67.0" - "@jsii/spec" "^1.67.0" + "@jsii/check-node" "1.68.0" + "@jsii/spec" "^1.68.0" clone "^2.1.2" - codemaker "^1.67.0" + codemaker "^1.68.0" commonmark "^0.30.0" escape-string-regexp "^4.0.0" fs-extra "^10.1.0" - jsii-reflect "^1.67.0" - jsii-rosetta "^1.67.0" + jsii-reflect "^1.68.0" + jsii-rosetta "^1.68.0" semver "^7.3.7" spdx-license-list "^6.6.0" xmlbuilder "^15.1.1" @@ -6738,6 +6753,18 @@ jsii-reflect@^1.67.0: oo-ascii-tree "^1.67.0" yargs "^16.2.0" +jsii-reflect@^1.68.0: + version "1.68.0" + resolved "https://registry.npmjs.org/jsii-reflect/-/jsii-reflect-1.68.0.tgz#87ed8693e34cb3cbc8081c7724a384c021b1e751" + integrity sha512-7dEw/nfS7DKZaRDwG2+AfRmfn+Qirj3hEcEGBIWfkRu1vCsFERnUFCm0gYo/1OLJjzUr69Jh5SHR/RnYvC9N2Q== + dependencies: + "@jsii/check-node" "1.68.0" + "@jsii/spec" "^1.68.0" + chalk "^4" + fs-extra "^10.1.0" + oo-ascii-tree "^1.68.0" + yargs "^16.2.0" + jsii-rosetta@^1.67.0: version "1.67.0" resolved "https://registry.npmjs.org/jsii-rosetta/-/jsii-rosetta-1.67.0.tgz#f2ee67511e29299cb212559340eff68b5522b551" @@ -6755,6 +6782,23 @@ jsii-rosetta@^1.67.0: workerpool "^6.2.1" yargs "^16.2.0" +jsii-rosetta@^1.68.0: + version "1.68.0" + resolved "https://registry.npmjs.org/jsii-rosetta/-/jsii-rosetta-1.68.0.tgz#3759e69ab8d9b6a6168741aed2cf3194b3dfa3ed" + integrity sha512-IJMh1SE/BnsOWpMICYssK25pa7oyzJqidg+kbaeDrFRaYN6KUCKB5T+FLffmnAygLpKHS5EAgK1B8Tt85ZYpJA== + dependencies: + "@jsii/check-node" "1.68.0" + "@jsii/spec" "1.68.0" + "@xmldom/xmldom" "^0.8.2" + commonmark "^0.30.0" + fast-glob "^3.2.12" + jsii "1.68.0" + semver "^7.3.7" + semver-intersect "^1.4.0" + typescript "~3.9.10" + workerpool "^6.2.1" + yargs "^16.2.0" + jsii@1.67.0, jsii@^1.67.0: version "1.67.0" resolved "https://registry.npmjs.org/jsii/-/jsii-1.67.0.tgz#e529e24656eac589c59328301d5363742e8ca023" @@ -6774,6 +6818,25 @@ jsii@1.67.0, jsii@^1.67.0: typescript "~3.9.10" yargs "^16.2.0" +jsii@1.68.0, jsii@^1.68.0: + version "1.68.0" + resolved "https://registry.npmjs.org/jsii/-/jsii-1.68.0.tgz#497cfacb13bb79029d4e322896278d005c65ab1e" + integrity sha512-L6ufFo+XqWY93VONxNZ3rL9qIaYPFr0P4gBHk1O6T9/HGZWMnYBjEqhWUb36ur1NBldNmMlSnoZFFitt7HksUQ== + dependencies: + "@jsii/check-node" "1.68.0" + "@jsii/spec" "^1.68.0" + case "^1.6.3" + chalk "^4" + fast-deep-equal "^3.1.3" + fs-extra "^10.1.0" + log4js "^6.6.1" + semver "^7.3.7" + semver-intersect "^1.4.0" + sort-json "^2.0.1" + spdx-license-list "^6.6.0" + typescript "~3.9.10" + yargs "^16.2.0" + json-buffer@3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" @@ -8268,6 +8331,11 @@ oo-ascii-tree@^1.67.0: resolved "https://registry.npmjs.org/oo-ascii-tree/-/oo-ascii-tree-1.67.0.tgz#229d3abdeefa5de106acd56c68671d2316227a80" integrity sha512-qnmzjsmOg8szLSbL3RRUi3bog/t3dDEexcuX4bkSJnvTHJqYUg2vdoTIjkbI67YT231fk7C8PRusMIOgrrBYUg== +oo-ascii-tree@^1.68.0: + version "1.68.0" + resolved "https://registry.npmjs.org/oo-ascii-tree/-/oo-ascii-tree-1.68.0.tgz#28aa8b9782bc48be06e9556476377cc8517e3d9f" + integrity sha512-4SQqsfgIDVLseCk7epcGRVh2KDPiARR/jQhze+02nV4qafWdjjZ6nQEnnCgy4fdBSj5KPup4Vzwf0EOTuywFDg== + open@^7.4.2: version "7.4.2" resolved "https://registry.npmjs.org/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" From 70404b7dcb8cb4d31296320ff5576bf6311c1c2c Mon Sep 17 00:00:00 2001 From: epolon Date: Fri, 23 Sep 2022 12:42:14 +0300 Subject: [PATCH 02/24] chore(release): 2.43.1 --- CHANGELOG.v2.alpha.md | 2 ++ CHANGELOG.v2.md | 7 +++++++ version.v2.json | 4 ++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.v2.alpha.md b/CHANGELOG.v2.alpha.md index 905c77b1b309c..2194ff30ac6b0 100644 --- a/CHANGELOG.v2.alpha.md +++ b/CHANGELOG.v2.alpha.md @@ -2,6 +2,8 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [2.43.1-alpha.0](https://github.com/aws/aws-cdk/compare/v2.43.0-alpha.0...v2.43.1-alpha.0) (2022-09-23) + ## [2.43.0-alpha.0](https://github.com/aws/aws-cdk/compare/v2.42.1-alpha.0...v2.43.0-alpha.0) (2022-09-21) diff --git a/CHANGELOG.v2.md b/CHANGELOG.v2.md index 1131ad35ddb85..06468cc8220d0 100644 --- a/CHANGELOG.v2.md +++ b/CHANGELOG.v2.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [2.43.1](https://github.com/aws/aws-cdk/compare/v2.43.0...v2.43.1) (2022-09-23) + + +### Bug Fixes + +* cannot use values that return an instance of a deprecated class for non TS / JS language ([#22204](https://github.com/aws/aws-cdk/issues/22204)) ([6182d33](https://github.com/aws/aws-cdk/commit/6182d33fdd3b4714e1888305a2e1c689a10e38ea)) + ## [2.43.0](https://github.com/aws/aws-cdk/compare/v2.42.1...v2.43.0) (2022-09-21) diff --git a/version.v2.json b/version.v2.json index 4a68670f41913..7d3475748c006 100644 --- a/version.v2.json +++ b/version.v2.json @@ -1,4 +1,4 @@ { - "version": "2.43.0", - "alphaVersion": "2.43.0-alpha.0" + "version": "2.43.1", + "alphaVersion": "2.43.1-alpha.0" } \ No newline at end of file From cdd294ab414bc63ccda5226333408fd12fce3ccb Mon Sep 17 00:00:00 2001 From: epolon Date: Fri, 23 Sep 2022 12:42:51 +0300 Subject: [PATCH 03/24] fix changelog heading --- CHANGELOG.v2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.v2.md b/CHANGELOG.v2.md index 06468cc8220d0..2c926fc39721d 100644 --- a/CHANGELOG.v2.md +++ b/CHANGELOG.v2.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. -### [2.43.1](https://github.com/aws/aws-cdk/compare/v2.43.0...v2.43.1) (2022-09-23) +## [2.43.1](https://github.com/aws/aws-cdk/compare/v2.43.0...v2.43.1) (2022-09-23) ### Bug Fixes From 419a8f19d138596ac80e9682fed520a50d354b4d Mon Sep 17 00:00:00 2001 From: Eli Polonsky Date: Fri, 23 Sep 2022 13:18:19 +0300 Subject: [PATCH 04/24] Update CHANGELOG.v2.alpha.md --- CHANGELOG.v2.alpha.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.v2.alpha.md b/CHANGELOG.v2.alpha.md index 2194ff30ac6b0..08548b9394b52 100644 --- a/CHANGELOG.v2.alpha.md +++ b/CHANGELOG.v2.alpha.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. -### [2.43.1-alpha.0](https://github.com/aws/aws-cdk/compare/v2.43.0-alpha.0...v2.43.1-alpha.0) (2022-09-23) +## [2.43.1-alpha.0](https://github.com/aws/aws-cdk/compare/v2.43.0-alpha.0...v2.43.1-alpha.0) (2022-09-23) ## [2.43.0-alpha.0](https://github.com/aws/aws-cdk/compare/v2.42.1-alpha.0...v2.43.0-alpha.0) (2022-09-21) From 2de0dc0078870e8a7286ce7568d4fc51baaf3736 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy <36202692+kaizencc@users.noreply.github.com> Date: Mon, 26 Sep 2022 10:42:35 -0400 Subject: [PATCH 05/24] chore: add core team members (#22174) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .github/workflows/github-merit-badger.yml | 2 +- .mergify.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/github-merit-badger.yml b/.github/workflows/github-merit-badger.yml index 30a7a51fc3233..71c2e3c5fa0a0 100644 --- a/.github/workflows/github-merit-badger.yml +++ b/.github/workflows/github-merit-badger.yml @@ -18,4 +18,4 @@ jobs: badges: '[beginning-contributor,repeat-contributor,valued-contributor,admired-contributor,star-contributor,distinguished-contributor]' thresholds: '[0,3,6,13,25,50]' badge-type: 'achievement' - ignore-usernames: '[RomainMuller,rix0rrr,Jerry-AWS,MrArnoldPalmer,iliapolo,otaviomacedo,madeline-k,kaizencc,comcalvi,corymhall,peterwoodworth,ryparker,TheRealAmazonKendra,vinayak-kukreja,Naumel,mrgrain,aws-cdk-automation]' + ignore-usernames: '[RomainMuller,rix0rrr,Jerry-AWS,MrArnoldPalmer,iliapolo,otaviomacedo,madeline-k,kaizencc,comcalvi,corymhall,peterwoodworth,ryparker,TheRealAmazonKendra,vinayak-kukreja,Naumel,mrgrain,pahud,cgarvis,aws-cdk-automation]' diff --git a/.mergify.yml b/.mergify.yml index c75f34e359160..1e5748e68ce04 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -10,7 +10,7 @@ pull_request_rules: label: add: [ contribution/core ] conditions: - - author~=^(RomainMuller|rix0rrr|Jerry-AWS|MrArnoldPalmer|iliapolo|uttarasridhar|otaviomacedo|madeline-k|kaizencc|comcalvi|corymhall|peterwoodworth|ryparker|TheRealAmazonKendra|yuth|vinayak-kukreja|Naumel|mrgrain)$ + - author~=^(RomainMuller|rix0rrr|Jerry-AWS|MrArnoldPalmer|iliapolo|uttarasridhar|otaviomacedo|madeline-k|kaizencc|comcalvi|corymhall|peterwoodworth|ryparker|TheRealAmazonKendra|yuth|vinayak-kukreja|Naumel|mrgrain|pahud|cgarvis)$ - -label~="contribution/core" - name: automatic merge actions: From 05723e74cc0e760f570c36ec02a70e8936287814 Mon Sep 17 00:00:00 2001 From: Juho Saarinen Date: Mon, 26 Sep 2022 18:19:31 +0300 Subject: [PATCH 06/24] feat(pipelines): allow disabling use of change sets (#21619) Possibility to change deployment way for whole pipeline, for single stage or for single stack in stage. Fixes #20827 ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/pipelines/README.md | 90 +- .../lib/blueprint/stage-deployment.ts | 6 + .../lib/codepipeline/codepipeline.ts | 52 +- .../lib/helpers-internal/pipeline-graph.ts | 50 +- .../helpers-internal/pipeline-graph.test.ts | 2 +- .../index.ts | 82 + .../test/codepipeline/codepipeline.test.ts | 61 + .../test/integ.pipeline-without-prepare.ts | 59 + .../PipelineStack.assets.json | 2 +- ...ipelineStackBetaStack1E6541489.assets.json | 2 +- .../assembly-PipelineStack-Beta/cdk.out | 2 +- .../assembly-PipelineStack-Beta/manifest.json | 2 +- .../cdk.out | 2 +- .../integ.json | 2 +- .../manifest.json | 2 +- .../tree.json | 32 +- .../PreparelessPipelineStack.assets.json | 32 + .../PreparelessPipelineStack.template.json | 950 +++++++++++ ...efaultTestDeployAssert7B7DD2C6.assets.json | 19 + ...aultTestDeployAssert7B7DD2C6.template.json | 36 + ...elineStackMyStageStack3DC192E7.assets.json | 19 + ...ineStackMyStageStack3DC192E7.template.json | 36 + ...MyStageStackMyAppStack51FBCD39.assets.json | 19 + ...StageStackMyAppStack51FBCD39.template.json | 43 + .../cdk.out | 1 + .../manifest.json | 59 + .../cdk.out | 1 + .../manifest.json | 60 + .../__entrypoint__.js | 118 ++ .../index.d.ts | 1 + .../index.js | 78 + .../index.ts | 82 + .../cdk.out | 1 + .../integ.json | 12 + .../manifest.json | 214 +++ .../tree.json | 1402 +++++++++++++++++ .../pipelines/test/testhelpers/test-app.ts | 8 +- 37 files changed, 3571 insertions(+), 68 deletions(-) create mode 100644 packages/@aws-cdk/pipelines/test/cdk-integ.out.pipeline-with-asset-variables/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/index.ts create mode 100644 packages/@aws-cdk/pipelines/test/integ.pipeline-without-prepare.ts create mode 100644 packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/PreparelessPipelineStack.assets.json create mode 100644 packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/PreparelessPipelineStack.template.json create mode 100644 packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6.assets.json create mode 100644 packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6.template.json create mode 100644 packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/PreparelessPipelineStackMyStageStack3DC192E7.assets.json create mode 100644 packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/PreparelessPipelineStackMyStageStack3DC192E7.template.json create mode 100644 packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/assembly-PreparelessPipelineStack-MyStage-Stack-MyApp/PreparelessPipelineStackMyStageStackMyAppStack51FBCD39.assets.json create mode 100644 packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/assembly-PreparelessPipelineStack-MyStage-Stack-MyApp/PreparelessPipelineStackMyStageStackMyAppStack51FBCD39.template.json create mode 100644 packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/assembly-PreparelessPipelineStack-MyStage-Stack-MyApp/cdk.out create mode 100644 packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/assembly-PreparelessPipelineStack-MyStage-Stack-MyApp/manifest.json create mode 100644 packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/cdk.out create mode 100644 packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/manifest.json create mode 100644 packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/__entrypoint__.js create mode 100644 packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/index.d.ts create mode 100644 packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/index.js create mode 100644 packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/index.ts create mode 100644 packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/cdk.out create mode 100644 packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/integ.json create mode 100644 packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/manifest.json create mode 100644 packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/tree.json diff --git a/packages/@aws-cdk/pipelines/README.md b/packages/@aws-cdk/pipelines/README.md index bf9def55c5ced..57e0b0b3ed15f 100644 --- a/packages/@aws-cdk/pipelines/README.md +++ b/packages/@aws-cdk/pipelines/README.md @@ -544,6 +544,32 @@ const pipeline = new pipelines.CodePipeline(this, 'Pipeline', { }); ``` +#### Deploying without change sets + +Deployment is done by default with `CodePipeline` engine using change sets, +i.e. to first create a change set and then execute it. This allows you to inject +steps that inspect the change set and approve or reject it, but failed deployments +are not retryable and creation of the change set costs time. + +The creation of change sets can be switched off by setting `useChangeSets: false`: + +```ts +declare const synth: pipelines.ShellStep; + +class PipelineStack extends Stack { + constructor(scope: Construct, id: string, props?: StackProps) { + super(scope, id, props); + + const pipeline = new pipelines.CodePipeline(this, 'Pipeline', { + synth, + + // Disable change set creation and make deployments in pipeline as single step + useChangeSets: false, + }); + } +} +``` + ### Validation Every `addStage()` and `addWave()` command takes additional options. As part of these options, @@ -867,11 +893,11 @@ because those values are passed in directly to the underlying `codepipeline.Pipe Docker can be used in 3 different places in the pipeline: -* If you are using Docker image assets in your application stages: Docker will +- If you are using Docker image assets in your application stages: Docker will run in the asset publishing projects. -* If you are using Docker image assets in your stack (for example as +- If you are using Docker image assets in your stack (for example as images for your CodeBuild projects): Docker will run in the self-mutate project. -* If you are using Docker to bundle file assets anywhere in your project (for +- If you are using Docker to bundle file assets anywhere in your project (for example, if you are using such construct libraries as `@aws-cdk/aws-lambda-nodejs`): Docker will run in the *synth* project. @@ -1069,26 +1095,26 @@ $ npx cdk bootstrap \ These command lines explained: -* `npx`: means to use the CDK CLI from the current NPM install. If you are using +- `npx`: means to use the CDK CLI from the current NPM install. If you are using a global install of the CDK CLI, leave this out. -* `--profile`: should indicate a profile with administrator privileges that has +- `--profile`: should indicate a profile with administrator privileges that has permissions to provision a pipeline in the indicated account. You can leave this flag out if either the AWS default credentials or the `AWS_*` environment variables confer these permissions. -* `--cloudformation-execution-policies`: ARN of the managed policy that future CDK +- `--cloudformation-execution-policies`: ARN of the managed policy that future CDK deployments should execute with. By default this is `AdministratorAccess`, but if you also specify the `--trust` flag to give another Account permissions to deploy into the current account, you must specify a value here. -* `--trust`: indicates which other account(s) should have permissions to deploy +- `--trust`: indicates which other account(s) should have permissions to deploy CDK applications into this account. In this case we indicate the Pipeline's account, but you could also use this for developer accounts (don't do that for production application accounts though!). -* `--trust-for-lookup`: gives a more limited set of permissions to the +- `--trust-for-lookup`: gives a more limited set of permissions to the trusted account, only allowing it to look up values such as availability zones, EC2 images and VPCs. `--trust-for-lookup` does not give permissions to modify anything in the account. Note that `--trust` implies `--trust-for-lookup`, so you don't need to specify the same acocunt twice. -* `aws://222222222222/us-east-2`: the account and region we're bootstrapping. +- `aws://222222222222/us-east-2`: the account and region we're bootstrapping. > Be aware that anyone who has access to the trusted Accounts **effectively has all > permissions conferred by the configured CloudFormation execution policies**, @@ -1126,10 +1152,10 @@ The "new" bootstrap stack (obtained by running `cdk bootstrap` with `CDK_NEW_BOOTSTRAP=1`) is slightly more elaborate than the "old" stack. It contains: -* An S3 bucket and ECR repository with predictable names, so that we can reference +- An S3 bucket and ECR repository with predictable names, so that we can reference assets in these storage locations *without* the use of CloudFormation template parameters. -* A set of roles with permissions to access these asset locations and to execute +- A set of roles with permissions to access these asset locations and to execute CloudFormation, assumable from whatever accounts you specify under `--trust`. It is possible and safe to migrate from the old bootstrap stack to the new @@ -1209,15 +1235,15 @@ very nature the library cannot take care of everything. We therefore expect you to mind the following: -* Maintain dependency hygiene and vet 3rd-party software you use. Any software you +- Maintain dependency hygiene and vet 3rd-party software you use. Any software you run on your build machine has the ability to change the infrastructure that gets deployed. Be careful with the software you depend on. -* Use dependency locking to prevent accidental upgrades! The default `CdkSynths` that +- Use dependency locking to prevent accidental upgrades! The default `CdkSynths` that come with CDK Pipelines will expect `package-lock.json` and `yarn.lock` to ensure your dependencies are the ones you expect. -* Credentials to production environments should be short-lived. After +- Credentials to production environments should be short-lived. After bootstrapping and the initial pipeline provisioning, there is no more need for developers to have access to any of the account credentials; all further changes can be deployed through git. Avoid the chances of credentials leaking @@ -1292,7 +1318,7 @@ use CDK Pipelines to build pipelines backed by other deployment engines. Here is a list of CDK Libraries that integrate CDK Pipelines with alternative deployment engines: -* GitHub Workflows: [`cdk-pipelines-github`](https://github.com/cdklabs/cdk-pipelines-github) +- GitHub Workflows: [`cdk-pipelines-github`](https://github.com/cdklabs/cdk-pipelines-github) ## Troubleshooting @@ -1329,11 +1355,11 @@ If you see this error during the **Synth** step, it means that CodeBuild is expecting to find a `cdk.out` directory in the root of your CodeBuild project, but the directory wasn't there. There are two common causes for this: -* `cdk synth` is not being executed: `cdk synth` used to be run +- `cdk synth` is not being executed: `cdk synth` used to be run implicitly for you, but you now have to explicitly include the command. For NPM-based projects, add `npx cdk synth` to the end of the `commands` property, for other languages add `npm install -g aws-cdk` and `cdk synth`. -* Your CDK project lives in a subdirectory: you added a `cd ` command +- Your CDK project lives in a subdirectory: you added a `cd ` command to the list of commands; don't forget to tell the `ScriptStep` about the different location of `cdk.out`, by passing `primaryOutputDirectory: '/cdk.out'`. @@ -1426,9 +1452,9 @@ all, and commit a file called `cdk.context.json` with the right lookup values in If you do want to do lookups in the pipeline, the cause is one of the following: -* The target environment has not been bootstrapped; OR -* The target environment has been bootstrapped without the right `--trust` relationship; OR -* The CodeBuild execution role does not have permissions to call `sts:AssumeRole`. +- The target environment has not been bootstrapped; OR +- The target environment has been bootstrapped without the right `--trust` relationship; OR +- The CodeBuild execution role does not have permissions to call `sts:AssumeRole`. See the section called **Context Lookups** for more information on using this feature. @@ -1452,8 +1478,8 @@ following: An "S3 Access Denied" error can have two causes: -* Asset hashes have changed, but self-mutation has been disabled in the pipeline. -* You have deleted and recreated the bootstrap stack, or changed its qualifier. +- Asset hashes have changed, but self-mutation has been disabled in the pipeline. +- You have deleted and recreated the bootstrap stack, or changed its qualifier. #### Self-mutation step has been removed @@ -1495,7 +1521,7 @@ The most automated way to solve the issue is to introduce a secondary bootstrap that the pipeline stack looks for, a change will be detected and the impacted policies and resources will be updated. A hypothetical recovery workflow would look something like this: -* First, for all impacted environments, create a secondary bootstrap stack: +- First, for all impacted environments, create a secondary bootstrap stack: ```sh $ env CDK_NEW_BOOTSTRAP=1 npx cdk bootstrap \ @@ -1504,7 +1530,7 @@ $ env CDK_NEW_BOOTSTRAP=1 npx cdk bootstrap \ aws://111111111111/us-east-1 ``` -* Update all impacted stacks in the pipeline to use this new qualifier. +- Update all impacted stacks in the pipeline to use this new qualifier. See https://docs.aws.amazon.com/cdk/latest/guide/bootstrapping.html for more info. ```ts @@ -1516,11 +1542,11 @@ new Stack(this, 'MyStack', { }); ``` -* Deploy the updated stacks. This will update the stacks to use the roles created in the new bootstrap stack. -* (Optional) Restore back to the original state: - * Revert the change made in step #2 above - * Re-deploy the pipeline to use the original qualifier. - * Delete the temporary bootstrap stack(s) +- Deploy the updated stacks. This will update the stacks to use the roles created in the new bootstrap stack. +- (Optional) Restore back to the original state: + - Revert the change made in step #2 above + - Re-deploy the pipeline to use the original qualifier. + - Delete the temporary bootstrap stack(s) ##### Manual Alternative @@ -1655,14 +1681,14 @@ const pipeline = new pipelines.CodePipeline(this, 'Pipeline', { There are some usability issues that are caused by underlying technology, and cannot be remedied by CDK at this point. They are reproduced here for completeness. -* **Console links to other accounts will not work**: the AWS CodePipeline +- **Console links to other accounts will not work**: the AWS CodePipeline console will assume all links are relative to the current account. You will not be able to use the pipeline console to click through to a CloudFormation stack in a different account. -* **If a change set failed to apply the pipeline must restarted**: if a change +- **If a change set failed to apply the pipeline must restarted**: if a change set failed to apply, it cannot be retried. The pipeline must be restarted from the top by clicking **Release Change**. -* **A stack that failed to create must be deleted manually**: if a stack +- **A stack that failed to create must be deleted manually**: if a stack failed to create on the first attempt, you must delete it using the CloudFormation console before starting the pipeline again by clicking **Release Change**. diff --git a/packages/@aws-cdk/pipelines/lib/blueprint/stage-deployment.ts b/packages/@aws-cdk/pipelines/lib/blueprint/stage-deployment.ts index 651ccb3b46d53..0761f03b6a01b 100644 --- a/packages/@aws-cdk/pipelines/lib/blueprint/stage-deployment.ts +++ b/packages/@aws-cdk/pipelines/lib/blueprint/stage-deployment.ts @@ -117,6 +117,12 @@ export class StageDeployment { */ public readonly stackSteps: StackSteps[]; + /** + * Determine if all stacks in stage should be deployed with prepare + * step or not. + */ + public readonly prepareStep?: boolean; + private constructor( /** The stacks deployed in this stage */ public readonly stacks: StackDeployment[], props: StageDeploymentProps = {}) { diff --git a/packages/@aws-cdk/pipelines/lib/codepipeline/codepipeline.ts b/packages/@aws-cdk/pipelines/lib/codepipeline/codepipeline.ts index 8d06e9bb1fa80..8bc6165ea1077 100644 --- a/packages/@aws-cdk/pipelines/lib/codepipeline/codepipeline.ts +++ b/packages/@aws-cdk/pipelines/lib/codepipeline/codepipeline.ts @@ -214,6 +214,16 @@ export interface CodePipelineProps { * @default - A new role is created */ readonly role?: iam.IRole; + + /** + * Deploy every stack by creating a change set and executing it + * + * When enabled, creates a "Prepare" and "Execute" action for each stack. Disable + * to deploy the stack in one pipeline action. + * + * @default true + */ + readonly useChangeSets?: boolean; } /** @@ -299,6 +309,7 @@ export class CodePipeline extends PipelineBase { private artifacts = new ArtifactMap(); private _synthProject?: cb.IProject; private readonly selfMutation: boolean; + private readonly useChangeSets: boolean; private _myCxAsmRoot?: string; private readonly dockerCredentials: DockerCredential[]; private readonly cachedFnSub = new CachedFnSub(); @@ -325,6 +336,7 @@ export class CodePipeline extends PipelineBase { this.dockerCredentials = props.dockerCredentials ?? []; this.singlePublisherPerAssetType = !(props.publishAssetsInParallel ?? true); this.cliVersion = props.cliVersion ?? preferredCliVersion(); + this.useChangeSets = props.useChangeSets ?? true; } /** @@ -389,6 +401,7 @@ export class CodePipeline extends PipelineBase { const graphFromBp = new PipelineGraph(this, { selfMutation: this.selfMutation, singlePublisherPerAssetType: this.singlePublisherPerAssetType, + prepareStep: this.useChangeSets, }); this._cloudAssemblyFileSet = graphFromBp.cloudAssemblyFileSet; @@ -519,10 +532,15 @@ export class CodePipeline extends PipelineBase { return this.createChangeSetAction(node.data.stack); case 'execute': - return this.executeChangeSetAction(node.data.stack, node.data.captureOutputs); + return node.data.withoutChangeSet + ? this.executeDeploymentAction(node.data.stack, node.data.captureOutputs) + : this.executeChangeSetAction(node.data.stack, node.data.captureOutputs); case 'step': return this.actionFromStep(node, node.data.step); + + default: + throw new Error(`CodePipeline does not support graph nodes of type '${node.data?.type}'. You are probably using a feature this CDK Pipelines implementation does not support.`); } } @@ -630,6 +648,38 @@ export class CodePipeline extends PipelineBase { }; } + private executeDeploymentAction(stack: StackDeployment, captureOutputs: boolean): ICodePipelineActionFactory { + const templateArtifact = this.artifacts.toCodePipeline(this._cloudAssemblyFileSet!); + const templateConfigurationPath = this.writeTemplateConfiguration(stack); + + const region = stack.region !== Stack.of(this).region ? stack.region : undefined; + const account = stack.account !== Stack.of(this).account ? stack.account : undefined; + + const relativeTemplatePath = path.relative(this.myCxAsmRoot, stack.absoluteTemplatePath); + + return { + produceAction: (stage, options) => { + stage.addAction(new cpa.CloudFormationCreateUpdateStackAction({ + actionName: options.actionName, + runOrder: options.runOrder, + stackName: stack.stackName, + templatePath: templateArtifact.atPath(toPosixPath(relativeTemplatePath)), + adminPermissions: true, + role: this.roleFromPlaceholderArn(this.pipeline, region, account, stack.assumeRoleArn), + deploymentRole: this.roleFromPlaceholderArn(this.pipeline, region, account, stack.executionRoleArn), + region: region, + templateConfiguration: templateConfigurationPath + ? templateArtifact.atPath(toPosixPath(templateConfigurationPath)) + : undefined, + cfnCapabilities: [CfnCapabilities.NAMED_IAM, CfnCapabilities.AUTO_EXPAND], + variablesNamespace: captureOutputs ? stackVariableNamespace(stack) : undefined, + })); + + return { runOrdersConsumed: 1 }; + }, + }; + } + private selfMutateAction(): ICodePipelineActionFactory { const installSuffix = this.cliVersion ? `@${this.cliVersion}` : ''; diff --git a/packages/@aws-cdk/pipelines/lib/helpers-internal/pipeline-graph.ts b/packages/@aws-cdk/pipelines/lib/helpers-internal/pipeline-graph.ts index e26058b724b62..22692f509581a 100644 --- a/packages/@aws-cdk/pipelines/lib/helpers-internal/pipeline-graph.ts +++ b/packages/@aws-cdk/pipelines/lib/helpers-internal/pipeline-graph.ts @@ -88,7 +88,7 @@ export class PipelineGraph { if (props.selfMutation) { const stage: AGraph = Graph.of('UpdatePipeline', { type: 'group' }); this.graph.add(stage); - this.selfMutateNode = GraphNode.of('SelfMutate', { type: 'self-update' }); + this.selfMutateNode = aGraphNode('SelfMutate', { type: 'self-update' }); stage.add(this.selfMutateNode); this.selfMutateNode.dependOn(this.synthNode); @@ -134,11 +134,12 @@ export class PipelineGraph { for (const stack of stage.stacks) { const stackGraph: AGraph = Graph.of(this.simpleStackName(stack.stackName, stage.stageName), { type: 'stack-group', stack }); - const prepareNode: AGraphNode | undefined = this.prepareStep ? GraphNode.of('Prepare', { type: 'prepare', stack }) : undefined; - const deployNode: AGraphNode = GraphNode.of('Deploy', { + const prepareNode: AGraphNode | undefined = this.prepareStep ? aGraphNode('Prepare', { type: 'prepare', stack }) : undefined; + const deployNode: AGraphNode = aGraphNode('Deploy', { type: 'execute', stack, captureOutputs: this.queries.stackOutputsReferenced(stack).length > 0, + withoutChangeSet: prepareNode === undefined, }); retGraph.add(stackGraph); @@ -157,9 +158,9 @@ export class PipelineGraph { // add changeset steps at the stack level if (stack.changeSet.length > 0) { if (prepareNode) { - this.addChangeSet(stack.changeSet, prepareNode, deployNode, stackGraph); + this.addChangeSetNode(stack.changeSet, prepareNode, deployNode, stackGraph); } else { - throw new Error('Your pipeline engine does not support changeSet steps'); + throw new Error(`Cannot use \'changeSet\' steps for stack \'${stack.stackName}\': the pipeline does not support them or they have been disabled`); } } @@ -217,7 +218,7 @@ export class PipelineGraph { return retGraph; } - private addChangeSet(changeSet: Step[], prepareNode: AGraphNode, deployNode: AGraphNode, graph: AGraph) { + private addChangeSetNode(changeSet: Step[], prepareNode: AGraphNode, deployNode: AGraphNode, graph: AGraph) { for (const c of changeSet) { const changeSetNode = this.addAndRecurse(c, graph); changeSetNode?.dependOn(prepareNode); @@ -255,7 +256,7 @@ export class PipelineGraph { const previous = this.added.get(step); if (previous) { return previous; } - const node: AGraphNode = GraphNode.of(step.id, { type: 'step', step }); + const node: AGraphNode = aGraphNode(step.id, { type: 'step', step }); // If the step is a source step, change the parent to a special "Source" stage // (CodePipeline wants it that way) @@ -299,7 +300,7 @@ export class PipelineGraph { ? (this.singlePublisher ? 'FileAsset' : `FileAsset${++this._fileAssetCtr}`) : (this.singlePublisher ? 'DockerAsset' : `DockerAsset${++this._dockerAssetCtr}`); - assetNode = GraphNode.of(id, { type: 'publish-assets', assets: [] }); + assetNode = aGraphNode(id, { type: 'publish-assets', assets: [] }); assetsGraph.add(assetNode); assetNode.dependOn(this.lastPreparationNode); @@ -311,6 +312,7 @@ export class PipelineGraph { if (data?.type !== 'publish-assets') { throw new Error(`${assetNode} has the wrong data.type: ${data?.type}`); } + if (!data.assets.some(a => a.assetSelector === stackAsset.assetSelector)) { data.assets.push(stackAsset); } @@ -327,20 +329,48 @@ export class PipelineGraph { } type GraphAnnotation = - { readonly type: 'group' } + | { readonly type: 'group' } | { readonly type: 'stack-group'; readonly stack: StackDeployment } | { readonly type: 'publish-assets'; readonly assets: StackAsset[] } | { readonly type: 'step'; readonly step: Step; isBuildStep?: boolean } | { readonly type: 'self-update' } | { readonly type: 'prepare'; readonly stack: StackDeployment } - | { readonly type: 'execute'; readonly stack: StackDeployment; readonly captureOutputs: boolean } + | ExecuteAnnotation + // Explicitly disable exhaustiveness checking on GraphAnnotation. This forces all consumers to adding + // a 'default' clause which allows us to extend this list in the future. + // The code below looks weird, 'type' must be a non-enumerable type that is not assignable to 'string'. + | { readonly type: { error: 'you must add a default case to your switch' } } ; +interface ExecuteAnnotation { + readonly type: 'execute'; + /** + * The stack to deploy + */ + readonly stack: StackDeployment; + + /** + * Whether or not outputs should be captured + */ + readonly captureOutputs: boolean; + + /** + * If this is executing a change set, or should do a direct deployment + * + * @default false + */ + readonly withoutChangeSet?: boolean; +} + // Type aliases for the graph nodes tagged with our specific annotation type // (to save on generics in the code above). export type AGraphNode = GraphNode; export type AGraph = Graph; +function aGraphNode(id: string, x: GraphAnnotation): AGraphNode { + return GraphNode.of(id, x); +} + function stripPrefix(s: string, prefix: string) { return s.startsWith(prefix) ? s.slice(prefix.length) : s; } \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/blueprint/helpers-internal/pipeline-graph.test.ts b/packages/@aws-cdk/pipelines/test/blueprint/helpers-internal/pipeline-graph.test.ts index 69391bf8b0594..747dcfcb91d77 100644 --- a/packages/@aws-cdk/pipelines/test/blueprint/helpers-internal/pipeline-graph.test.ts +++ b/packages/@aws-cdk/pipelines/test/blueprint/helpers-internal/pipeline-graph.test.ts @@ -288,7 +288,7 @@ describe('options for other engines', () => { // THEN expect(() => new PipelineGraph(blueprint, { prepareStep: false, - })).toThrow('Your pipeline engine does not support changeSet steps'); + })).toThrow(/Cannot use 'changeSet' steps/); }); }); diff --git a/packages/@aws-cdk/pipelines/test/cdk-integ.out.pipeline-with-asset-variables/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/index.ts b/packages/@aws-cdk/pipelines/test/cdk-integ.out.pipeline-with-asset-variables/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/index.ts new file mode 100644 index 0000000000000..2459d44ab1d18 --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/cdk-integ.out.pipeline-with-asset-variables/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/index.ts @@ -0,0 +1,82 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +import { S3 } from 'aws-sdk'; + +const AUTO_DELETE_OBJECTS_TAG = 'aws-cdk:auto-delete-objects'; + +const s3 = new S3(); + +export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { + switch (event.RequestType) { + case 'Create': + return; + case 'Update': + return onUpdate(event); + case 'Delete': + return onDelete(event.ResourceProperties?.BucketName); + } +} + +async function onUpdate(event: AWSLambda.CloudFormationCustomResourceEvent) { + const updateEvent = event as AWSLambda.CloudFormationCustomResourceUpdateEvent; + const oldBucketName = updateEvent.OldResourceProperties?.BucketName; + const newBucketName = updateEvent.ResourceProperties?.BucketName; + const bucketNameHasChanged = newBucketName != null && oldBucketName != null && newBucketName !== oldBucketName; + + /* If the name of the bucket has changed, CloudFormation will try to delete the bucket + and create a new one with the new name. So we have to delete the contents of the + bucket so that this operation does not fail. */ + if (bucketNameHasChanged) { + return onDelete(oldBucketName); + } +} + +/** + * Recursively delete all items in the bucket + * + * @param bucketName the bucket name + */ +async function emptyBucket(bucketName: string) { + const listedObjects = await s3.listObjectVersions({ Bucket: bucketName }).promise(); + const contents = [...listedObjects.Versions ?? [], ...listedObjects.DeleteMarkers ?? []]; + if (contents.length === 0) { + return; + } + + const records = contents.map((record: any) => ({ Key: record.Key, VersionId: record.VersionId })); + await s3.deleteObjects({ Bucket: bucketName, Delete: { Objects: records } }).promise(); + + if (listedObjects?.IsTruncated) { + await emptyBucket(bucketName); + } +} + +async function onDelete(bucketName?: string) { + if (!bucketName) { + throw new Error('No BucketName was provided.'); + } + if (!await isBucketTaggedForDeletion(bucketName)) { + process.stdout.write(`Bucket does not have '${AUTO_DELETE_OBJECTS_TAG}' tag, skipping cleaning.\n`); + return; + } + try { + await emptyBucket(bucketName); + } catch (e) { + if (e.code !== 'NoSuchBucket') { + throw e; + } + // Bucket doesn't exist. Ignoring + } +} + +/** + * The bucket will only be tagged for deletion if it's being deleted in the same + * deployment as this Custom Resource. + * + * If the Custom Resource is every deleted before the bucket, it must be because + * `autoDeleteObjects` has been switched to false, in which case the tag would have + * been removed before we get to this Delete event. + */ +async function isBucketTaggedForDeletion(bucketName: string) { + const response = await s3.getBucketTagging({ Bucket: bucketName }).promise(); + return response.TagSet.some(tag => tag.Key === AUTO_DELETE_OBJECTS_TAG && tag.Value === 'true'); +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/codepipeline/codepipeline.test.ts b/packages/@aws-cdk/pipelines/test/codepipeline/codepipeline.test.ts index 5cc67f6debb7c..b0ecae2286d1a 100644 --- a/packages/@aws-cdk/pipelines/test/codepipeline/codepipeline.test.ts +++ b/packages/@aws-cdk/pipelines/test/codepipeline/codepipeline.test.ts @@ -237,6 +237,67 @@ test('CodePipeline supports use of existing role', () => { }); }); +describe('deployment of stack', () => { + test('is done with Prepare and Deploy step by default', () => { + const pipelineStack = new cdk.Stack(app, 'PipelineStack', { env: PIPELINE_ENV }); + const pipeline = new ModernTestGitHubNpmPipeline(pipelineStack, 'Cdk', { + crossAccountKeys: true, + }); + pipeline.addStage(new FileAssetApp(pipelineStack, 'App', {})); + + // THEN + const template = Template.fromStack(pipelineStack); + + // There should be Prepare step in piepline + template.hasResourceProperties('AWS::CodePipeline::Pipeline', { + Stages: Match.arrayWith([{ + Actions: Match.arrayWith([ + Match.objectLike({ + Configuration: Match.objectLike({ + ActionMode: 'CHANGE_SET_REPLACE', + }), + Name: 'Prepare', + }), + Match.objectLike({ + Configuration: Match.objectLike({ + ActionMode: 'CHANGE_SET_EXECUTE', + }), + Name: 'Deploy', + }), + ]), + Name: 'App', + }]), + }); + }); + + test('can be done with single step', () => { + const pipelineStack = new cdk.Stack(app, 'PipelineStack', { env: PIPELINE_ENV }); + const pipeline = new ModernTestGitHubNpmPipeline(pipelineStack, 'Cdk', { + crossAccountKeys: true, + useChangeSets: false, + }); + pipeline.addStage(new FileAssetApp(pipelineStack, 'App', {})); + + // THEN + const template = Template.fromStack(pipelineStack); + + // There should be Prepare step in piepline + template.hasResourceProperties('AWS::CodePipeline::Pipeline', { + Stages: Match.arrayWith([{ + Actions: Match.arrayWith([ + Match.objectLike({ + Configuration: Match.objectLike({ + ActionMode: 'CREATE_UPDATE', + }), + Name: 'Deploy', + }), + ]), + Name: 'App', + }]), + }); + }); +}); + interface ReuseCodePipelineStackProps extends cdk.StackProps { reuseCrossRegionSupportStacks?: boolean; } diff --git a/packages/@aws-cdk/pipelines/test/integ.pipeline-without-prepare.ts b/packages/@aws-cdk/pipelines/test/integ.pipeline-without-prepare.ts new file mode 100644 index 0000000000000..a48438ad2cbec --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/integ.pipeline-without-prepare.ts @@ -0,0 +1,59 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +/// !cdk-integ VarablePipelineStack pragma:set-context:@aws-cdk/core:newStyleStackSynthesis=true +import * as s3 from '@aws-cdk/aws-s3'; +import { App, Stack, StackProps, RemovalPolicy, Stage, StageProps, DefaultStackSynthesizer } from '@aws-cdk/core'; +import * as integ from '@aws-cdk/integ-tests'; +import { Construct } from 'constructs'; +import * as pipelines from '../lib'; +import { PlainStackApp } from './testhelpers'; + +class MyStage extends Stage { + constructor(scope: Construct, id: string, props?: StageProps) { + super(scope, id, props); + + const stack = new Stack(this, 'Stack', { + ...props, + synthesizer: new DefaultStackSynthesizer(), + }); + + new PlainStackApp(stack, 'MyApp'); + } +} + +class PipelineStack extends Stack { + constructor(scope: Construct, id: string, props?: StackProps) { + super(scope, id, props); + + const sourceBucket = new s3.Bucket(this, 'SourceBucket', { + removalPolicy: RemovalPolicy.DESTROY, + autoDeleteObjects: true, + }); + const pipeline = new pipelines.CodePipeline(this, 'Pipeline', { + synth: new pipelines.ShellStep('Synth', { + input: pipelines.CodePipelineSource.s3(sourceBucket, 'key'), + // input: pipelines.CodePipelineSource.gitHub('cdklabs/construct-hub-probe', 'main', { + // trigger: GitHubTrigger.POLL, + // }), + commands: ['mkdir cdk.out', 'touch cdk.out/dummy'], + }), + selfMutation: false, + useChangeSets: false, + }); + + pipeline.addStage(new MyStage(this, 'MyStage', {})); + } +} + +const app = new App({ + context: { + '@aws-cdk/core:newStyleStackSynthesis': '1', + }, +}); + +const stack = new PipelineStack(app, 'PreparelessPipelineStack'); + +new integ.IntegTest(app, 'PreparelessPipelineTest', { + testCases: [stack], +}); + +app.synth(); diff --git a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/PipelineStack.assets.json b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/PipelineStack.assets.json index 0799a1aa58659..ad85c4f0e29b6 100644 --- a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/PipelineStack.assets.json +++ b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/PipelineStack.assets.json @@ -1,5 +1,5 @@ { - "version": "20.0.0", + "version": "21.0.0", "files": { "339869b7efdb6a771fce40473ae99ce3e3174959597875b90007c83a87221f57": { "source": { diff --git a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/assembly-PipelineStack-Beta/PipelineStackBetaStack1E6541489.assets.json b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/assembly-PipelineStack-Beta/PipelineStackBetaStack1E6541489.assets.json index 09d77bb1e7823..4b182342ba90f 100644 --- a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/assembly-PipelineStack-Beta/PipelineStackBetaStack1E6541489.assets.json +++ b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/assembly-PipelineStack-Beta/PipelineStackBetaStack1E6541489.assets.json @@ -1,5 +1,5 @@ { - "version": "20.0.0", + "version": "21.0.0", "files": { "8289faf53c7da377bb2b90615999171adef5e1d8f6b88810e5fef75e6ca09ba5": { "source": { diff --git a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/assembly-PipelineStack-Beta/cdk.out b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/assembly-PipelineStack-Beta/cdk.out index 588d7b269d34f..8ecc185e9dbee 100644 --- a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/assembly-PipelineStack-Beta/cdk.out +++ b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/assembly-PipelineStack-Beta/cdk.out @@ -1 +1 @@ -{"version":"20.0.0"} \ No newline at end of file +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/assembly-PipelineStack-Beta/manifest.json b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/assembly-PipelineStack-Beta/manifest.json index 14ab527bbc9bc..6dad8563dba5a 100644 --- a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/assembly-PipelineStack-Beta/manifest.json +++ b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/assembly-PipelineStack-Beta/manifest.json @@ -1,5 +1,5 @@ { - "version": "20.0.0", + "version": "21.0.0", "artifacts": { "PipelineStackBetaStack1E6541489.assets": { "type": "cdk:asset-manifest", diff --git a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/cdk.out b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/cdk.out index 588d7b269d34f..8ecc185e9dbee 100644 --- a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/cdk.out +++ b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/cdk.out @@ -1 +1 @@ -{"version":"20.0.0"} \ No newline at end of file +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/integ.json b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/integ.json index 66f5b01318d4f..00c9e8da3dbec 100644 --- a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/integ.json +++ b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "20.0.0", + "version": "21.0.0", "testCases": { "integ.newpipeline-with-vpc": { "stacks": [ diff --git a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/manifest.json b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/manifest.json index 81748cc96388c..f265f1c8dae5d 100644 --- a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "20.0.0", + "version": "21.0.0", "artifacts": { "assembly-PipelineStack-Beta": { "type": "cdk:cloud-assembly", diff --git a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/tree.json b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/tree.json index 8ce20f7a8d7dc..6683466c9909d 100644 --- a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/tree.json +++ b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/tree.json @@ -9,7 +9,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } }, "PipelineStack": { @@ -1298,13 +1298,13 @@ }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } } }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } }, "Build": { @@ -1742,13 +1742,13 @@ }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } } }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } }, "UpdatePipeline": { @@ -1760,13 +1760,13 @@ "path": "PipelineStack/Pipeline/Pipeline/UpdatePipeline/SelfMutate", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } } }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } }, "Assets": { @@ -1778,7 +1778,7 @@ "path": "PipelineStack/Pipeline/Pipeline/Assets/FileAsset1", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } }, "FileAsset2": { @@ -1786,13 +1786,13 @@ "path": "PipelineStack/Pipeline/Pipeline/Assets/FileAsset2", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } } }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } }, "Beta": { @@ -1804,7 +1804,7 @@ "path": "PipelineStack/Pipeline/Pipeline/Beta/Prepare", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } }, "Deploy": { @@ -1812,13 +1812,13 @@ "path": "PipelineStack/Pipeline/Pipeline/Beta/Deploy", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } } }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } }, "MutableRolearn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}": { @@ -1838,7 +1838,7 @@ "path": "PipelineStack/Pipeline/Pipeline/arn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}/8389e75f-0810-4838-bf64-d6f85a95cf83", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } } }, @@ -2449,7 +2449,7 @@ }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } }, "Assets": { @@ -2978,7 +2978,7 @@ }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } } }, diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/PreparelessPipelineStack.assets.json b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/PreparelessPipelineStack.assets.json new file mode 100644 index 0000000000000..0e07fad5fe474 --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/PreparelessPipelineStack.assets.json @@ -0,0 +1,32 @@ +{ + "version": "21.0.0", + "files": { + "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26": { + "source": { + "path": "asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26", + "packaging": "zip" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26.zip", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + }, + "d48998cffedf7ada707ce9f74130f229eb47e4c549355b1031589d0c4d78d9ad": { + "source": { + "path": "PreparelessPipelineStack.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "d48998cffedf7ada707ce9f74130f229eb47e4c549355b1031589d0c4d78d9ad.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/PreparelessPipelineStack.template.json b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/PreparelessPipelineStack.template.json new file mode 100644 index 0000000000000..66c500812ee7a --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/PreparelessPipelineStack.template.json @@ -0,0 +1,950 @@ +{ + "Resources": { + "SourceBucketDDD2130A": { + "Type": "AWS::S3::Bucket", + "Properties": { + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ] + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "SourceBucketPolicy703DFBF9": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "SourceBucketDDD2130A" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:DeleteObject*", + "s3:GetBucket*", + "s3:List*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "SourceBucketDDD2130A", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "SourceBucketDDD2130A", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "SourceBucketAutoDeleteObjectsCustomResourceC68FC040": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "SourceBucketDDD2130A" + } + }, + "DependsOn": [ + "SourceBucketPolicy703DFBF9" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ] + } + }, + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26.zip" + }, + "Timeout": 900, + "MemorySize": 128, + "Handler": "__entrypoint__.handler", + "Role": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + }, + "Runtime": "nodejs14.x", + "Description": { + "Fn::Join": [ + "", + [ + "Lambda function for auto-deleting objects in ", + { + "Ref": "SourceBucketDDD2130A" + }, + " S3 bucket." + ] + ] + } + }, + "DependsOn": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092" + ] + }, + "PipelineArtifactsBucketAEA9A052": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "aws:kms" + } + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "PipelineArtifactsBucketPolicyF53CCC52": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "PipelineArtifactsBucketAEA9A052" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":role/cdk-hnb659fds-deploy-role-", + { + "Ref": "AWS::AccountId" + }, + "-", + { + "Ref": "AWS::Region" + } + ] + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelineRoleB27FAA37": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codepipeline.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelineRoleDefaultPolicy7BDC1ABB": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:Abort*", + "s3:DeleteObject*", + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineCodeBuildActionRole226DB0CB", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "PipelineSourceS3CodePipelineActionRole83895A58", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":role/cdk-hnb659fds-deploy-role-", + { + "Ref": "AWS::AccountId" + }, + "-", + { + "Ref": "AWS::Region" + } + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelineRoleDefaultPolicy7BDC1ABB", + "Roles": [ + { + "Ref": "PipelineRoleB27FAA37" + } + ] + } + }, + "Pipeline9850B417": { + "Type": "AWS::CodePipeline::Pipeline", + "Properties": { + "RoleArn": { + "Fn::GetAtt": [ + "PipelineRoleB27FAA37", + "Arn" + ] + }, + "Stages": [ + { + "Actions": [ + { + "ActionTypeId": { + "Category": "Source", + "Owner": "AWS", + "Provider": "S3", + "Version": "1" + }, + "Configuration": { + "S3Bucket": { + "Ref": "SourceBucketDDD2130A" + }, + "S3ObjectKey": "key" + }, + "Name": { + "Ref": "SourceBucketDDD2130A" + }, + "OutputArtifacts": [ + { + "Name": "c8506b445957b8105ede7b68ebe35e9406d642cd0c_Source" + } + ], + "RoleArn": { + "Fn::GetAtt": [ + "PipelineSourceS3CodePipelineActionRole83895A58", + "Arn" + ] + }, + "RunOrder": 1 + } + ], + "Name": "Source" + }, + { + "Actions": [ + { + "ActionTypeId": { + "Category": "Build", + "Owner": "AWS", + "Provider": "CodeBuild", + "Version": "1" + }, + "Configuration": { + "ProjectName": { + "Ref": "PipelineBuildSynthCdkBuildProject6BEFA8E6" + } + }, + "InputArtifacts": [ + { + "Name": "c8506b445957b8105ede7b68ebe35e9406d642cd0c_Source" + } + ], + "Name": "Synth", + "OutputArtifacts": [ + { + "Name": "Synth_Output" + } + ], + "RoleArn": { + "Fn::GetAtt": [ + "PipelineCodeBuildActionRole226DB0CB", + "Arn" + ] + }, + "RunOrder": 1 + } + ], + "Name": "Build" + }, + { + "Actions": [ + { + "ActionTypeId": { + "Category": "Deploy", + "Owner": "AWS", + "Provider": "CloudFormation", + "Version": "1" + }, + "Configuration": { + "StackName": "MyStage-Stack", + "Capabilities": "CAPABILITY_NAMED_IAM,CAPABILITY_AUTO_EXPAND", + "RoleArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":role/cdk-hnb659fds-cfn-exec-role-", + { + "Ref": "AWS::AccountId" + }, + "-", + { + "Ref": "AWS::Region" + } + ] + ] + }, + "ActionMode": "CREATE_UPDATE", + "TemplatePath": "Synth_Output::assembly-PreparelessPipelineStack-MyStage/PreparelessPipelineStackMyStageStack3DC192E7.template.json" + }, + "InputArtifacts": [ + { + "Name": "Synth_Output" + } + ], + "Name": "Deploy", + "RoleArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":role/cdk-hnb659fds-deploy-role-", + { + "Ref": "AWS::AccountId" + }, + "-", + { + "Ref": "AWS::Region" + } + ] + ] + }, + "RunOrder": 1 + } + ], + "Name": "MyStage" + } + ], + "ArtifactStore": { + "Location": { + "Ref": "PipelineArtifactsBucketAEA9A052" + }, + "Type": "S3" + }, + "RestartExecutionOnUpdate": true + }, + "DependsOn": [ + "PipelineRoleDefaultPolicy7BDC1ABB", + "PipelineRoleB27FAA37" + ] + }, + "PipelineSourceS3CodePipelineActionRole83895A58": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelineSourceS3CodePipelineActionRoleDefaultPolicyB176A07F": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "SourceBucketDDD2130A", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "SourceBucketDDD2130A", + "Arn" + ] + }, + "/key" + ] + ] + } + ] + }, + { + "Action": [ + "s3:Abort*", + "s3:DeleteObject*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelineSourceS3CodePipelineActionRoleDefaultPolicyB176A07F", + "Roles": [ + { + "Ref": "PipelineSourceS3CodePipelineActionRole83895A58" + } + ] + } + }, + "PipelineBuildSynthCdkBuildProjectRole231EEA2A": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codebuild.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelineBuildSynthCdkBuildProjectRoleDefaultPolicyFB6C941C": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/codebuild/", + { + "Ref": "PipelineBuildSynthCdkBuildProject6BEFA8E6" + }, + ":*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/codebuild/", + { + "Ref": "PipelineBuildSynthCdkBuildProject6BEFA8E6" + } + ] + ] + } + ] + }, + { + "Action": [ + "codebuild:BatchPutCodeCoverages", + "codebuild:BatchPutTestCases", + "codebuild:CreateReport", + "codebuild:CreateReportGroup", + "codebuild:UpdateReport" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":codebuild:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":report-group/", + { + "Ref": "PipelineBuildSynthCdkBuildProject6BEFA8E6" + }, + "-*" + ] + ] + } + }, + { + "Action": [ + "s3:Abort*", + "s3:DeleteObject*", + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelineBuildSynthCdkBuildProjectRoleDefaultPolicyFB6C941C", + "Roles": [ + { + "Ref": "PipelineBuildSynthCdkBuildProjectRole231EEA2A" + } + ] + } + }, + "PipelineBuildSynthCdkBuildProject6BEFA8E6": { + "Type": "AWS::CodeBuild::Project", + "Properties": { + "Artifacts": { + "Type": "CODEPIPELINE" + }, + "Environment": { + "ComputeType": "BUILD_GENERAL1_SMALL", + "Image": "aws/codebuild/standard:5.0", + "ImagePullCredentialsType": "CODEBUILD", + "PrivilegedMode": false, + "Type": "LINUX_CONTAINER" + }, + "ServiceRole": { + "Fn::GetAtt": [ + "PipelineBuildSynthCdkBuildProjectRole231EEA2A", + "Arn" + ] + }, + "Source": { + "BuildSpec": "{\n \"version\": \"0.2\",\n \"phases\": {\n \"build\": {\n \"commands\": [\n \"mkdir cdk.out\",\n \"touch cdk.out/dummy\"\n ]\n }\n },\n \"artifacts\": {\n \"base-directory\": \"cdk.out\",\n \"files\": \"**/*\"\n }\n}", + "Type": "CODEPIPELINE" + }, + "Cache": { + "Type": "NO_CACHE" + }, + "Description": "Pipeline step PreparelessPipelineStack/Pipeline/Build/Synth", + "EncryptionKey": "alias/aws/s3" + } + }, + "PipelineCodeBuildActionRole226DB0CB": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Condition": { + "Bool": { + "aws:ViaAWSService": "codepipeline.amazonaws.com" + } + }, + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelineCodeBuildActionRoleDefaultPolicy1D62A6FE": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "codebuild:BatchGetBuilds", + "codebuild:StartBuild", + "codebuild:StopBuild" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelineBuildSynthCdkBuildProject6BEFA8E6", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelineCodeBuildActionRoleDefaultPolicy1D62A6FE", + "Roles": [ + { + "Ref": "PipelineCodeBuildActionRole226DB0CB" + } + ] + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6.assets.json b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6.assets.json new file mode 100644 index 0000000000000..770de88c374a0 --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6.template.json b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/PreparelessPipelineStackMyStageStack3DC192E7.assets.json b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/PreparelessPipelineStackMyStageStack3DC192E7.assets.json new file mode 100644 index 0000000000000..639913e637b14 --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/PreparelessPipelineStackMyStageStack3DC192E7.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "PreparelessPipelineStackMyStageStack3DC192E7.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/PreparelessPipelineStackMyStageStack3DC192E7.template.json b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/PreparelessPipelineStackMyStageStack3DC192E7.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/PreparelessPipelineStackMyStageStack3DC192E7.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/assembly-PreparelessPipelineStack-MyStage-Stack-MyApp/PreparelessPipelineStackMyStageStackMyAppStack51FBCD39.assets.json b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/assembly-PreparelessPipelineStack-MyStage-Stack-MyApp/PreparelessPipelineStackMyStageStackMyAppStack51FBCD39.assets.json new file mode 100644 index 0000000000000..2be2327136882 --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/assembly-PreparelessPipelineStack-MyStage-Stack-MyApp/PreparelessPipelineStackMyStageStackMyAppStack51FBCD39.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "1fd92c49dfa8050744f5e5169a7c3739923777fad154ba6b7275118191534ffc": { + "source": { + "path": "PreparelessPipelineStackMyStageStackMyAppStack51FBCD39.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "1fd92c49dfa8050744f5e5169a7c3739923777fad154ba6b7275118191534ffc.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/assembly-PreparelessPipelineStack-MyStage-Stack-MyApp/PreparelessPipelineStackMyStageStackMyAppStack51FBCD39.template.json b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/assembly-PreparelessPipelineStack-MyStage-Stack-MyApp/PreparelessPipelineStackMyStageStackMyAppStack51FBCD39.template.json new file mode 100644 index 0000000000000..fa27c2ed77a28 --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/assembly-PreparelessPipelineStack-MyStage-Stack-MyApp/PreparelessPipelineStackMyStageStackMyAppStack51FBCD39.template.json @@ -0,0 +1,43 @@ +{ + "Resources": { + "Bucket83908E77": { + "Type": "AWS::S3::Bucket", + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/assembly-PreparelessPipelineStack-MyStage-Stack-MyApp/cdk.out b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/assembly-PreparelessPipelineStack-MyStage-Stack-MyApp/cdk.out new file mode 100644 index 0000000000000..8ecc185e9dbee --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/assembly-PreparelessPipelineStack-MyStage-Stack-MyApp/cdk.out @@ -0,0 +1 @@ +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/assembly-PreparelessPipelineStack-MyStage-Stack-MyApp/manifest.json b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/assembly-PreparelessPipelineStack-MyStage-Stack-MyApp/manifest.json new file mode 100644 index 0000000000000..f7bf8d40a960f --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/assembly-PreparelessPipelineStack-MyStage-Stack-MyApp/manifest.json @@ -0,0 +1,59 @@ +{ + "version": "21.0.0", + "artifacts": { + "PreparelessPipelineStackMyStageStackMyAppStack51FBCD39.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "PreparelessPipelineStackMyStageStackMyAppStack51FBCD39.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "PreparelessPipelineStackMyStageStackMyAppStack51FBCD39": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "PreparelessPipelineStackMyStageStackMyAppStack51FBCD39.template.json", + "validateOnSynth": true, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/1fd92c49dfa8050744f5e5169a7c3739923777fad154ba6b7275118191534ffc.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "PreparelessPipelineStackMyStageStackMyAppStack51FBCD39.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + }, + "stackName": "MyStage-MyApp-Stack" + }, + "dependencies": [ + "PreparelessPipelineStackMyStageStackMyAppStack51FBCD39.assets" + ], + "metadata": { + "/PreparelessPipelineStack/MyStage/Stack/MyApp/Stack/Bucket/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Bucket83908E77" + } + ], + "/PreparelessPipelineStack/MyStage/Stack/MyApp/Stack/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/PreparelessPipelineStack/MyStage/Stack/MyApp/Stack/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "PreparelessPipelineStack/MyStage/Stack/MyApp/Stack" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/cdk.out b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/cdk.out new file mode 100644 index 0000000000000..8ecc185e9dbee --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/cdk.out @@ -0,0 +1 @@ +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/manifest.json b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/manifest.json new file mode 100644 index 0000000000000..6887ae7fd2585 --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/manifest.json @@ -0,0 +1,60 @@ +{ + "version": "21.0.0", + "artifacts": { + "assembly-PreparelessPipelineStack-MyStage-Stack-MyApp": { + "type": "cdk:cloud-assembly", + "properties": { + "directoryName": "assembly-PreparelessPipelineStack-MyStage-Stack-MyApp", + "displayName": "PreparelessPipelineStack/MyStage/Stack/MyApp" + } + }, + "PreparelessPipelineStackMyStageStack3DC192E7.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "PreparelessPipelineStackMyStageStack3DC192E7.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "PreparelessPipelineStackMyStageStack3DC192E7": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "PreparelessPipelineStackMyStageStack3DC192E7.template.json", + "validateOnSynth": true, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "PreparelessPipelineStackMyStageStack3DC192E7.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + }, + "stackName": "MyStage-Stack" + }, + "dependencies": [ + "PreparelessPipelineStackMyStageStack3DC192E7.assets" + ], + "metadata": { + "/PreparelessPipelineStack/MyStage/Stack/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/PreparelessPipelineStack/MyStage/Stack/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "PreparelessPipelineStack/MyStage/Stack" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/__entrypoint__.js b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/__entrypoint__.js new file mode 100644 index 0000000000000..9df94382cc74e --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/__entrypoint__.js @@ -0,0 +1,118 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handler = exports.external = void 0; +const https = require("https"); +const url = require("url"); +// for unit tests +exports.external = { + sendHttpRequest: defaultSendHttpRequest, + log: defaultLog, + includeStackTraces: true, + userHandlerIndex: './index', +}; +const CREATE_FAILED_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::CREATE_FAILED'; +const MISSING_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::MISSING_PHYSICAL_ID'; +async function handler(event, context) { + const sanitizedEvent = { ...event, ResponseURL: '...' }; + exports.external.log(JSON.stringify(sanitizedEvent, undefined, 2)); + // ignore DELETE event when the physical resource ID is the marker that + // indicates that this DELETE is a subsequent DELETE to a failed CREATE + // operation. + if (event.RequestType === 'Delete' && event.PhysicalResourceId === CREATE_FAILED_PHYSICAL_ID_MARKER) { + exports.external.log('ignoring DELETE event caused by a failed CREATE event'); + await submitResponse('SUCCESS', event); + return; + } + try { + // invoke the user handler. this is intentionally inside the try-catch to + // ensure that if there is an error it's reported as a failure to + // cloudformation (otherwise cfn waits). + // eslint-disable-next-line @typescript-eslint/no-require-imports + const userHandler = require(exports.external.userHandlerIndex).handler; + const result = await userHandler(sanitizedEvent, context); + // validate user response and create the combined event + const responseEvent = renderResponse(event, result); + // submit to cfn as success + await submitResponse('SUCCESS', responseEvent); + } + catch (e) { + const resp = { + ...event, + Reason: exports.external.includeStackTraces ? e.stack : e.message, + }; + if (!resp.PhysicalResourceId) { + // special case: if CREATE fails, which usually implies, we usually don't + // have a physical resource id. in this case, the subsequent DELETE + // operation does not have any meaning, and will likely fail as well. to + // address this, we use a marker so the provider framework can simply + // ignore the subsequent DELETE. + if (event.RequestType === 'Create') { + exports.external.log('CREATE failed, responding with a marker physical resource id so that the subsequent DELETE will be ignored'); + resp.PhysicalResourceId = CREATE_FAILED_PHYSICAL_ID_MARKER; + } + else { + // otherwise, if PhysicalResourceId is not specified, something is + // terribly wrong because all other events should have an ID. + exports.external.log(`ERROR: Malformed event. "PhysicalResourceId" is required: ${JSON.stringify(event)}`); + } + } + // this is an actual error, fail the activity altogether and exist. + await submitResponse('FAILED', resp); + } +} +exports.handler = handler; +function renderResponse(cfnRequest, handlerResponse = {}) { + // if physical ID is not returned, we have some defaults for you based + // on the request type. + const physicalResourceId = handlerResponse.PhysicalResourceId ?? cfnRequest.PhysicalResourceId ?? cfnRequest.RequestId; + // if we are in DELETE and physical ID was changed, it's an error. + if (cfnRequest.RequestType === 'Delete' && physicalResourceId !== cfnRequest.PhysicalResourceId) { + throw new Error(`DELETE: cannot change the physical resource ID from "${cfnRequest.PhysicalResourceId}" to "${handlerResponse.PhysicalResourceId}" during deletion`); + } + // merge request event and result event (result prevails). + return { + ...cfnRequest, + ...handlerResponse, + PhysicalResourceId: physicalResourceId, + }; +} +async function submitResponse(status, event) { + const json = { + Status: status, + Reason: event.Reason ?? status, + StackId: event.StackId, + RequestId: event.RequestId, + PhysicalResourceId: event.PhysicalResourceId || MISSING_PHYSICAL_ID_MARKER, + LogicalResourceId: event.LogicalResourceId, + NoEcho: event.NoEcho, + Data: event.Data, + }; + exports.external.log('submit response to cloudformation', json); + const responseBody = JSON.stringify(json); + const parsedUrl = url.parse(event.ResponseURL); + const req = { + hostname: parsedUrl.hostname, + path: parsedUrl.path, + method: 'PUT', + headers: { 'content-type': '', 'content-length': responseBody.length }, + }; + await exports.external.sendHttpRequest(req, responseBody); +} +async function defaultSendHttpRequest(options, responseBody) { + return new Promise((resolve, reject) => { + try { + const request = https.request(options, _ => resolve()); + request.on('error', reject); + request.write(responseBody); + request.end(); + } + catch (e) { + reject(e); + } + }); +} +function defaultLog(fmt, ...params) { + // eslint-disable-next-line no-console + console.log(fmt, ...params); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9kZWpzLWVudHJ5cG9pbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJub2RlanMtZW50cnlwb2ludC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwrQkFBK0I7QUFDL0IsMkJBQTJCO0FBRTNCLGlCQUFpQjtBQUNKLFFBQUEsUUFBUSxHQUFHO0lBQ3RCLGVBQWUsRUFBRSxzQkFBc0I7SUFDdkMsR0FBRyxFQUFFLFVBQVU7SUFDZixrQkFBa0IsRUFBRSxJQUFJO0lBQ3hCLGdCQUFnQixFQUFFLFNBQVM7Q0FDNUIsQ0FBQztBQUVGLE1BQU0sZ0NBQWdDLEdBQUcsd0RBQXdELENBQUM7QUFDbEcsTUFBTSwwQkFBMEIsR0FBRyw4REFBOEQsQ0FBQztBQVczRixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtELEVBQUUsT0FBMEI7SUFDMUcsTUFBTSxjQUFjLEdBQUcsRUFBRSxHQUFHLEtBQUssRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLENBQUM7SUFDeEQsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxjQUFjLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFM0QsdUVBQXVFO0lBQ3ZFLHVFQUF1RTtJQUN2RSxhQUFhO0lBQ2IsSUFBSSxLQUFLLENBQUMsV0FBVyxLQUFLLFFBQVEsSUFBSSxLQUFLLENBQUMsa0JBQWtCLEtBQUssZ0NBQWdDLEVBQUU7UUFDbkcsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsdURBQXVELENBQUMsQ0FBQztRQUN0RSxNQUFNLGNBQWMsQ0FBQyxTQUFTLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDdkMsT0FBTztLQUNSO0lBRUQsSUFBSTtRQUNGLHlFQUF5RTtRQUN6RSxpRUFBaUU7UUFDakUsd0NBQXdDO1FBQ3hDLGlFQUFpRTtRQUNqRSxNQUFNLFdBQVcsR0FBWSxPQUFPLENBQUMsZ0JBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLE9BQU8sQ0FBQztRQUN4RSxNQUFNLE1BQU0sR0FBRyxNQUFNLFdBQVcsQ0FBQyxjQUFjLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFFMUQsdURBQXVEO1FBQ3ZELE1BQU0sYUFBYSxHQUFHLGNBQWMsQ0FBQyxLQUFLLEVBQUUsTUFBTSxDQUFDLENBQUM7UUFFcEQsMkJBQTJCO1FBQzNCLE1BQU0sY0FBYyxDQUFDLFNBQVMsRUFBRSxhQUFhLENBQUMsQ0FBQztLQUNoRDtJQUFDLE9BQU8sQ0FBQyxFQUFFO1FBQ1YsTUFBTSxJQUFJLEdBQWE7WUFDckIsR0FBRyxLQUFLO1lBQ1IsTUFBTSxFQUFFLGdCQUFRLENBQUMsa0JBQWtCLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPO1NBQzFELENBQUM7UUFFRixJQUFJLENBQUMsSUFBSSxDQUFDLGtCQUFrQixFQUFFO1lBQzVCLHlFQUF5RTtZQUN6RSxtRUFBbUU7WUFDbkUsd0VBQXdFO1lBQ3hFLHFFQUFxRTtZQUNyRSxnQ0FBZ0M7WUFDaEMsSUFBSSxLQUFLLENBQUMsV0FBVyxLQUFLLFFBQVEsRUFBRTtnQkFDbEMsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsNEdBQTRHLENBQUMsQ0FBQztnQkFDM0gsSUFBSSxDQUFDLGtCQUFrQixHQUFHLGdDQUFnQyxDQUFDO2FBQzVEO2lCQUFNO2dCQUNMLGtFQUFrRTtnQkFDbEUsNkRBQTZEO2dCQUM3RCxnQkFBUSxDQUFDLEdBQUcsQ0FBQyw2REFBNkQsSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUM7YUFDcEc7U0FDRjtRQUVELG1FQUFtRTtRQUNuRSxNQUFNLGNBQWMsQ0FBQyxRQUFRLEVBQUUsSUFBSSxDQUFDLENBQUM7S0FDdEM7QUFDSCxDQUFDO0FBbkRELDBCQW1EQztBQUVELFNBQVMsY0FBYyxDQUNyQixVQUF5RixFQUN6RixrQkFBMEMsRUFBRztJQUU3QyxzRUFBc0U7SUFDdEUsdUJBQXVCO0lBQ3ZCLE1BQU0sa0JBQWtCLEdBQUcsZUFBZSxDQUFDLGtCQUFrQixJQUFJLFVBQVUsQ0FBQyxrQkFBa0IsSUFBSSxVQUFVLENBQUMsU0FBUyxDQUFDO0lBRXZILGtFQUFrRTtJQUNsRSxJQUFJLFVBQVUsQ0FBQyxXQUFXLEtBQUssUUFBUSxJQUFJLGtCQUFrQixLQUFLLFVBQVUsQ0FBQyxrQkFBa0IsRUFBRTtRQUMvRixNQUFNLElBQUksS0FBSyxDQUFDLHdEQUF3RCxVQUFVLENBQUMsa0JBQWtCLFNBQVMsZUFBZSxDQUFDLGtCQUFrQixtQkFBbUIsQ0FBQyxDQUFDO0tBQ3RLO0lBRUQsMERBQTBEO0lBQzFELE9BQU87UUFDTCxHQUFHLFVBQVU7UUFDYixHQUFHLGVBQWU7UUFDbEIsa0JBQWtCLEVBQUUsa0JBQWtCO0tBQ3ZDLENBQUM7QUFDSixDQUFDO0FBRUQsS0FBSyxVQUFVLGNBQWMsQ0FBQyxNQUE0QixFQUFFLEtBQWU7SUFDekUsTUFBTSxJQUFJLEdBQW1EO1FBQzNELE1BQU0sRUFBRSxNQUFNO1FBQ2QsTUFBTSxFQUFFLEtBQUssQ0FBQyxNQUFNLElBQUksTUFBTTtRQUM5QixPQUFPLEVBQUUsS0FBSyxDQUFDLE9BQU87UUFDdEIsU0FBUyxFQUFFLEtBQUssQ0FBQyxTQUFTO1FBQzFCLGtCQUFrQixFQUFFLEtBQUssQ0FBQyxrQkFBa0IsSUFBSSwwQkFBMEI7UUFDMUUsaUJBQWlCLEVBQUUsS0FBSyxDQUFDLGlCQUFpQjtRQUMxQyxNQUFNLEVBQUUsS0FBSyxDQUFDLE1BQU07UUFDcEIsSUFBSSxFQUFFLEtBQUssQ0FBQyxJQUFJO0tBQ2pCLENBQUM7SUFFRixnQkFBUSxDQUFDLEdBQUcsQ0FBQyxtQ0FBbUMsRUFBRSxJQUFJLENBQUMsQ0FBQztJQUV4RCxNQUFNLFlBQVksR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQzFDLE1BQU0sU0FBUyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLFdBQVcsQ0FBQyxDQUFDO0lBQy9DLE1BQU0sR0FBRyxHQUFHO1FBQ1YsUUFBUSxFQUFFLFNBQVMsQ0FBQyxRQUFRO1FBQzVCLElBQUksRUFBRSxTQUFTLENBQUMsSUFBSTtRQUNwQixNQUFNLEVBQUUsS0FBSztRQUNiLE9BQU8sRUFBRSxFQUFFLGNBQWMsRUFBRSxFQUFFLEVBQUUsZ0JBQWdCLEVBQUUsWUFBWSxDQUFDLE1BQU0sRUFBRTtLQUN2RSxDQUFDO0lBRUYsTUFBTSxnQkFBUSxDQUFDLGVBQWUsQ0FBQyxHQUFHLEVBQUUsWUFBWSxDQUFDLENBQUM7QUFDcEQsQ0FBQztBQUVELEtBQUssVUFBVSxzQkFBc0IsQ0FBQyxPQUE2QixFQUFFLFlBQW9CO0lBQ3ZGLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEVBQUU7UUFDckMsSUFBSTtZQUNGLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQztZQUN2RCxPQUFPLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQztZQUM1QixPQUFPLENBQUMsS0FBSyxDQUFDLFlBQVksQ0FBQyxDQUFDO1lBQzVCLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQztTQUNmO1FBQUMsT0FBTyxDQUFDLEVBQUU7WUFDVixNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7U0FDWDtJQUNILENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVELFNBQVMsVUFBVSxDQUFDLEdBQVcsRUFBRSxHQUFHLE1BQWE7SUFDL0Msc0NBQXNDO0lBQ3RDLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsTUFBTSxDQUFDLENBQUM7QUFDOUIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGh0dHBzIGZyb20gJ2h0dHBzJztcbmltcG9ydCAqIGFzIHVybCBmcm9tICd1cmwnO1xuXG4vLyBmb3IgdW5pdCB0ZXN0c1xuZXhwb3J0IGNvbnN0IGV4dGVybmFsID0ge1xuICBzZW5kSHR0cFJlcXVlc3Q6IGRlZmF1bHRTZW5kSHR0cFJlcXVlc3QsXG4gIGxvZzogZGVmYXVsdExvZyxcbiAgaW5jbHVkZVN0YWNrVHJhY2VzOiB0cnVlLFxuICB1c2VySGFuZGxlckluZGV4OiAnLi9pbmRleCcsXG59O1xuXG5jb25zdCBDUkVBVEVfRkFJTEVEX1BIWVNJQ0FMX0lEX01BUktFUiA9ICdBV1NDREs6OkN1c3RvbVJlc291cmNlUHJvdmlkZXJGcmFtZXdvcms6OkNSRUFURV9GQUlMRUQnO1xuY29uc3QgTUlTU0lOR19QSFlTSUNBTF9JRF9NQVJLRVIgPSAnQVdTQ0RLOjpDdXN0b21SZXNvdXJjZVByb3ZpZGVyRnJhbWV3b3JrOjpNSVNTSU5HX1BIWVNJQ0FMX0lEJztcblxuZXhwb3J0IHR5cGUgUmVzcG9uc2UgPSBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50ICYgSGFuZGxlclJlc3BvbnNlO1xuZXhwb3J0IHR5cGUgSGFuZGxlciA9IChldmVudDogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCwgY29udGV4dDogQVdTTGFtYmRhLkNvbnRleHQpID0+IFByb21pc2U8SGFuZGxlclJlc3BvbnNlIHwgdm9pZD47XG5leHBvcnQgdHlwZSBIYW5kbGVyUmVzcG9uc2UgPSB1bmRlZmluZWQgfCB7XG4gIERhdGE/OiBhbnk7XG4gIFBoeXNpY2FsUmVzb3VyY2VJZD86IHN0cmluZztcbiAgUmVhc29uPzogc3RyaW5nO1xuICBOb0VjaG8/OiBib29sZWFuO1xufTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQsIGNvbnRleHQ6IEFXU0xhbWJkYS5Db250ZXh0KSB7XG4gIGNvbnN0IHNhbml0aXplZEV2ZW50ID0geyAuLi5ldmVudCwgUmVzcG9uc2VVUkw6ICcuLi4nIH07XG4gIGV4dGVybmFsLmxvZyhKU09OLnN0cmluZ2lmeShzYW5pdGl6ZWRFdmVudCwgdW5kZWZpbmVkLCAyKSk7XG5cbiAgLy8gaWdub3JlIERFTEVURSBldmVudCB3aGVuIHRoZSBwaHlzaWNhbCByZXNvdXJjZSBJRCBpcyB0aGUgbWFya2VyIHRoYXRcbiAgLy8gaW5kaWNhdGVzIHRoYXQgdGhpcyBERUxFVEUgaXMgYSBzdWJzZXF1ZW50IERFTEVURSB0byBhIGZhaWxlZCBDUkVBVEVcbiAgLy8gb3BlcmF0aW9uLlxuICBpZiAoZXZlbnQuUmVxdWVzdFR5cGUgPT09ICdEZWxldGUnICYmIGV2ZW50LlBoeXNpY2FsUmVzb3VyY2VJZCA9PT0gQ1JFQVRFX0ZBSUxFRF9QSFlTSUNBTF9JRF9NQVJLRVIpIHtcbiAgICBleHRlcm5hbC5sb2coJ2lnbm9yaW5nIERFTEVURSBldmVudCBjYXVzZWQgYnkgYSBmYWlsZWQgQ1JFQVRFIGV2ZW50Jyk7XG4gICAgYXdhaXQgc3VibWl0UmVzcG9uc2UoJ1NVQ0NFU1MnLCBldmVudCk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgdHJ5IHtcbiAgICAvLyBpbnZva2UgdGhlIHVzZXIgaGFuZGxlci4gdGhpcyBpcyBpbnRlbnRpb25hbGx5IGluc2lkZSB0aGUgdHJ5LWNhdGNoIHRvXG4gICAgLy8gZW5zdXJlIHRoYXQgaWYgdGhlcmUgaXMgYW4gZXJyb3IgaXQncyByZXBvcnRlZCBhcyBhIGZhaWx1cmUgdG9cbiAgICAvLyBjbG91ZGZvcm1hdGlvbiAob3RoZXJ3aXNlIGNmbiB3YWl0cykuXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHNcbiAgICBjb25zdCB1c2VySGFuZGxlcjogSGFuZGxlciA9IHJlcXVpcmUoZXh0ZXJuYWwudXNlckhhbmRsZXJJbmRleCkuaGFuZGxlcjtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB1c2VySGFuZGxlcihzYW5pdGl6ZWRFdmVudCwgY29udGV4dCk7XG5cbiAgICAvLyB2YWxpZGF0ZSB1c2VyIHJlc3BvbnNlIGFuZCBjcmVhdGUgdGhlIGNvbWJpbmVkIGV2ZW50XG4gICAgY29uc3QgcmVzcG9uc2VFdmVudCA9IHJlbmRlclJlc3BvbnNlKGV2ZW50LCByZXN1bHQpO1xuXG4gICAgLy8gc3VibWl0IHRvIGNmbiBhcyBzdWNjZXNzXG4gICAgYXdhaXQgc3VibWl0UmVzcG9uc2UoJ1NVQ0NFU1MnLCByZXNwb25zZUV2ZW50KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnN0IHJlc3A6IFJlc3BvbnNlID0ge1xuICAgICAgLi4uZXZlbnQsXG4gICAgICBSZWFzb246IGV4dGVybmFsLmluY2x1ZGVTdGFja1RyYWNlcyA/IGUuc3RhY2sgOiBlLm1lc3NhZ2UsXG4gICAgfTtcblxuICAgIGlmICghcmVzcC5QaHlzaWNhbFJlc291cmNlSWQpIHtcbiAgICAgIC8vIHNwZWNpYWwgY2FzZTogaWYgQ1JFQVRFIGZhaWxzLCB3aGljaCB1c3VhbGx5IGltcGxpZXMsIHdlIHVzdWFsbHkgZG9uJ3RcbiAgICAgIC8vIGhhdmUgYSBwaHlzaWNhbCByZXNvdXJjZSBpZC4gaW4gdGhpcyBjYXNlLCB0aGUgc3Vic2VxdWVudCBERUxFVEVcbiAgICAgIC8vIG9wZXJhdGlvbiBkb2VzIG5vdCBoYXZlIGFueSBtZWFuaW5nLCBhbmQgd2lsbCBsaWtlbHkgZmFpbCBhcyB3ZWxsLiB0b1xuICAgICAgLy8gYWRkcmVzcyB0aGlzLCB3ZSB1c2UgYSBtYXJrZXIgc28gdGhlIHByb3ZpZGVyIGZyYW1ld29yayBjYW4gc2ltcGx5XG4gICAgICAvLyBpZ25vcmUgdGhlIHN1YnNlcXVlbnQgREVMRVRFLlxuICAgICAgaWYgKGV2ZW50LlJlcXVlc3RUeXBlID09PSAnQ3JlYXRlJykge1xuICAgICAgICBleHRlcm5hbC5sb2coJ0NSRUFURSBmYWlsZWQsIHJlc3BvbmRpbmcgd2l0aCBhIG1hcmtlciBwaHlzaWNhbCByZXNvdXJjZSBpZCBzbyB0aGF0IHRoZSBzdWJzZXF1ZW50IERFTEVURSB3aWxsIGJlIGlnbm9yZWQnKTtcbiAgICAgICAgcmVzcC5QaHlzaWNhbFJlc291cmNlSWQgPSBDUkVBVEVfRkFJTEVEX1BIWVNJQ0FMX0lEX01BUktFUjtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIG90aGVyd2lzZSwgaWYgUGh5c2ljYWxSZXNvdXJjZUlkIGlzIG5vdCBzcGVjaWZpZWQsIHNvbWV0aGluZyBpc1xuICAgICAgICAvLyB0ZXJyaWJseSB3cm9uZyBiZWNhdXNlIGFsbCBvdGhlciBldmVudHMgc2hvdWxkIGhhdmUgYW4gSUQuXG4gICAgICAgIGV4dGVybmFsLmxvZyhgRVJST1I6IE1hbGZvcm1lZCBldmVudC4gXCJQaHlzaWNhbFJlc291cmNlSWRcIiBpcyByZXF1aXJlZDogJHtKU09OLnN0cmluZ2lmeShldmVudCl9YCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gdGhpcyBpcyBhbiBhY3R1YWwgZXJyb3IsIGZhaWwgdGhlIGFjdGl2aXR5IGFsdG9nZXRoZXIgYW5kIGV4aXN0LlxuICAgIGF3YWl0IHN1Ym1pdFJlc3BvbnNlKCdGQUlMRUQnLCByZXNwKTtcbiAgfVxufVxuXG5mdW5jdGlvbiByZW5kZXJSZXNwb25zZShcbiAgY2ZuUmVxdWVzdDogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCAmIHsgUGh5c2ljYWxSZXNvdXJjZUlkPzogc3RyaW5nIH0sXG4gIGhhbmRsZXJSZXNwb25zZTogdm9pZCB8IEhhbmRsZXJSZXNwb25zZSA9IHsgfSk6IFJlc3BvbnNlIHtcblxuICAvLyBpZiBwaHlzaWNhbCBJRCBpcyBub3QgcmV0dXJuZWQsIHdlIGhhdmUgc29tZSBkZWZhdWx0cyBmb3IgeW91IGJhc2VkXG4gIC8vIG9uIHRoZSByZXF1ZXN0IHR5cGUuXG4gIGNvbnN0IHBoeXNpY2FsUmVzb3VyY2VJZCA9IGhhbmRsZXJSZXNwb25zZS5QaHlzaWNhbFJlc291cmNlSWQgPz8gY2ZuUmVxdWVzdC5QaHlzaWNhbFJlc291cmNlSWQgPz8gY2ZuUmVxdWVzdC5SZXF1ZXN0SWQ7XG5cbiAgLy8gaWYgd2UgYXJlIGluIERFTEVURSBhbmQgcGh5c2ljYWwgSUQgd2FzIGNoYW5nZWQsIGl0J3MgYW4gZXJyb3IuXG4gIGlmIChjZm5SZXF1ZXN0LlJlcXVlc3RUeXBlID09PSAnRGVsZXRlJyAmJiBwaHlzaWNhbFJlc291cmNlSWQgIT09IGNmblJlcXVlc3QuUGh5c2ljYWxSZXNvdXJjZUlkKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBERUxFVEU6IGNhbm5vdCBjaGFuZ2UgdGhlIHBoeXNpY2FsIHJlc291cmNlIElEIGZyb20gXCIke2NmblJlcXVlc3QuUGh5c2ljYWxSZXNvdXJjZUlkfVwiIHRvIFwiJHtoYW5kbGVyUmVzcG9uc2UuUGh5c2ljYWxSZXNvdXJjZUlkfVwiIGR1cmluZyBkZWxldGlvbmApO1xuICB9XG5cbiAgLy8gbWVyZ2UgcmVxdWVzdCBldmVudCBhbmQgcmVzdWx0IGV2ZW50IChyZXN1bHQgcHJldmFpbHMpLlxuICByZXR1cm4ge1xuICAgIC4uLmNmblJlcXVlc3QsXG4gICAgLi4uaGFuZGxlclJlc3BvbnNlLFxuICAgIFBoeXNpY2FsUmVzb3VyY2VJZDogcGh5c2ljYWxSZXNvdXJjZUlkLFxuICB9O1xufVxuXG5hc3luYyBmdW5jdGlvbiBzdWJtaXRSZXNwb25zZShzdGF0dXM6ICdTVUNDRVNTJyB8ICdGQUlMRUQnLCBldmVudDogUmVzcG9uc2UpIHtcbiAgY29uc3QganNvbjogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VSZXNwb25zZSA9IHtcbiAgICBTdGF0dXM6IHN0YXR1cyxcbiAgICBSZWFzb246IGV2ZW50LlJlYXNvbiA/PyBzdGF0dXMsXG4gICAgU3RhY2tJZDogZXZlbnQuU3RhY2tJZCxcbiAgICBSZXF1ZXN0SWQ6IGV2ZW50LlJlcXVlc3RJZCxcbiAgICBQaHlzaWNhbFJlc291cmNlSWQ6IGV2ZW50LlBoeXNpY2FsUmVzb3VyY2VJZCB8fCBNSVNTSU5HX1BIWVNJQ0FMX0lEX01BUktFUixcbiAgICBMb2dpY2FsUmVzb3VyY2VJZDogZXZlbnQuTG9naWNhbFJlc291cmNlSWQsXG4gICAgTm9FY2hvOiBldmVudC5Ob0VjaG8sXG4gICAgRGF0YTogZXZlbnQuRGF0YSxcbiAgfTtcblxuICBleHRlcm5hbC5sb2coJ3N1Ym1pdCByZXNwb25zZSB0byBjbG91ZGZvcm1hdGlvbicsIGpzb24pO1xuXG4gIGNvbnN0IHJlc3BvbnNlQm9keSA9IEpTT04uc3RyaW5naWZ5KGpzb24pO1xuICBjb25zdCBwYXJzZWRVcmwgPSB1cmwucGFyc2UoZXZlbnQuUmVzcG9uc2VVUkwpO1xuICBjb25zdCByZXEgPSB7XG4gICAgaG9zdG5hbWU6IHBhcnNlZFVybC5ob3N0bmFtZSxcbiAgICBwYXRoOiBwYXJzZWRVcmwucGF0aCxcbiAgICBtZXRob2Q6ICdQVVQnLFxuICAgIGhlYWRlcnM6IHsgJ2NvbnRlbnQtdHlwZSc6ICcnLCAnY29udGVudC1sZW5ndGgnOiByZXNwb25zZUJvZHkubGVuZ3RoIH0sXG4gIH07XG5cbiAgYXdhaXQgZXh0ZXJuYWwuc2VuZEh0dHBSZXF1ZXN0KHJlcSwgcmVzcG9uc2VCb2R5KTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZGVmYXVsdFNlbmRIdHRwUmVxdWVzdChvcHRpb25zOiBodHRwcy5SZXF1ZXN0T3B0aW9ucywgcmVzcG9uc2VCb2R5OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVxdWVzdCA9IGh0dHBzLnJlcXVlc3Qob3B0aW9ucywgXyA9PiByZXNvbHZlKCkpO1xuICAgICAgcmVxdWVzdC5vbignZXJyb3InLCByZWplY3QpO1xuICAgICAgcmVxdWVzdC53cml0ZShyZXNwb25zZUJvZHkpO1xuICAgICAgcmVxdWVzdC5lbmQoKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICByZWplY3QoZSk7XG4gICAgfVxuICB9KTtcbn1cblxuZnVuY3Rpb24gZGVmYXVsdExvZyhmbXQ6IHN0cmluZywgLi4ucGFyYW1zOiBhbnlbXSkge1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZVxuICBjb25zb2xlLmxvZyhmbXQsIC4uLnBhcmFtcyk7XG59XG4iXX0= \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/index.d.ts b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/index.d.ts new file mode 100644 index 0000000000000..3554dc94d4617 --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/index.d.ts @@ -0,0 +1 @@ +export declare function handler(event: AWSLambda.CloudFormationCustomResourceEvent): Promise; diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/index.js b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/index.js new file mode 100644 index 0000000000000..7ce4156d4ba41 --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/index.js @@ -0,0 +1,78 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handler = void 0; +// eslint-disable-next-line import/no-extraneous-dependencies +const aws_sdk_1 = require("aws-sdk"); +const AUTO_DELETE_OBJECTS_TAG = 'aws-cdk:auto-delete-objects'; +const s3 = new aws_sdk_1.S3(); +async function handler(event) { + switch (event.RequestType) { + case 'Create': + return; + case 'Update': + return onUpdate(event); + case 'Delete': + return onDelete(event.ResourceProperties?.BucketName); + } +} +exports.handler = handler; +async function onUpdate(event) { + const updateEvent = event; + const oldBucketName = updateEvent.OldResourceProperties?.BucketName; + const newBucketName = updateEvent.ResourceProperties?.BucketName; + const bucketNameHasChanged = newBucketName != null && oldBucketName != null && newBucketName !== oldBucketName; + /* If the name of the bucket has changed, CloudFormation will try to delete the bucket + and create a new one with the new name. So we have to delete the contents of the + bucket so that this operation does not fail. */ + if (bucketNameHasChanged) { + return onDelete(oldBucketName); + } +} +/** + * Recursively delete all items in the bucket + * + * @param bucketName the bucket name + */ +async function emptyBucket(bucketName) { + const listedObjects = await s3.listObjectVersions({ Bucket: bucketName }).promise(); + const contents = [...listedObjects.Versions ?? [], ...listedObjects.DeleteMarkers ?? []]; + if (contents.length === 0) { + return; + } + const records = contents.map((record) => ({ Key: record.Key, VersionId: record.VersionId })); + await s3.deleteObjects({ Bucket: bucketName, Delete: { Objects: records } }).promise(); + if (listedObjects?.IsTruncated) { + await emptyBucket(bucketName); + } +} +async function onDelete(bucketName) { + if (!bucketName) { + throw new Error('No BucketName was provided.'); + } + if (!await isBucketTaggedForDeletion(bucketName)) { + process.stdout.write(`Bucket does not have '${AUTO_DELETE_OBJECTS_TAG}' tag, skipping cleaning.\n`); + return; + } + try { + await emptyBucket(bucketName); + } + catch (e) { + if (e.code !== 'NoSuchBucket') { + throw e; + } + // Bucket doesn't exist. Ignoring + } +} +/** + * The bucket will only be tagged for deletion if it's being deleted in the same + * deployment as this Custom Resource. + * + * If the Custom Resource is every deleted before the bucket, it must be because + * `autoDeleteObjects` has been switched to false, in which case the tag would have + * been removed before we get to this Delete event. + */ +async function isBucketTaggedForDeletion(bucketName) { + const response = await s3.getBucketTagging({ Bucket: bucketName }).promise(); + return response.TagSet.some(tag => tag.Key === AUTO_DELETE_OBJECTS_TAG && tag.Value === 'true'); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2REFBNkQ7QUFDN0QscUNBQTZCO0FBRTdCLE1BQU0sdUJBQXVCLEdBQUcsNkJBQTZCLENBQUM7QUFFOUQsTUFBTSxFQUFFLEdBQUcsSUFBSSxZQUFFLEVBQUUsQ0FBQztBQUViLEtBQUssVUFBVSxPQUFPLENBQUMsS0FBa0Q7SUFDOUUsUUFBUSxLQUFLLENBQUMsV0FBVyxFQUFFO1FBQ3pCLEtBQUssUUFBUTtZQUNYLE9BQU87UUFDVCxLQUFLLFFBQVE7WUFDWCxPQUFPLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUN6QixLQUFLLFFBQVE7WUFDWCxPQUFPLFFBQVEsQ0FBQyxLQUFLLENBQUMsa0JBQWtCLEVBQUUsVUFBVSxDQUFDLENBQUM7S0FDekQ7QUFDSCxDQUFDO0FBVEQsMEJBU0M7QUFFRCxLQUFLLFVBQVUsUUFBUSxDQUFDLEtBQWtEO0lBQ3hFLE1BQU0sV0FBVyxHQUFHLEtBQTBELENBQUM7SUFDL0UsTUFBTSxhQUFhLEdBQUcsV0FBVyxDQUFDLHFCQUFxQixFQUFFLFVBQVUsQ0FBQztJQUNwRSxNQUFNLGFBQWEsR0FBRyxXQUFXLENBQUMsa0JBQWtCLEVBQUUsVUFBVSxDQUFDO0lBQ2pFLE1BQU0sb0JBQW9CLEdBQUcsYUFBYSxJQUFJLElBQUksSUFBSSxhQUFhLElBQUksSUFBSSxJQUFJLGFBQWEsS0FBSyxhQUFhLENBQUM7SUFFL0c7O3NEQUVrRDtJQUNsRCxJQUFJLG9CQUFvQixFQUFFO1FBQ3hCLE9BQU8sUUFBUSxDQUFDLGFBQWEsQ0FBQyxDQUFDO0tBQ2hDO0FBQ0gsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxLQUFLLFVBQVUsV0FBVyxDQUFDLFVBQWtCO0lBQzNDLE1BQU0sYUFBYSxHQUFHLE1BQU0sRUFBRSxDQUFDLGtCQUFrQixDQUFDLEVBQUUsTUFBTSxFQUFFLFVBQVUsRUFBRSxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7SUFDcEYsTUFBTSxRQUFRLEdBQUcsQ0FBQyxHQUFHLGFBQWEsQ0FBQyxRQUFRLElBQUksRUFBRSxFQUFFLEdBQUcsYUFBYSxDQUFDLGFBQWEsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUN6RixJQUFJLFFBQVEsQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFO1FBQ3pCLE9BQU87S0FDUjtJQUVELE1BQU0sT0FBTyxHQUFHLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxNQUFXLEVBQUUsRUFBRSxDQUFDLENBQUMsRUFBRSxHQUFHLEVBQUUsTUFBTSxDQUFDLEdBQUcsRUFBRSxTQUFTLEVBQUUsTUFBTSxDQUFDLFNBQVMsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUNsRyxNQUFNLEVBQUUsQ0FBQyxhQUFhLENBQUMsRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLE1BQU0sRUFBRSxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsRUFBRSxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7SUFFdkYsSUFBSSxhQUFhLEVBQUUsV0FBVyxFQUFFO1FBQzlCLE1BQU0sV0FBVyxDQUFDLFVBQVUsQ0FBQyxDQUFDO0tBQy9CO0FBQ0gsQ0FBQztBQUVELEtBQUssVUFBVSxRQUFRLENBQUMsVUFBbUI7SUFDekMsSUFBSSxDQUFDLFVBQVUsRUFBRTtRQUNmLE1BQU0sSUFBSSxLQUFLLENBQUMsNkJBQTZCLENBQUMsQ0FBQztLQUNoRDtJQUNELElBQUksQ0FBQyxNQUFNLHlCQUF5QixDQUFDLFVBQVUsQ0FBQyxFQUFFO1FBQ2hELE9BQU8sQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLHlCQUF5Qix1QkFBdUIsNkJBQTZCLENBQUMsQ0FBQztRQUNwRyxPQUFPO0tBQ1I7SUFDRCxJQUFJO1FBQ0YsTUFBTSxXQUFXLENBQUMsVUFBVSxDQUFDLENBQUM7S0FDL0I7SUFBQyxPQUFPLENBQUMsRUFBRTtRQUNWLElBQUksQ0FBQyxDQUFDLElBQUksS0FBSyxjQUFjLEVBQUU7WUFDN0IsTUFBTSxDQUFDLENBQUM7U0FDVDtRQUNELGlDQUFpQztLQUNsQztBQUNILENBQUM7QUFFRDs7Ozs7OztHQU9HO0FBQ0gsS0FBSyxVQUFVLHlCQUF5QixDQUFDLFVBQWtCO0lBQ3pELE1BQU0sUUFBUSxHQUFHLE1BQU0sRUFBRSxDQUFDLGdCQUFnQixDQUFDLEVBQUUsTUFBTSxFQUFFLFVBQVUsRUFBRSxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7SUFDN0UsT0FBTyxRQUFRLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEtBQUssdUJBQXVCLElBQUksR0FBRyxDQUFDLEtBQUssS0FBSyxNQUFNLENBQUMsQ0FBQztBQUNsRyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGltcG9ydC9uby1leHRyYW5lb3VzLWRlcGVuZGVuY2llc1xuaW1wb3J0IHsgUzMgfSBmcm9tICdhd3Mtc2RrJztcblxuY29uc3QgQVVUT19ERUxFVEVfT0JKRUNUU19UQUcgPSAnYXdzLWNkazphdXRvLWRlbGV0ZS1vYmplY3RzJztcblxuY29uc3QgczMgPSBuZXcgUzMoKTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQpIHtcbiAgc3dpdGNoIChldmVudC5SZXF1ZXN0VHlwZSkge1xuICAgIGNhc2UgJ0NyZWF0ZSc6XG4gICAgICByZXR1cm47XG4gICAgY2FzZSAnVXBkYXRlJzpcbiAgICAgIHJldHVybiBvblVwZGF0ZShldmVudCk7XG4gICAgY2FzZSAnRGVsZXRlJzpcbiAgICAgIHJldHVybiBvbkRlbGV0ZShldmVudC5SZXNvdXJjZVByb3BlcnRpZXM/LkJ1Y2tldE5hbWUpO1xuICB9XG59XG5cbmFzeW5jIGZ1bmN0aW9uIG9uVXBkYXRlKGV2ZW50OiBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50KSB7XG4gIGNvbnN0IHVwZGF0ZUV2ZW50ID0gZXZlbnQgYXMgQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VVcGRhdGVFdmVudDtcbiAgY29uc3Qgb2xkQnVja2V0TmFtZSA9IHVwZGF0ZUV2ZW50Lk9sZFJlc291cmNlUHJvcGVydGllcz8uQnVja2V0TmFtZTtcbiAgY29uc3QgbmV3QnVja2V0TmFtZSA9IHVwZGF0ZUV2ZW50LlJlc291cmNlUHJvcGVydGllcz8uQnVja2V0TmFtZTtcbiAgY29uc3QgYnVja2V0TmFtZUhhc0NoYW5nZWQgPSBuZXdCdWNrZXROYW1lICE9IG51bGwgJiYgb2xkQnVja2V0TmFtZSAhPSBudWxsICYmIG5ld0J1Y2tldE5hbWUgIT09IG9sZEJ1Y2tldE5hbWU7XG5cbiAgLyogSWYgdGhlIG5hbWUgb2YgdGhlIGJ1Y2tldCBoYXMgY2hhbmdlZCwgQ2xvdWRGb3JtYXRpb24gd2lsbCB0cnkgdG8gZGVsZXRlIHRoZSBidWNrZXRcbiAgICAgYW5kIGNyZWF0ZSBhIG5ldyBvbmUgd2l0aCB0aGUgbmV3IG5hbWUuIFNvIHdlIGhhdmUgdG8gZGVsZXRlIHRoZSBjb250ZW50cyBvZiB0aGVcbiAgICAgYnVja2V0IHNvIHRoYXQgdGhpcyBvcGVyYXRpb24gZG9lcyBub3QgZmFpbC4gKi9cbiAgaWYgKGJ1Y2tldE5hbWVIYXNDaGFuZ2VkKSB7XG4gICAgcmV0dXJuIG9uRGVsZXRlKG9sZEJ1Y2tldE5hbWUpO1xuICB9XG59XG5cbi8qKlxuICogUmVjdXJzaXZlbHkgZGVsZXRlIGFsbCBpdGVtcyBpbiB0aGUgYnVja2V0XG4gKlxuICogQHBhcmFtIGJ1Y2tldE5hbWUgdGhlIGJ1Y2tldCBuYW1lXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGVtcHR5QnVja2V0KGJ1Y2tldE5hbWU6IHN0cmluZykge1xuICBjb25zdCBsaXN0ZWRPYmplY3RzID0gYXdhaXQgczMubGlzdE9iamVjdFZlcnNpb25zKHsgQnVja2V0OiBidWNrZXROYW1lIH0pLnByb21pc2UoKTtcbiAgY29uc3QgY29udGVudHMgPSBbLi4ubGlzdGVkT2JqZWN0cy5WZXJzaW9ucyA/PyBbXSwgLi4ubGlzdGVkT2JqZWN0cy5EZWxldGVNYXJrZXJzID8/IFtdXTtcbiAgaWYgKGNvbnRlbnRzLmxlbmd0aCA9PT0gMCkge1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGNvbnN0IHJlY29yZHMgPSBjb250ZW50cy5tYXAoKHJlY29yZDogYW55KSA9PiAoeyBLZXk6IHJlY29yZC5LZXksIFZlcnNpb25JZDogcmVjb3JkLlZlcnNpb25JZCB9KSk7XG4gIGF3YWl0IHMzLmRlbGV0ZU9iamVjdHMoeyBCdWNrZXQ6IGJ1Y2tldE5hbWUsIERlbGV0ZTogeyBPYmplY3RzOiByZWNvcmRzIH0gfSkucHJvbWlzZSgpO1xuXG4gIGlmIChsaXN0ZWRPYmplY3RzPy5Jc1RydW5jYXRlZCkge1xuICAgIGF3YWl0IGVtcHR5QnVja2V0KGJ1Y2tldE5hbWUpO1xuICB9XG59XG5cbmFzeW5jIGZ1bmN0aW9uIG9uRGVsZXRlKGJ1Y2tldE5hbWU/OiBzdHJpbmcpIHtcbiAgaWYgKCFidWNrZXROYW1lKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdObyBCdWNrZXROYW1lIHdhcyBwcm92aWRlZC4nKTtcbiAgfVxuICBpZiAoIWF3YWl0IGlzQnVja2V0VGFnZ2VkRm9yRGVsZXRpb24oYnVja2V0TmFtZSkpIHtcbiAgICBwcm9jZXNzLnN0ZG91dC53cml0ZShgQnVja2V0IGRvZXMgbm90IGhhdmUgJyR7QVVUT19ERUxFVEVfT0JKRUNUU19UQUd9JyB0YWcsIHNraXBwaW5nIGNsZWFuaW5nLlxcbmApO1xuICAgIHJldHVybjtcbiAgfVxuICB0cnkge1xuICAgIGF3YWl0IGVtcHR5QnVja2V0KGJ1Y2tldE5hbWUpO1xuICB9IGNhdGNoIChlKSB7XG4gICAgaWYgKGUuY29kZSAhPT0gJ05vU3VjaEJ1Y2tldCcpIHtcbiAgICAgIHRocm93IGU7XG4gICAgfVxuICAgIC8vIEJ1Y2tldCBkb2Vzbid0IGV4aXN0LiBJZ25vcmluZ1xuICB9XG59XG5cbi8qKlxuICogVGhlIGJ1Y2tldCB3aWxsIG9ubHkgYmUgdGFnZ2VkIGZvciBkZWxldGlvbiBpZiBpdCdzIGJlaW5nIGRlbGV0ZWQgaW4gdGhlIHNhbWVcbiAqIGRlcGxveW1lbnQgYXMgdGhpcyBDdXN0b20gUmVzb3VyY2UuXG4gKlxuICogSWYgdGhlIEN1c3RvbSBSZXNvdXJjZSBpcyBldmVyeSBkZWxldGVkIGJlZm9yZSB0aGUgYnVja2V0LCBpdCBtdXN0IGJlIGJlY2F1c2VcbiAqIGBhdXRvRGVsZXRlT2JqZWN0c2AgaGFzIGJlZW4gc3dpdGNoZWQgdG8gZmFsc2UsIGluIHdoaWNoIGNhc2UgdGhlIHRhZyB3b3VsZCBoYXZlXG4gKiBiZWVuIHJlbW92ZWQgYmVmb3JlIHdlIGdldCB0byB0aGlzIERlbGV0ZSBldmVudC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gaXNCdWNrZXRUYWdnZWRGb3JEZWxldGlvbihidWNrZXROYW1lOiBzdHJpbmcpIHtcbiAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBzMy5nZXRCdWNrZXRUYWdnaW5nKHsgQnVja2V0OiBidWNrZXROYW1lIH0pLnByb21pc2UoKTtcbiAgcmV0dXJuIHJlc3BvbnNlLlRhZ1NldC5zb21lKHRhZyA9PiB0YWcuS2V5ID09PSBBVVRPX0RFTEVURV9PQkpFQ1RTX1RBRyAmJiB0YWcuVmFsdWUgPT09ICd0cnVlJyk7XG59Il19 \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/index.ts b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/index.ts new file mode 100644 index 0000000000000..2459d44ab1d18 --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/index.ts @@ -0,0 +1,82 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +import { S3 } from 'aws-sdk'; + +const AUTO_DELETE_OBJECTS_TAG = 'aws-cdk:auto-delete-objects'; + +const s3 = new S3(); + +export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { + switch (event.RequestType) { + case 'Create': + return; + case 'Update': + return onUpdate(event); + case 'Delete': + return onDelete(event.ResourceProperties?.BucketName); + } +} + +async function onUpdate(event: AWSLambda.CloudFormationCustomResourceEvent) { + const updateEvent = event as AWSLambda.CloudFormationCustomResourceUpdateEvent; + const oldBucketName = updateEvent.OldResourceProperties?.BucketName; + const newBucketName = updateEvent.ResourceProperties?.BucketName; + const bucketNameHasChanged = newBucketName != null && oldBucketName != null && newBucketName !== oldBucketName; + + /* If the name of the bucket has changed, CloudFormation will try to delete the bucket + and create a new one with the new name. So we have to delete the contents of the + bucket so that this operation does not fail. */ + if (bucketNameHasChanged) { + return onDelete(oldBucketName); + } +} + +/** + * Recursively delete all items in the bucket + * + * @param bucketName the bucket name + */ +async function emptyBucket(bucketName: string) { + const listedObjects = await s3.listObjectVersions({ Bucket: bucketName }).promise(); + const contents = [...listedObjects.Versions ?? [], ...listedObjects.DeleteMarkers ?? []]; + if (contents.length === 0) { + return; + } + + const records = contents.map((record: any) => ({ Key: record.Key, VersionId: record.VersionId })); + await s3.deleteObjects({ Bucket: bucketName, Delete: { Objects: records } }).promise(); + + if (listedObjects?.IsTruncated) { + await emptyBucket(bucketName); + } +} + +async function onDelete(bucketName?: string) { + if (!bucketName) { + throw new Error('No BucketName was provided.'); + } + if (!await isBucketTaggedForDeletion(bucketName)) { + process.stdout.write(`Bucket does not have '${AUTO_DELETE_OBJECTS_TAG}' tag, skipping cleaning.\n`); + return; + } + try { + await emptyBucket(bucketName); + } catch (e) { + if (e.code !== 'NoSuchBucket') { + throw e; + } + // Bucket doesn't exist. Ignoring + } +} + +/** + * The bucket will only be tagged for deletion if it's being deleted in the same + * deployment as this Custom Resource. + * + * If the Custom Resource is every deleted before the bucket, it must be because + * `autoDeleteObjects` has been switched to false, in which case the tag would have + * been removed before we get to this Delete event. + */ +async function isBucketTaggedForDeletion(bucketName: string) { + const response = await s3.getBucketTagging({ Bucket: bucketName }).promise(); + return response.TagSet.some(tag => tag.Key === AUTO_DELETE_OBJECTS_TAG && tag.Value === 'true'); +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/cdk.out b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/cdk.out new file mode 100644 index 0000000000000..8ecc185e9dbee --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/integ.json b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/integ.json new file mode 100644 index 0000000000000..240c501c9dba6 --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "21.0.0", + "testCases": { + "PreparelessPipelineTest/DefaultTest": { + "stacks": [ + "PreparelessPipelineStack" + ], + "assertionStack": "PreparelessPipelineTest/DefaultTest/DeployAssert", + "assertionStackName": "PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/manifest.json b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/manifest.json new file mode 100644 index 0000000000000..595a5fc4aa045 --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/manifest.json @@ -0,0 +1,214 @@ +{ + "version": "21.0.0", + "artifacts": { + "assembly-PreparelessPipelineStack-MyStage": { + "type": "cdk:cloud-assembly", + "properties": { + "directoryName": "assembly-PreparelessPipelineStack-MyStage", + "displayName": "PreparelessPipelineStack/MyStage" + } + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + }, + "PreparelessPipelineStack.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "PreparelessPipelineStack.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "PreparelessPipelineStack": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "PreparelessPipelineStack.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/d48998cffedf7ada707ce9f74130f229eb47e4c549355b1031589d0c4d78d9ad.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "PreparelessPipelineStack.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "PreparelessPipelineStack.assets" + ], + "metadata": { + "/PreparelessPipelineStack/SourceBucket/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "SourceBucketDDD2130A" + } + ], + "/PreparelessPipelineStack/SourceBucket/Policy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "SourceBucketPolicy703DFBF9" + } + ], + "/PreparelessPipelineStack/SourceBucket/AutoDeleteObjectsCustomResource/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "SourceBucketAutoDeleteObjectsCustomResourceC68FC040" + } + ], + "/PreparelessPipelineStack/Custom::S3AutoDeleteObjectsCustomResourceProvider/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092" + } + ], + "/PreparelessPipelineStack/Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F" + } + ], + "/PreparelessPipelineStack/Pipeline/Pipeline/ArtifactsBucket/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineArtifactsBucketAEA9A052" + } + ], + "/PreparelessPipelineStack/Pipeline/Pipeline/ArtifactsBucket/Policy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineArtifactsBucketPolicyF53CCC52" + } + ], + "/PreparelessPipelineStack/Pipeline/Pipeline/Role/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineRoleB27FAA37" + } + ], + "/PreparelessPipelineStack/Pipeline/Pipeline/Role/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineRoleDefaultPolicy7BDC1ABB" + } + ], + "/PreparelessPipelineStack/Pipeline/Pipeline/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Pipeline9850B417" + } + ], + "/PreparelessPipelineStack/Pipeline/Pipeline/Source/S3/CodePipelineActionRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineSourceS3CodePipelineActionRole83895A58" + } + ], + "/PreparelessPipelineStack/Pipeline/Pipeline/Source/S3/CodePipelineActionRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineSourceS3CodePipelineActionRoleDefaultPolicyB176A07F" + } + ], + "/PreparelessPipelineStack/Pipeline/Pipeline/Build/Synth/CdkBuildProject/Role/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineBuildSynthCdkBuildProjectRole231EEA2A" + } + ], + "/PreparelessPipelineStack/Pipeline/Pipeline/Build/Synth/CdkBuildProject/Role/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineBuildSynthCdkBuildProjectRoleDefaultPolicyFB6C941C" + } + ], + "/PreparelessPipelineStack/Pipeline/Pipeline/Build/Synth/CdkBuildProject/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineBuildSynthCdkBuildProject6BEFA8E6" + } + ], + "/PreparelessPipelineStack/Pipeline/CodeBuildActionRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineCodeBuildActionRole226DB0CB" + } + ], + "/PreparelessPipelineStack/Pipeline/CodeBuildActionRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineCodeBuildActionRoleDefaultPolicy1D62A6FE" + } + ], + "/PreparelessPipelineStack/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/PreparelessPipelineStack/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "PreparelessPipelineStack" + }, + "PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6.assets" + ], + "metadata": { + "/PreparelessPipelineTest/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/PreparelessPipelineTest/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "PreparelessPipelineTest/DefaultTest/DeployAssert" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/tree.json b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/tree.json new file mode 100644 index 0000000000000..05ba185d3d1dd --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/tree.json @@ -0,0 +1,1402 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.92" + } + }, + "PreparelessPipelineStack": { + "id": "PreparelessPipelineStack", + "path": "PreparelessPipelineStack", + "children": { + "SourceBucket": { + "id": "SourceBucket", + "path": "PreparelessPipelineStack/SourceBucket", + "children": { + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/SourceBucket/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::S3::Bucket", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "aws-cdk:auto-delete-objects", + "value": "true" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-s3.CfnBucket", + "version": "0.0.0" + } + }, + "Policy": { + "id": "Policy", + "path": "PreparelessPipelineStack/SourceBucket/Policy", + "children": { + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/SourceBucket/Policy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::S3::BucketPolicy", + "aws:cdk:cloudformation:props": { + "bucket": { + "Ref": "SourceBucketDDD2130A" + }, + "policyDocument": { + "Statement": [ + { + "Action": [ + "s3:DeleteObject*", + "s3:GetBucket*", + "s3:List*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "SourceBucketDDD2130A", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "SourceBucketDDD2130A", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-s3.CfnBucketPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-s3.BucketPolicy", + "version": "0.0.0" + } + }, + "AutoDeleteObjectsCustomResource": { + "id": "AutoDeleteObjectsCustomResource", + "path": "PreparelessPipelineStack/SourceBucket/AutoDeleteObjectsCustomResource", + "children": { + "Default": { + "id": "Default", + "path": "PreparelessPipelineStack/SourceBucket/AutoDeleteObjectsCustomResource/Default", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.CustomResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-s3.Bucket", + "version": "0.0.0" + } + }, + "Custom::S3AutoDeleteObjectsCustomResourceProvider": { + "id": "Custom::S3AutoDeleteObjectsCustomResourceProvider", + "path": "PreparelessPipelineStack/Custom::S3AutoDeleteObjectsCustomResourceProvider", + "children": { + "Staging": { + "id": "Staging", + "path": "PreparelessPipelineStack/Custom::S3AutoDeleteObjectsCustomResourceProvider/Staging", + "constructInfo": { + "fqn": "@aws-cdk/core.AssetStaging", + "version": "0.0.0" + } + }, + "Role": { + "id": "Role", + "path": "PreparelessPipelineStack/Custom::S3AutoDeleteObjectsCustomResourceProvider/Role", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + }, + "Handler": { + "id": "Handler", + "path": "PreparelessPipelineStack/Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.CustomResourceProvider", + "version": "0.0.0" + } + }, + "Pipeline": { + "id": "Pipeline", + "path": "PreparelessPipelineStack/Pipeline", + "children": { + "Pipeline": { + "id": "Pipeline", + "path": "PreparelessPipelineStack/Pipeline/Pipeline", + "children": { + "ArtifactsBucket": { + "id": "ArtifactsBucket", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/ArtifactsBucket", + "children": { + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/ArtifactsBucket/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::S3::Bucket", + "aws:cdk:cloudformation:props": { + "bucketEncryption": { + "serverSideEncryptionConfiguration": [ + { + "serverSideEncryptionByDefault": { + "sseAlgorithm": "aws:kms" + } + } + ] + }, + "publicAccessBlockConfiguration": { + "blockPublicAcls": true, + "blockPublicPolicy": true, + "ignorePublicAcls": true, + "restrictPublicBuckets": true + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-s3.CfnBucket", + "version": "0.0.0" + } + }, + "Policy": { + "id": "Policy", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/ArtifactsBucket/Policy", + "children": { + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/ArtifactsBucket/Policy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::S3::BucketPolicy", + "aws:cdk:cloudformation:props": { + "bucket": { + "Ref": "PipelineArtifactsBucketAEA9A052" + }, + "policyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":role/cdk-hnb659fds-deploy-role-", + { + "Ref": "AWS::AccountId" + }, + "-", + { + "Ref": "AWS::Region" + } + ] + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-s3.CfnBucketPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-s3.BucketPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-s3.Bucket", + "version": "0.0.0" + } + }, + "Role": { + "id": "Role", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Role", + "children": { + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Role/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codepipeline.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Role/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Role/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "s3:Abort*", + "s3:DeleteObject*", + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineCodeBuildActionRole226DB0CB", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "PipelineSourceS3CodePipelineActionRole83895A58", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":role/cdk-hnb659fds-deploy-role-", + { + "Ref": "AWS::AccountId" + }, + "-", + { + "Ref": "AWS::Region" + } + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "policyName": "PipelineRoleDefaultPolicy7BDC1ABB", + "roles": [ + { + "Ref": "PipelineRoleB27FAA37" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::CodePipeline::Pipeline", + "aws:cdk:cloudformation:props": { + "roleArn": { + "Fn::GetAtt": [ + "PipelineRoleB27FAA37", + "Arn" + ] + }, + "stages": [ + { + "name": "Source", + "actions": [ + { + "name": { + "Ref": "SourceBucketDDD2130A" + }, + "outputArtifacts": [ + { + "name": "c8506b445957b8105ede7b68ebe35e9406d642cd0c_Source" + } + ], + "actionTypeId": { + "category": "Source", + "version": "1", + "owner": "AWS", + "provider": "S3" + }, + "configuration": { + "S3Bucket": { + "Ref": "SourceBucketDDD2130A" + }, + "S3ObjectKey": "key" + }, + "runOrder": 1, + "roleArn": { + "Fn::GetAtt": [ + "PipelineSourceS3CodePipelineActionRole83895A58", + "Arn" + ] + } + } + ] + }, + { + "name": "Build", + "actions": [ + { + "name": "Synth", + "inputArtifacts": [ + { + "name": "c8506b445957b8105ede7b68ebe35e9406d642cd0c_Source" + } + ], + "outputArtifacts": [ + { + "name": "Synth_Output" + } + ], + "actionTypeId": { + "category": "Build", + "version": "1", + "owner": "AWS", + "provider": "CodeBuild" + }, + "configuration": { + "ProjectName": { + "Ref": "PipelineBuildSynthCdkBuildProject6BEFA8E6" + } + }, + "runOrder": 1, + "roleArn": { + "Fn::GetAtt": [ + "PipelineCodeBuildActionRole226DB0CB", + "Arn" + ] + } + } + ] + }, + { + "name": "MyStage", + "actions": [ + { + "name": "Deploy", + "inputArtifacts": [ + { + "name": "Synth_Output" + } + ], + "actionTypeId": { + "category": "Deploy", + "version": "1", + "owner": "AWS", + "provider": "CloudFormation" + }, + "configuration": { + "StackName": "MyStage-Stack", + "Capabilities": "CAPABILITY_NAMED_IAM,CAPABILITY_AUTO_EXPAND", + "RoleArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":role/cdk-hnb659fds-cfn-exec-role-", + { + "Ref": "AWS::AccountId" + }, + "-", + { + "Ref": "AWS::Region" + } + ] + ] + }, + "ActionMode": "CREATE_UPDATE", + "TemplatePath": "Synth_Output::assembly-PreparelessPipelineStack-MyStage/PreparelessPipelineStackMyStageStack3DC192E7.template.json" + }, + "runOrder": 1, + "roleArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":role/cdk-hnb659fds-deploy-role-", + { + "Ref": "AWS::AccountId" + }, + "-", + { + "Ref": "AWS::Region" + } + ] + ] + } + } + ] + } + ], + "artifactStore": { + "type": "S3", + "location": { + "Ref": "PipelineArtifactsBucketAEA9A052" + } + }, + "restartExecutionOnUpdate": true + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-codepipeline.CfnPipeline", + "version": "0.0.0" + } + }, + "Source": { + "id": "Source", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Source", + "children": { + "S3": { + "id": "S3", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Source/S3", + "children": { + "CodePipelineActionRole": { + "id": "CodePipelineActionRole", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Source/S3/CodePipelineActionRole", + "children": { + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Source/S3/CodePipelineActionRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Source/S3/CodePipelineActionRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Source/S3/CodePipelineActionRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "SourceBucketDDD2130A", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "SourceBucketDDD2130A", + "Arn" + ] + }, + "/key" + ] + ] + } + ] + }, + { + "Action": [ + "s3:Abort*", + "s3:DeleteObject*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "policyName": "PipelineSourceS3CodePipelineActionRoleDefaultPolicyB176A07F", + "roles": [ + { + "Ref": "PipelineSourceS3CodePipelineActionRole83895A58" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Role", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.92" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.92" + } + }, + "Build": { + "id": "Build", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Build", + "children": { + "Synth": { + "id": "Synth", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Build/Synth", + "children": { + "CdkBuildProject": { + "id": "CdkBuildProject", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Build/Synth/CdkBuildProject", + "children": { + "Role": { + "id": "Role", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Build/Synth/CdkBuildProject/Role", + "children": { + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Build/Synth/CdkBuildProject/Role/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codebuild.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Build/Synth/CdkBuildProject/Role/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Build/Synth/CdkBuildProject/Role/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/codebuild/", + { + "Ref": "PipelineBuildSynthCdkBuildProject6BEFA8E6" + }, + ":*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/codebuild/", + { + "Ref": "PipelineBuildSynthCdkBuildProject6BEFA8E6" + } + ] + ] + } + ] + }, + { + "Action": [ + "codebuild:BatchPutCodeCoverages", + "codebuild:BatchPutTestCases", + "codebuild:CreateReport", + "codebuild:CreateReportGroup", + "codebuild:UpdateReport" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":codebuild:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":report-group/", + { + "Ref": "PipelineBuildSynthCdkBuildProject6BEFA8E6" + }, + "-*" + ] + ] + } + }, + { + "Action": [ + "s3:Abort*", + "s3:DeleteObject*", + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "policyName": "PipelineBuildSynthCdkBuildProjectRoleDefaultPolicyFB6C941C", + "roles": [ + { + "Ref": "PipelineBuildSynthCdkBuildProjectRole231EEA2A" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Build/Synth/CdkBuildProject/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::CodeBuild::Project", + "aws:cdk:cloudformation:props": { + "artifacts": { + "type": "CODEPIPELINE" + }, + "environment": { + "type": "LINUX_CONTAINER", + "image": "aws/codebuild/standard:5.0", + "imagePullCredentialsType": "CODEBUILD", + "privilegedMode": false, + "computeType": "BUILD_GENERAL1_SMALL" + }, + "serviceRole": { + "Fn::GetAtt": [ + "PipelineBuildSynthCdkBuildProjectRole231EEA2A", + "Arn" + ] + }, + "source": { + "type": "CODEPIPELINE", + "buildSpec": "{\n \"version\": \"0.2\",\n \"phases\": {\n \"build\": {\n \"commands\": [\n \"mkdir cdk.out\",\n \"touch cdk.out/dummy\"\n ]\n }\n },\n \"artifacts\": {\n \"base-directory\": \"cdk.out\",\n \"files\": \"**/*\"\n }\n}" + }, + "cache": { + "type": "NO_CACHE" + }, + "description": "Pipeline step PreparelessPipelineStack/Pipeline/Build/Synth", + "encryptionKey": "alias/aws/s3" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-codebuild.CfnProject", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-codebuild.PipelineProject", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.92" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.92" + } + }, + "MyStage": { + "id": "MyStage", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/MyStage", + "children": { + "Deploy": { + "id": "Deploy", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/MyStage/Deploy", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.92" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.92" + } + }, + "MutableRolearn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}": { + "id": "MutableRolearn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/MutableRolearn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "arn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}": { + "id": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/arn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "children": { + "8389e75f-0810-4838-bf64-d6f85a95cf83": { + "id": "8389e75f-0810-4838-bf64-d6f85a95cf83", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/arn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}/8389e75f-0810-4838-bf64-d6f85a95cf83", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.92" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "MutableRolearn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}": { + "id": "MutableRolearn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/MutableRolearn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "arn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}": { + "id": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/arn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-codepipeline.Pipeline", + "version": "0.0.0" + } + }, + "CodeBuildActionRole": { + "id": "CodeBuildActionRole", + "path": "PreparelessPipelineStack/Pipeline/CodeBuildActionRole", + "children": { + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/Pipeline/CodeBuildActionRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Condition": { + "Bool": { + "aws:ViaAWSService": "codepipeline.amazonaws.com" + } + }, + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "PreparelessPipelineStack/Pipeline/CodeBuildActionRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/Pipeline/CodeBuildActionRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "codebuild:BatchGetBuilds", + "codebuild:StartBuild", + "codebuild:StopBuild" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelineBuildSynthCdkBuildProject6BEFA8E6", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "PipelineCodeBuildActionRoleDefaultPolicy1D62A6FE", + "roles": [ + { + "Ref": "PipelineCodeBuildActionRole226DB0CB" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Role", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/pipelines.CodePipeline", + "version": "0.0.0" + } + }, + "MyStage": { + "id": "MyStage", + "path": "PreparelessPipelineStack/MyStage", + "children": { + "Stack": { + "id": "Stack", + "path": "PreparelessPipelineStack/MyStage/Stack", + "children": { + "MyApp": { + "id": "MyApp", + "path": "PreparelessPipelineStack/MyStage/Stack/MyApp", + "children": { + "Stack": { + "id": "Stack", + "path": "PreparelessPipelineStack/MyStage/Stack/MyApp/Stack", + "children": { + "Bucket": { + "id": "Bucket", + "path": "PreparelessPipelineStack/MyStage/Stack/MyApp/Stack/Bucket", + "children": { + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/MyStage/Stack/MyApp/Stack/Bucket/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::S3::Bucket", + "aws:cdk:cloudformation:props": {} + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-s3.CfnBucket", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-s3.Bucket", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "PreparelessPipelineStack/MyStage/Stack/MyApp/Stack/BootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "PreparelessPipelineStack/MyStage/Stack/MyApp/Stack/CheckBootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stage", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "PreparelessPipelineStack/MyStage/Stack/BootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "PreparelessPipelineStack/MyStage/Stack/CheckBootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stage", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "PreparelessPipelineTest": { + "id": "PreparelessPipelineTest", + "path": "PreparelessPipelineTest", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "PreparelessPipelineTest/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "PreparelessPipelineTest/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.92" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "PreparelessPipelineTest/DefaultTest/DeployAssert", + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTest", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/testhelpers/test-app.ts b/packages/@aws-cdk/pipelines/test/testhelpers/test-app.ts index 24e29b2625929..fca2cd26f04d4 100644 --- a/packages/@aws-cdk/pipelines/test/testhelpers/test-app.ts +++ b/packages/@aws-cdk/pipelines/test/testhelpers/test-app.ts @@ -223,4 +223,10 @@ export class PlainStackApp extends Stage { } } - +export class MultiStackApp extends Stage { + constructor(scope: Construct, id: string, props?: StageProps) { + super(scope, id, props); + new BucketStack(this, 'Stack1'); + new BucketStack(this, 'Stack2'); + } +} From 6128e3908f4f6b6a1db66ebf7f77b6c966d1f9e7 Mon Sep 17 00:00:00 2001 From: jiaoshen110 Date: Tue, 27 Sep 2022 01:26:27 +0930 Subject: [PATCH 07/24] fix(aws-elasticloadbalancingv2): Validation for interval and timeout of application-target-group (#22225) closes: https://github.com/aws/aws-cdk/issues/22155 Update the validation for interval and timeout of health check on the application target group ---- ### All Submissions: * [X] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../lib/alb/application-target-group.ts | 23 +++++++++---------- .../test/alb/target-group.test.ts | 21 +++++++++++++++++ 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-target-group.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-target-group.ts index 5f658168107bd..4599f289d892a 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-target-group.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-target-group.ts @@ -391,20 +391,19 @@ export class ApplicationTargetGroup extends TargetGroupBase implements IApplicat ret.push('At least one of \'port\' or \'protocol\' is required for a non-Lambda TargetGroup'); } - if (this.healthCheck && this.healthCheck.protocol) { - - if (ALB_HEALTH_CHECK_PROTOCOLS.includes(this.healthCheck.protocol)) { - if (this.healthCheck.interval && this.healthCheck.timeout && - this.healthCheck.interval.toMilliseconds() <= this.healthCheck.timeout.toMilliseconds()) { - ret.push(`Healthcheck interval ${this.healthCheck.interval.toHumanString()} must be greater than the timeout ${this.healthCheck.timeout.toHumanString()}`); - } + if (this.healthCheck) { + if (this.healthCheck.interval && this.healthCheck.timeout && + this.healthCheck.interval.toMilliseconds() <= this.healthCheck.timeout.toMilliseconds()) { + ret.push(`Healthcheck interval ${this.healthCheck.interval.toHumanString()} must be greater than the timeout ${this.healthCheck.timeout.toHumanString()}`); } - if (!ALB_HEALTH_CHECK_PROTOCOLS.includes(this.healthCheck.protocol)) { - ret.push([ - `Health check protocol '${this.healthCheck.protocol}' is not supported. `, - `Must be one of [${ALB_HEALTH_CHECK_PROTOCOLS.join(', ')}]`, - ].join('')); + if (this.healthCheck.protocol) { + if (!ALB_HEALTH_CHECK_PROTOCOLS.includes(this.healthCheck.protocol)) { + ret.push([ + `Health check protocol '${this.healthCheck.protocol}' is not supported. `, + `Must be one of [${ALB_HEALTH_CHECK_PROTOCOLS.join(', ')}]`, + ].join('')); + } } } diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/target-group.test.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/target-group.test.ts index a8305cd2a34f2..c11ce5a524a7f 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/target-group.test.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/target-group.test.ts @@ -528,6 +528,27 @@ describe('tests', () => { }).toThrow('Healthcheck interval 1 minute must be greater than the timeout 2 minutes'); }); + test('Throws validation error, when `configureHealthCheck()`protocol is undefined and `interval` is smaller than `timeout`', () => { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'Stack'); + const vpc = new ec2.Vpc(stack, 'VPC', {}); + const tg = new elbv2.ApplicationTargetGroup(stack, 'TargetGroup', { + vpc, + }); + + // WHEN + tg.configureHealthCheck({ + interval: cdk.Duration.seconds(60), + timeout: cdk.Duration.seconds(120), + }); + + // THEN + expect(() => { + app.synth(); + }).toThrow('Healthcheck interval 1 minute must be greater than the timeout 2 minute'); + }); + test('imported targetGroup has targetGroupName', () => { // GIVEN const app = new cdk.App(); From c070acea1b12ec4f73c7d2087c5408d7e38a90a3 Mon Sep 17 00:00:00 2001 From: joshlartz Date: Mon, 26 Sep 2022 11:39:22 -0500 Subject: [PATCH 08/24] fix(rds): changing engine versions would fail to update on DBInstances that were part of a DBCluster (#22185) Engine version should not be set on instances that are part of a cluster. The cluster is responsible for this setting and throws an API error when an update is attempted on them. closes #21758 #22180 ---- ### All Submissions: * [X] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [x] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *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-rds/lib/cluster.ts | 1 - .../aws-cdk-rds-s3-mysql-8-integ.assets.json | 6 +- ...aws-cdk-rds-s3-mysql-8-integ.template.json | 3 +- .../cluster-s3.mysql-8.integ.snapshot/cdk.out | 2 +- .../integ.json | 2 +- .../manifest.json | 4 +- .../tree.json | 33 ++++--- .../cdk-integ-cluster-snapshot.assets.json | 6 +- .../cdk-integ-cluster-snapshot.template.json | 12 +-- .../cluster-snapshot.integ.snapshot/cdk.out | 2 +- .../integ.json | 2 +- .../manifest.json | 4 +- .../cluster-snapshot.integ.snapshot/tree.json | 86 +++++++++---------- .../@aws-cdk/aws-rds/test/cluster.test.ts | 16 ++++ 14 files changed, 92 insertions(+), 87 deletions(-) diff --git a/packages/@aws-cdk/aws-rds/lib/cluster.ts b/packages/@aws-cdk/aws-rds/lib/cluster.ts index 3eb555171b170..de35ba3df6f5f 100644 --- a/packages/@aws-cdk/aws-rds/lib/cluster.ts +++ b/packages/@aws-cdk/aws-rds/lib/cluster.ts @@ -890,7 +890,6 @@ function createInstances(cluster: DatabaseClusterNew, props: DatabaseClusterBase const instance = new CfnDBInstance(cluster, `Instance${instanceIndex}`, { // Link to cluster engine: props.engine.engineType, - engineVersion: props.engine.engineVersion?.fullVersion, dbClusterIdentifier: cluster.clusterIdentifier, dbInstanceIdentifier: instanceIdentifier, // Instance properties diff --git a/packages/@aws-cdk/aws-rds/test/cluster-s3.mysql-8.integ.snapshot/aws-cdk-rds-s3-mysql-8-integ.assets.json b/packages/@aws-cdk/aws-rds/test/cluster-s3.mysql-8.integ.snapshot/aws-cdk-rds-s3-mysql-8-integ.assets.json index f4e331845bafa..430b13a84ce1f 100644 --- a/packages/@aws-cdk/aws-rds/test/cluster-s3.mysql-8.integ.snapshot/aws-cdk-rds-s3-mysql-8-integ.assets.json +++ b/packages/@aws-cdk/aws-rds/test/cluster-s3.mysql-8.integ.snapshot/aws-cdk-rds-s3-mysql-8-integ.assets.json @@ -1,7 +1,7 @@ { - "version": "20.0.0", + "version": "21.0.0", "files": { - "69d4b966c56ecebfa2378a3bf4ad6e2abe3ca2a900162197f7658ec8a258f509": { + "25234cf4a3c5d1399adfab4811db12cbab88f9459af99963277edcbbd40e815a": { "source": { "path": "aws-cdk-rds-s3-mysql-8-integ.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "69d4b966c56ecebfa2378a3bf4ad6e2abe3ca2a900162197f7658ec8a258f509.json", + "objectKey": "25234cf4a3c5d1399adfab4811db12cbab88f9459af99963277edcbbd40e815a.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk/aws-rds/test/cluster-s3.mysql-8.integ.snapshot/aws-cdk-rds-s3-mysql-8-integ.template.json b/packages/@aws-cdk/aws-rds/test/cluster-s3.mysql-8.integ.snapshot/aws-cdk-rds-s3-mysql-8-integ.template.json index b11711610c53f..2ac33a1ea212d 100644 --- a/packages/@aws-cdk/aws-rds/test/cluster-s3.mysql-8.integ.snapshot/aws-cdk-rds-s3-mysql-8-integ.template.json +++ b/packages/@aws-cdk/aws-rds/test/cluster-s3.mysql-8.integ.snapshot/aws-cdk-rds-s3-mysql-8-integ.template.json @@ -557,8 +557,7 @@ "DBSubnetGroupName": { "Ref": "DatabaseSubnets56F17B9A" }, - "Engine": "aurora-mysql", - "EngineVersion": "8.0.mysql_aurora.3.01.0" + "Engine": "aurora-mysql" }, "DependsOn": [ "VPCPrivateSubnet1DefaultRouteAE1D6490", diff --git a/packages/@aws-cdk/aws-rds/test/cluster-s3.mysql-8.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-rds/test/cluster-s3.mysql-8.integ.snapshot/cdk.out index 588d7b269d34f..8ecc185e9dbee 100644 --- a/packages/@aws-cdk/aws-rds/test/cluster-s3.mysql-8.integ.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-rds/test/cluster-s3.mysql-8.integ.snapshot/cdk.out @@ -1 +1 @@ -{"version":"20.0.0"} \ No newline at end of file +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-rds/test/cluster-s3.mysql-8.integ.snapshot/integ.json b/packages/@aws-cdk/aws-rds/test/cluster-s3.mysql-8.integ.snapshot/integ.json index a568e6fc706fa..8ffe8f32ed74b 100644 --- a/packages/@aws-cdk/aws-rds/test/cluster-s3.mysql-8.integ.snapshot/integ.json +++ b/packages/@aws-cdk/aws-rds/test/cluster-s3.mysql-8.integ.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "20.0.0", + "version": "21.0.0", "testCases": { "integ.cluster-s3.mysql-8": { "stacks": [ diff --git a/packages/@aws-cdk/aws-rds/test/cluster-s3.mysql-8.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-rds/test/cluster-s3.mysql-8.integ.snapshot/manifest.json index 3b8e9d9e3df9e..434c9f37b0cc9 100644 --- a/packages/@aws-cdk/aws-rds/test/cluster-s3.mysql-8.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-rds/test/cluster-s3.mysql-8.integ.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "20.0.0", + "version": "21.0.0", "artifacts": { "Tree": { "type": "cdk:tree", @@ -23,7 +23,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/69d4b966c56ecebfa2378a3bf4ad6e2abe3ca2a900162197f7658ec8a258f509.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/25234cf4a3c5d1399adfab4811db12cbab88f9459af99963277edcbbd40e815a.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ diff --git a/packages/@aws-cdk/aws-rds/test/cluster-s3.mysql-8.integ.snapshot/tree.json b/packages/@aws-cdk/aws-rds/test/cluster-s3.mysql-8.integ.snapshot/tree.json index 454718d69084e..1b78db0ffdca6 100644 --- a/packages/@aws-cdk/aws-rds/test/cluster-s3.mysql-8.integ.snapshot/tree.json +++ b/packages/@aws-cdk/aws-rds/test/cluster-s3.mysql-8.integ.snapshot/tree.json @@ -9,7 +9,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.85" + "version": "10.1.102" } }, "aws-cdk-rds-s3-mysql-8-integ": { @@ -91,8 +91,8 @@ "id": "Acl", "path": "aws-cdk-rds-s3-mysql-8-integ/VPC/PublicSubnet1/Acl", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" } }, "RouteTable": { @@ -258,8 +258,8 @@ "id": "Acl", "path": "aws-cdk-rds-s3-mysql-8-integ/VPC/PublicSubnet2/Acl", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" } }, "RouteTable": { @@ -425,8 +425,8 @@ "id": "Acl", "path": "aws-cdk-rds-s3-mysql-8-integ/VPC/PrivateSubnet1/Acl", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" } }, "RouteTable": { @@ -544,8 +544,8 @@ "id": "Acl", "path": "aws-cdk-rds-s3-mysql-8-integ/VPC/PrivateSubnet2/Acl", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" } }, "RouteTable": { @@ -861,8 +861,8 @@ "id": "AuroraMySqlDatabaseClusterEngineDefaultParameterGroup", "path": "aws-cdk-rds-s3-mysql-8-integ/Database/AuroraMySqlDatabaseClusterEngineDefaultParameterGroup", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" } }, "ClusterParameterGroup": { @@ -953,8 +953,7 @@ "dbSubnetGroupName": { "Ref": "DatabaseSubnets56F17B9A" }, - "engine": "aurora-mysql", - "engineVersion": "8.0.mysql_aurora.3.01.0" + "engine": "aurora-mysql" } }, "constructInfo": { @@ -970,14 +969,14 @@ } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/cdk-integ-cluster-snapshot.assets.json b/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/cdk-integ-cluster-snapshot.assets.json index b8c141d51c1b5..bff378fc65a8d 100644 --- a/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/cdk-integ-cluster-snapshot.assets.json +++ b/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/cdk-integ-cluster-snapshot.assets.json @@ -1,5 +1,5 @@ { - "version": "20.0.0", + "version": "21.0.0", "files": { "2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aadd": { "source": { @@ -27,7 +27,7 @@ } } }, - "8ec192f0414bf86ff0d9b73ae3fe511c7d1a75d03f9d894cafa1e3b1d0d33f39": { + "d32f18fd62a56b01413ffc4d63a52dcec293772c9f178d8b459ba255481f9326": { "source": { "path": "cdk-integ-cluster-snapshot.template.json", "packaging": "file" @@ -35,7 +35,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "8ec192f0414bf86ff0d9b73ae3fe511c7d1a75d03f9d894cafa1e3b1d0d33f39.json", + "objectKey": "d32f18fd62a56b01413ffc4d63a52dcec293772c9f178d8b459ba255481f9326.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/cdk-integ-cluster-snapshot.template.json b/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/cdk-integ-cluster-snapshot.template.json index da2e9266169ef..366c5f002461c 100644 --- a/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/cdk-integ-cluster-snapshot.template.json +++ b/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/cdk-integ-cluster-snapshot.template.json @@ -478,8 +478,7 @@ "DBSubnetGroupName": { "Ref": "ClusterSubnetsDCFA5CB7" }, - "Engine": "aurora-mysql", - "EngineVersion": "5.7.mysql_aurora.2.10.2" + "Engine": "aurora-mysql" }, "DependsOn": [ "VpcPrivateSubnet1DefaultRouteBE02A9ED", @@ -500,8 +499,7 @@ "DBSubnetGroupName": { "Ref": "ClusterSubnetsDCFA5CB7" }, - "Engine": "aurora-mysql", - "EngineVersion": "5.7.mysql_aurora.2.10.2" + "Engine": "aurora-mysql" }, "DependsOn": [ "VpcPrivateSubnet1DefaultRouteBE02A9ED", @@ -1542,8 +1540,7 @@ "DBSubnetGroupName": { "Ref": "FromSnapshotSubnets9ED4B8EE" }, - "Engine": "aurora-mysql", - "EngineVersion": "5.7.mysql_aurora.2.10.2" + "Engine": "aurora-mysql" }, "DependsOn": [ "VpcPrivateSubnet1DefaultRouteBE02A9ED", @@ -1564,8 +1561,7 @@ "DBSubnetGroupName": { "Ref": "FromSnapshotSubnets9ED4B8EE" }, - "Engine": "aurora-mysql", - "EngineVersion": "5.7.mysql_aurora.2.10.2" + "Engine": "aurora-mysql" }, "DependsOn": [ "VpcPrivateSubnet1DefaultRouteBE02A9ED", diff --git a/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/cdk.out index 588d7b269d34f..8ecc185e9dbee 100644 --- a/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/cdk.out @@ -1 +1 @@ -{"version":"20.0.0"} \ No newline at end of file +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/integ.json b/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/integ.json index 2e575b90eb401..329077c22006c 100644 --- a/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/integ.json +++ b/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "20.0.0", + "version": "21.0.0", "testCases": { "integ.cluster-snapshot": { "stacks": [ diff --git a/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/manifest.json index cc4f2d49839ad..40957601b77fe 100644 --- a/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "20.0.0", + "version": "21.0.0", "artifacts": { "Tree": { "type": "cdk:tree", @@ -23,7 +23,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/8ec192f0414bf86ff0d9b73ae3fe511c7d1a75d03f9d894cafa1e3b1d0d33f39.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/d32f18fd62a56b01413ffc4d63a52dcec293772c9f178d8b459ba255481f9326.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ diff --git a/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/tree.json b/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/tree.json index 1104de32f857b..42da845702130 100644 --- a/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/tree.json +++ b/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/tree.json @@ -9,7 +9,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.85" + "version": "10.1.102" } }, "cdk-integ-cluster-snapshot": { @@ -91,8 +91,8 @@ "id": "Acl", "path": "cdk-integ-cluster-snapshot/Vpc/PublicSubnet1/Acl", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" } }, "RouteTable": { @@ -258,8 +258,8 @@ "id": "Acl", "path": "cdk-integ-cluster-snapshot/Vpc/PublicSubnet2/Acl", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" } }, "RouteTable": { @@ -377,8 +377,8 @@ "id": "Acl", "path": "cdk-integ-cluster-snapshot/Vpc/PrivateSubnet1/Acl", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" } }, "RouteTable": { @@ -496,8 +496,8 @@ "id": "Acl", "path": "cdk-integ-cluster-snapshot/Vpc/PrivateSubnet2/Acl", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" } }, "RouteTable": { @@ -685,8 +685,8 @@ "id": "AuroraMySqlDatabaseClusterEngineDefaultParameterGroup", "path": "cdk-integ-cluster-snapshot/Cluster/AuroraMySqlDatabaseClusterEngineDefaultParameterGroup", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" } }, "Secret": { @@ -824,8 +824,7 @@ "dbSubnetGroupName": { "Ref": "ClusterSubnetsDCFA5CB7" }, - "engine": "aurora-mysql", - "engineVersion": "5.7.mysql_aurora.2.10.2" + "engine": "aurora-mysql" } }, "constructInfo": { @@ -846,8 +845,7 @@ "dbSubnetGroupName": { "Ref": "ClusterSubnetsDCFA5CB7" }, - "engine": "aurora-mysql", - "engineVersion": "5.7.mysql_aurora.2.10.2" + "engine": "aurora-mysql" } }, "constructInfo": { @@ -1012,8 +1010,8 @@ "id": "Stage", "path": "cdk-integ-cluster-snapshot/Snapshoter/OnEventHandler/Code/Stage", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.AssetStaging", + "version": "0.0.0" } }, "AssetBucket": { @@ -1384,8 +1382,8 @@ "id": "Stage", "path": "cdk-integ-cluster-snapshot/Snapshoter/SnapshotProvider/framework-onEvent/Code/Stage", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.AssetStaging", + "version": "0.0.0" } }, "AssetBucket": { @@ -1597,8 +1595,8 @@ "id": "Stage", "path": "cdk-integ-cluster-snapshot/Snapshoter/SnapshotProvider/framework-isComplete/Code/Stage", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.AssetStaging", + "version": "0.0.0" } }, "AssetBucket": { @@ -1807,8 +1805,8 @@ "id": "Stage", "path": "cdk-integ-cluster-snapshot/Snapshoter/SnapshotProvider/framework-onTimeout/Code/Stage", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.AssetStaging", + "version": "0.0.0" } }, "AssetBucket": { @@ -2007,14 +2005,14 @@ "id": "Resource", "path": "cdk-integ-cluster-snapshot/Snapshoter/SnapshotProvider/waiter-state-machine/Resource", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" } } }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.85" + "version": "10.1.102" } } }, @@ -2031,20 +2029,20 @@ "id": "Default", "path": "cdk-integ-cluster-snapshot/Snapshoter/Snapshot/Default", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.CustomResource", + "version": "0.0.0" } } }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.85" + "version": "10.1.102" } }, "FromSnapshot": { @@ -2160,8 +2158,8 @@ "id": "AuroraMySqlDatabaseClusterEngineDefaultParameterGroup", "path": "cdk-integ-cluster-snapshot/FromSnapshot/AuroraMySqlDatabaseClusterEngineDefaultParameterGroup", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" } }, "Secret": { @@ -2452,8 +2450,7 @@ "dbSubnetGroupName": { "Ref": "FromSnapshotSubnets9ED4B8EE" }, - "engine": "aurora-mysql", - "engineVersion": "5.7.mysql_aurora.2.10.2" + "engine": "aurora-mysql" } }, "constructInfo": { @@ -2474,8 +2471,7 @@ "dbSubnetGroupName": { "Ref": "FromSnapshotSubnets9ED4B8EE" }, - "engine": "aurora-mysql", - "engineVersion": "5.7.mysql_aurora.2.10.2" + "engine": "aurora-mysql" } }, "constructInfo": { @@ -2525,8 +2521,8 @@ "id": "SARMapping", "path": "cdk-integ-cluster-snapshot/FromSnapshot/RotationSingleUser/SARMapping", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.CfnMapping", + "version": "0.0.0" } }, "Resource": { @@ -2625,20 +2621,20 @@ "id": "Service-principalMap", "path": "cdk-integ-cluster-snapshot/Service-principalMap", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.CfnMapping", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-rds/test/cluster.test.ts b/packages/@aws-cdk/aws-rds/test/cluster.test.ts index 35bd5af905f5c..397a3828b200a 100644 --- a/packages/@aws-cdk/aws-rds/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-rds/test/cluster.test.ts @@ -2599,6 +2599,22 @@ describe('cluster', () => { BacktrackWindow: 24 * 60 * 60, }); }); + + test('DB instances should not have engine version set when part of a cluster', () => { + // GIVEN + const stack = testStack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + + // WHEN + new DatabaseCluster(stack, 'Database', { + engine: DatabaseClusterEngine.auroraPostgres({ version: AuroraPostgresEngineVersion.VER_14_3 }), + instanceProps: { vpc }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { + EngineVersion: Match.absent(), + }); + }); }); test.each([ From 953d6841fa3ed43258d0454e245cebcab6323e0d Mon Sep 17 00:00:00 2001 From: AWS CDK Automation <43080478+aws-cdk-automation@users.noreply.github.com> Date: Mon, 26 Sep 2022 10:15:49 -0700 Subject: [PATCH 09/24] feat(cfnspec): cloudformation spec v89.0.0 (#22232) --- packages/@aws-cdk/cfnspec/CHANGELOG.md | 26 +++++++++++ .../000_cfn/000_official/000_AWS_Glue.json | 43 ++++++++++++++++++- .../000_official/000_AWS_Redshift.json | 7 ++- 3 files changed, 73 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/cfnspec/CHANGELOG.md b/packages/@aws-cdk/cfnspec/CHANGELOG.md index 9e9427136047f..45cff09aed9cd 100644 --- a/packages/@aws-cdk/cfnspec/CHANGELOG.md +++ b/packages/@aws-cdk/cfnspec/CHANGELOG.md @@ -3,6 +3,32 @@ ## New Resource Types +## Attribute Changes + +* AWS::Redshift::ClusterSubnetGroup ClusterSubnetGroupName (__added__) + +## Property Changes + +* AWS::Glue::Job ExecutionClass (__added__) +* AWS::Glue::Job NonOverridableArguments (__added__) +* AWS::Glue::Trigger EventBatchingCondition (__added__) +* AWS::Glue::Trigger Type.UpdateType (__changed__) + * Old: Mutable + * New: Immutable +* AWS::Glue::Workflow MaxConcurrentRuns (__added__) +* AWS::Redshift::ClusterSubnetGroup SubnetIds.DuplicatesAllowed (__deleted__) +* AWS::Redshift::ClusterSubnetGroup Tags.DuplicatesAllowed (__deleted__) + +## Property Type Changes + +* AWS::Glue::Trigger.EventBatchingCondition (__added__) + + +# CloudFormation Resource Specification v89.0.0 + +## New Resource Types + + ## Attribute Changes * AWS::EC2::VPNConnection Documentation (__changed__) diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Glue.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Glue.json index a1cfe89917973..6a0dc8458ba5d 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Glue.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Glue.json @@ -1465,6 +1465,23 @@ } } }, + "AWS::Glue::Trigger.EventBatchingCondition": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-trigger-eventbatchingcondition.html", + "Properties": { + "BatchSize": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-trigger-eventbatchingcondition.html#cfn-glue-trigger-eventbatchingcondition-batchsize", + "PrimitiveType": "Integer", + "Required": true, + "UpdateType": "Mutable" + }, + "BatchWindow": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-trigger-eventbatchingcondition.html#cfn-glue-trigger-eventbatchingcondition-batchwindow", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + } + } + }, "AWS::Glue::Trigger.NotificationProperty": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-trigger-notificationproperty.html", "Properties": { @@ -1790,6 +1807,12 @@ "Required": false, "UpdateType": "Mutable" }, + "ExecutionClass": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-glue-job.html#cfn-glue-job-executionclass", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, "ExecutionProperty": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-glue-job.html#cfn-glue-job-executionproperty", "Required": false, @@ -1826,6 +1849,12 @@ "Required": false, "UpdateType": "Immutable" }, + "NonOverridableArguments": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-glue-job.html#cfn-glue-job-nonoverridablearguments", + "PrimitiveType": "Json", + "Required": false, + "UpdateType": "Mutable" + }, "NotificationProperty": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-glue-job.html#cfn-glue-job-notificationproperty", "Required": false, @@ -2174,6 +2203,12 @@ "Required": false, "UpdateType": "Mutable" }, + "EventBatchingCondition": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-glue-trigger.html#cfn-glue-trigger-eventbatchingcondition", + "Required": false, + "Type": "EventBatchingCondition", + "UpdateType": "Mutable" + }, "Name": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-glue-trigger.html#cfn-glue-trigger-name", "PrimitiveType": "String", @@ -2208,7 +2243,7 @@ "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-glue-trigger.html#cfn-glue-trigger-type", "PrimitiveType": "String", "Required": true, - "UpdateType": "Mutable" + "UpdateType": "Immutable" }, "WorkflowName": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-glue-trigger.html#cfn-glue-trigger-workflowname", @@ -2233,6 +2268,12 @@ "Required": false, "UpdateType": "Mutable" }, + "MaxConcurrentRuns": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-glue-workflow.html#cfn-glue-workflow-maxconcurrentruns", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, "Name": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-glue-workflow.html#cfn-glue-workflow-name", "PrimitiveType": "String", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Redshift.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Redshift.json index 60ab9bfb6744c..b23081964e3ec 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Redshift.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Redshift.json @@ -470,6 +470,11 @@ } }, "AWS::Redshift::ClusterSubnetGroup": { + "Attributes": { + "ClusterSubnetGroupName": { + "PrimitiveType": "String" + } + }, "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-redshift-clustersubnetgroup.html", "Properties": { "Description": { @@ -480,7 +485,6 @@ }, "SubnetIds": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-redshift-clustersubnetgroup.html#cfn-redshift-clustersubnetgroup-subnetids", - "DuplicatesAllowed": true, "PrimitiveItemType": "String", "Required": true, "Type": "List", @@ -488,7 +492,6 @@ }, "Tags": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-redshift-clustersubnetgroup.html#cfn-redshift-clustersubnetgroup-tags", - "DuplicatesAllowed": true, "ItemType": "Tag", "Required": false, "Type": "List", From d9f0e809d583d23cb83b4e2855574675a669c33f Mon Sep 17 00:00:00 2001 From: Marcio Cruz de Almeida <67694075+marciocadev@users.noreply.github.com> Date: Mon, 26 Sep 2022 14:56:53 -0300 Subject: [PATCH 10/24] feat(cloudwatch): add gauge widget (#22213) Description: GraphWidget doesn't have the option to create gauge graphs, and adding gauge to `GraphWidgetView` doesn't solve this problem because gauge graphs require left Y axis settings with min and max value and in `GraphView` this is optional Solution: I created a custom `GaugeWidget` to solve this issue Closes #22136 ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *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-cloudwatch/README.md | 18 +++ packages/@aws-cdk/aws-cloudwatch/lib/graph.ts | 129 ++++++++++++++++++ ...efaultTestDeployAssertF43E2A2D.assets.json | 19 +++ ...aultTestDeployAssertF43E2A2D.template.json | 36 +++++ .../test/gauge-alarm.integ.snapshot/cdk.out | 1 + .../gauge-alarm.assets.json | 19 +++ .../gauge-alarm.template.json | 66 +++++++++ .../gauge-alarm.integ.snapshot/integ.json | 12 ++ .../gauge-alarm.integ.snapshot/manifest.json | 117 ++++++++++++++++ .../test/gauge-alarm.integ.snapshot/tree.json | 118 ++++++++++++++++ .../aws-cloudwatch/test/graphs.test.ts | 30 +++- .../aws-cloudwatch/test/integ.gauge-alarm.ts | 35 +++++ 12 files changed, 599 insertions(+), 1 deletion(-) create mode 100644 packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/GaugeAlarmIntegrationTestDefaultTestDeployAssertF43E2A2D.assets.json create mode 100644 packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/GaugeAlarmIntegrationTestDefaultTestDeployAssertF43E2A2D.template.json create mode 100644 packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/gauge-alarm.assets.json create mode 100644 packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/gauge-alarm.template.json create mode 100644 packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/integ.json create mode 100644 packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/manifest.json create mode 100644 packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/tree.json create mode 100644 packages/@aws-cdk/aws-cloudwatch/test/integ.gauge-alarm.ts diff --git a/packages/@aws-cdk/aws-cloudwatch/README.md b/packages/@aws-cdk/aws-cloudwatch/README.md index e92e0f09356c7..1f87ad8b169a1 100644 --- a/packages/@aws-cdk/aws-cloudwatch/README.md +++ b/packages/@aws-cdk/aws-cloudwatch/README.md @@ -399,6 +399,24 @@ dashboard.addWidgets(new cloudwatch.GraphWidget({ })); ``` +### Gauge widget + +Gauge graph requires the max and min value of the left Y axis, if no value is informed the limits will be from 0 to 100. + +```ts +declare const dashboard: cloudwatch.Dashboard; +declare const errorAlarm: cloudwatch.Alarm; +declare const gaugeMetric: cloudwatch.Metric; + +dashboard.addWidgets(new cloudwatch.GaugeWidget({ + metrics: [gaugeMetric], + leftYAxis: { + min: 0, + max: 1000, + } +})); +``` + ### Alarm widget An alarm widget shows the graph and the alarm line of a single alarm: diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/graph.ts b/packages/@aws-cdk/aws-cloudwatch/lib/graph.ts index 16bce048a7132..ebcf83afab423 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/graph.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/graph.ts @@ -139,6 +139,135 @@ export enum GraphWidgetView { PIE = 'pie', } +/** + * Properties for a GaugeWidget + */ +export interface GaugeWidgetProps extends MetricWidgetProps { + /** + * Metrics to display on left Y axis + * + * @default - No metrics + */ + readonly metrics?: IMetric[]; + + /** + * Annotations for the left Y axis + * + * @default - No annotations + */ + readonly annotations?: HorizontalAnnotation[]; + + /** + * Left Y axis + * + * @default - None + */ + readonly leftYAxis?: YAxisProps; + + /** + * Position of the legend + * + * @default - bottom + */ + readonly legendPosition?: LegendPosition; + + /** + * Whether the graph should show live data + * + * @default false + */ + readonly liveData?: boolean; + + /** + * Whether to show the value from the entire time range. Only applicable for Bar and Pie charts. + * + * If false, values will be from the most recent period of your chosen time range; + * if true, shows the value from the entire time range. + * + * @default false + */ + readonly setPeriodToTimeRange?: boolean; + + /** + * The default period for all metrics in this widget. + * The period is the length of time represented by one data point on the graph. + * This default can be overridden within each metric definition. + * + * @default cdk.Duration.seconds(300) + */ + readonly period?: cdk.Duration; + + /** + * The default statistic to be displayed for each metric. + * This default can be overridden within the definition of each individual metric + * + * @default - The statistic for each metric is used + */ + readonly statistic?: string; +} + +/** + * A dashboard gauge widget that displays metrics + */ +export class GaugeWidget extends ConcreteWidget { + + private readonly props: GaugeWidgetProps; + + private readonly metrics: IMetric[]; + + constructor(props: GaugeWidgetProps) { + super(props.width || 6, props.height || 6); + this.props = props; + this.metrics = props.metrics ?? []; + this.copyMetricWarnings(...this.metrics); + } + + /** + * Add another metric to the left Y axis of the GaugeWidget + * + * @param metric the metric to add + */ + public addMetric(metric: IMetric) { + this.metrics.push(metric); + this.copyMetricWarnings(metric); + } + + public toJson(): any[] { + const horizontalAnnotations = [ + ...(this.props.annotations || []).map(mapAnnotation('annotations')), + ]; + + const metrics = allMetricsGraphJson(this.metrics, []); + const leftYAxis = { + ...this.props.leftYAxis, + min: this.props.leftYAxis?.min ?? 0, + max: this.props.leftYAxis?.max ?? 100, + }; + return [{ + type: 'metric', + width: this.width, + height: this.height, + x: this.x, + y: this.y, + properties: { + view: 'gauge', + title: this.props.title, + region: this.props.region || cdk.Aws.REGION, + metrics: metrics.length > 0 ? metrics : undefined, + annotations: horizontalAnnotations.length > 0 ? { horizontal: horizontalAnnotations } : undefined, + yAxis: { + left: leftYAxis ?? undefined, + }, + legend: this.props.legendPosition !== undefined ? { position: this.props.legendPosition } : undefined, + liveData: this.props.liveData, + setPeriodToTimeRange: this.props.setPeriodToTimeRange, + period: this.props.period?.toSeconds(), + stat: this.props.statistic, + }, + }]; + } +} + /** * Properties for a GraphWidget */ diff --git a/packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/GaugeAlarmIntegrationTestDefaultTestDeployAssertF43E2A2D.assets.json b/packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/GaugeAlarmIntegrationTestDefaultTestDeployAssertF43E2A2D.assets.json new file mode 100644 index 0000000000000..0fd619fc0de05 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/GaugeAlarmIntegrationTestDefaultTestDeployAssertF43E2A2D.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "GaugeAlarmIntegrationTestDefaultTestDeployAssertF43E2A2D.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/GaugeAlarmIntegrationTestDefaultTestDeployAssertF43E2A2D.template.json b/packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/GaugeAlarmIntegrationTestDefaultTestDeployAssertF43E2A2D.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/GaugeAlarmIntegrationTestDefaultTestDeployAssertF43E2A2D.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/cdk.out new file mode 100644 index 0000000000000..8ecc185e9dbee --- /dev/null +++ b/packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/gauge-alarm.assets.json b/packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/gauge-alarm.assets.json new file mode 100644 index 0000000000000..3d8f0b81624cf --- /dev/null +++ b/packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/gauge-alarm.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "8f02ef0ee3c900b85f9315a4a4807bd1e86f92b80d77cadc667b2b21b5b34e0b": { + "source": { + "path": "gauge-alarm.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "8f02ef0ee3c900b85f9315a4a4807bd1e86f92b80d77cadc667b2b21b5b34e0b.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/gauge-alarm.template.json b/packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/gauge-alarm.template.json new file mode 100644 index 0000000000000..d041fba598679 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/gauge-alarm.template.json @@ -0,0 +1,66 @@ +{ + "Resources": { + "queue": { + "Type": "AWS::SQS::Queue" + }, + "DashCCD7F836": { + "Type": "AWS::CloudWatch::Dashboard", + "Properties": { + "DashboardBody": { + "Fn::Join": [ + "", + [ + "{\"widgets\":[{\"type\":\"metric\",\"width\":24,\"height\":6,\"x\":0,\"y\":0,\"properties\":{\"view\":\"gauge\",\"region\":\"", + { + "Ref": "AWS::Region" + }, + "\",\"metrics\":[[\"AWS/SQS\",\"ApproximateNumberOfMessagesVisible\",\"QueueName\",\"", + { + "Fn::GetAtt": [ + "queue", + "QueueName" + ] + }, + "\"]],\"yAxis\":{\"left\":{\"max\":500,\"min\":0}}}}]}" + ] + ] + }, + "DashboardName": "MyCustomGaugeAlarm" + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/integ.json b/packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/integ.json new file mode 100644 index 0000000000000..bf239bc153d3d --- /dev/null +++ b/packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "21.0.0", + "testCases": { + "GaugeAlarmIntegrationTest/DefaultTest": { + "stacks": [ + "gauge-alarm" + ], + "assertionStack": "GaugeAlarmIntegrationTest/DefaultTest/DeployAssert", + "assertionStackName": "GaugeAlarmIntegrationTestDefaultTestDeployAssertF43E2A2D" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/manifest.json new file mode 100644 index 0000000000000..6589330e057d3 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/manifest.json @@ -0,0 +1,117 @@ +{ + "version": "21.0.0", + "artifacts": { + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + }, + "gauge-alarm.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "gauge-alarm.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "gauge-alarm": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "gauge-alarm.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/8f02ef0ee3c900b85f9315a4a4807bd1e86f92b80d77cadc667b2b21b5b34e0b.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "gauge-alarm.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "gauge-alarm.assets" + ], + "metadata": { + "/gauge-alarm/queue": [ + { + "type": "aws:cdk:logicalId", + "data": "queue" + } + ], + "/gauge-alarm/Dash/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "DashCCD7F836" + } + ], + "/gauge-alarm/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/gauge-alarm/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "gauge-alarm" + }, + "GaugeAlarmIntegrationTestDefaultTestDeployAssertF43E2A2D.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "GaugeAlarmIntegrationTestDefaultTestDeployAssertF43E2A2D.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "GaugeAlarmIntegrationTestDefaultTestDeployAssertF43E2A2D": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "GaugeAlarmIntegrationTestDefaultTestDeployAssertF43E2A2D.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "GaugeAlarmIntegrationTestDefaultTestDeployAssertF43E2A2D.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "GaugeAlarmIntegrationTestDefaultTestDeployAssertF43E2A2D.assets" + ], + "metadata": { + "/GaugeAlarmIntegrationTest/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/GaugeAlarmIntegrationTest/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "GaugeAlarmIntegrationTest/DefaultTest/DeployAssert" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/tree.json b/packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/tree.json new file mode 100644 index 0000000000000..c5c5f23258bd5 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudwatch/test/gauge-alarm.integ.snapshot/tree.json @@ -0,0 +1,118 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.102" + } + }, + "gauge-alarm": { + "id": "gauge-alarm", + "path": "gauge-alarm", + "children": { + "queue": { + "id": "queue", + "path": "gauge-alarm/queue", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + }, + "Dash": { + "id": "Dash", + "path": "gauge-alarm/Dash", + "children": { + "Resource": { + "id": "Resource", + "path": "gauge-alarm/Dash/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::CloudWatch::Dashboard", + "aws:cdk:cloudformation:props": { + "dashboardBody": { + "Fn::Join": [ + "", + [ + "{\"widgets\":[{\"type\":\"metric\",\"width\":24,\"height\":6,\"x\":0,\"y\":0,\"properties\":{\"view\":\"gauge\",\"region\":\"", + { + "Ref": "AWS::Region" + }, + "\",\"metrics\":[[\"AWS/SQS\",\"ApproximateNumberOfMessagesVisible\",\"QueueName\",\"", + { + "Fn::GetAtt": [ + "queue", + "QueueName" + ] + }, + "\"]],\"yAxis\":{\"left\":{\"max\":500,\"min\":0}}}}]}" + ] + ] + }, + "dashboardName": "MyCustomGaugeAlarm" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-cloudwatch.CfnDashboard", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-cloudwatch.Dashboard", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "GaugeAlarmIntegrationTest": { + "id": "GaugeAlarmIntegrationTest", + "path": "GaugeAlarmIntegrationTest", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "GaugeAlarmIntegrationTest/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "GaugeAlarmIntegrationTest/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.102" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "GaugeAlarmIntegrationTest/DefaultTest/DeployAssert", + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTest", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudwatch/test/graphs.test.ts b/packages/@aws-cdk/aws-cloudwatch/test/graphs.test.ts index e459063e5936d..5f9d3f0d27842 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/graphs.test.ts +++ b/packages/@aws-cdk/aws-cloudwatch/test/graphs.test.ts @@ -1,5 +1,5 @@ import { Duration, Stack } from '@aws-cdk/core'; -import { Alarm, AlarmWidget, Color, GraphWidget, GraphWidgetView, LegendPosition, LogQueryWidget, Metric, Shading, SingleValueWidget, LogQueryVisualizationType, CustomWidget } from '../lib'; +import { Alarm, AlarmWidget, Color, GraphWidget, GraphWidgetView, LegendPosition, LogQueryWidget, Metric, Shading, SingleValueWidget, LogQueryVisualizationType, CustomWidget, GaugeWidget } from '../lib'; describe('Graphs', () => { test('add stacked property to graphs', () => { @@ -795,6 +795,34 @@ describe('Graphs', () => { }]); }); + test('add GaugeWidget', () => { + // GIVEN + const stack = new Stack(); + const widget = new GaugeWidget({ + metrics: [new Metric({ namespace: 'CDK', metricName: 'Test' })], + }); + + // THEN + expect(stack.resolve(widget.toJson())).toEqual([{ + type: 'metric', + width: 6, + height: 6, + properties: { + view: 'gauge', + region: { Ref: 'AWS::Region' }, + metrics: [ + ['CDK', 'Test'], + ], + yAxis: { + left: { + min: 0, + max: 100, + }, + }, + }, + }]); + }); + test('GraphWidget supports stat and period', () => { // GIVEN const stack = new Stack(); diff --git a/packages/@aws-cdk/aws-cloudwatch/test/integ.gauge-alarm.ts b/packages/@aws-cdk/aws-cloudwatch/test/integ.gauge-alarm.ts new file mode 100644 index 0000000000000..4d782a8ed76b9 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudwatch/test/integ.gauge-alarm.ts @@ -0,0 +1,35 @@ +import * as cdk from '@aws-cdk/core'; +import * as integ from '@aws-cdk/integ-tests'; +import * as cloudwatch from '../lib'; + +const app = new cdk.App(); + +const stack = new cdk.Stack(app, 'gauge-alarm'); + +const queue = new cdk.CfnResource(stack, 'queue', { type: 'AWS::SQS::Queue' }); + +const numberOfMessagesVisibleMetric = new cloudwatch.Metric({ + namespace: 'AWS/SQS', + metricName: 'ApproximateNumberOfMessagesVisible', + dimensionsMap: { QueueName: queue.getAtt('QueueName').toString() }, +}); + +const dashboard = new cloudwatch.Dashboard(stack, 'Dash', { + dashboardName: 'MyCustomGaugeAlarm', +}); +dashboard.addWidgets( + new cloudwatch.GaugeWidget({ + leftYAxis: { + max: 500, + min: 0, + }, + width: 24, + metrics: [numberOfMessagesVisibleMetric], + }), +); + +new integ.IntegTest(app, 'GaugeAlarmIntegrationTest', { + testCases: [stack], +}); + +app.synth(); From dd8ae45ad844370fd960cd89010ed2c9979119b0 Mon Sep 17 00:00:00 2001 From: Kendra Neil <53584728+TheRealAmazonKendra@users.noreply.github.com> Date: Mon, 26 Sep 2022 14:33:59 -0400 Subject: [PATCH 11/24] chore(pr-lint): use aws-cdk-automation for requesting changes (#22237) ---- ### All Submissions: * [ ] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .github/workflows/pr-linter.yml | 2 +- tools/@aws-cdk/prlint/lint.ts | 2 +- tools/@aws-cdk/prlint/test/lint.test.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pr-linter.yml b/.github/workflows/pr-linter.yml index 0c79f516dce5e..67b7bece9419c 100644 --- a/.github/workflows/pr-linter.yml +++ b/.github/workflows/pr-linter.yml @@ -30,5 +30,5 @@ jobs: - name: Validate uses: ./tools/@aws-cdk/prlint env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.PROJEN_GITHUB_TOKEN }} REPO_ROOT: ${{ github.workspace }} diff --git a/tools/@aws-cdk/prlint/lint.ts b/tools/@aws-cdk/prlint/lint.ts index ec2be8a86bae0..425be04d6f8e4 100644 --- a/tools/@aws-cdk/prlint/lint.ts +++ b/tools/@aws-cdk/prlint/lint.ts @@ -171,7 +171,7 @@ export class PullRequestLinter { private async dismissPreviousPRLinterReviews(): Promise { const reviews = await this.client.pulls.listReviews(this.prParams); reviews.data.forEach(async (review: any) => { - if (review.user?.login === 'github-actions[bot]' && review.state !== 'DISMISSED') { + if (review.user?.login === 'aws-cdk-automation' && review.state !== 'DISMISSED') { await this.client.pulls.dismissReview({ ...this.prParams, review_id: review.id, diff --git a/tools/@aws-cdk/prlint/test/lint.test.ts b/tools/@aws-cdk/prlint/test/lint.test.ts index d7f0140a2fe76..5cd298eb65a49 100644 --- a/tools/@aws-cdk/prlint/test/lint.test.ts +++ b/tools/@aws-cdk/prlint/test/lint.test.ts @@ -355,7 +355,7 @@ function configureMock(pr: linter.GitHubPr, prFiles?: linter.GitHubFile[]): lint }, listReviews(_props: { _owner: string, _repo: string, _pull_number: number }) { - return { data: [{ id: 1111122222, user: { login: 'github-actions[bot]' }, state: 'CHANGES_REQUESTED' }] }; + return { data: [{ id: 1111122222, user: { login: 'aws-cdk-automation' }, state: 'CHANGES_REQUESTED' }] }; }, dismissReview() {}, From f834a4537643b32131076111be0693c6f8f96b24 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Mon, 26 Sep 2022 15:10:45 -0400 Subject: [PATCH 12/24] feat(logs): add dimensions to metric filter (#21654) Allows attaching dimensions to a metric filter. Uses the API design suggested by @marcogrcr in his [comment](https://github.com/aws/aws-cdk/issues/16999#issuecomment-1005172655) which is agreed on. resolves #16999 ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) *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/README.md | 3 + packages/@aws-cdk/aws-logs/lib/log-group.ts | 8 ++ .../@aws-cdk/aws-logs/lib/metric-filter.ts | 5 + .../test/integ.metricfilter-dimensions.ts | 32 +++++ ...-metricfilter-dimensions-integ.assets.json | 19 +++ ...etricfilter-dimensions-integ.template.json | 68 +++++++++ .../cdk.out | 1 + .../integ.json | 12 ++ .../manifest.json | 117 ++++++++++++++++ ...efaultTestDeployAssertF7E39B09.assets.json | 19 +++ ...aultTestDeployAssertF7E39B09.template.json | 36 +++++ .../tree.json | 131 ++++++++++++++++++ .../metricfilter.lit.integ.snapshot/tree.json | 8 +- .../aws-logs/test/metricfilter.test.ts | 56 ++++++++ 14 files changed, 511 insertions(+), 4 deletions(-) create mode 100644 packages/@aws-cdk/aws-logs/test/integ.metricfilter-dimensions.ts create mode 100644 packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/aws-cdk-metricfilter-dimensions-integ.assets.json create mode 100644 packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/aws-cdk-metricfilter-dimensions-integ.template.json create mode 100644 packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/integ.json create mode 100644 packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/manifest.json create mode 100644 packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/metricfilterdimensionsDefaultTestDeployAssertF7E39B09.assets.json create mode 100644 packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/metricfilterdimensionsDefaultTestDeployAssertF7E39B09.template.json create mode 100644 packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/tree.json diff --git a/packages/@aws-cdk/aws-logs/README.md b/packages/@aws-cdk/aws-logs/README.md index e1cac6e3813ab..c10a095920c65 100644 --- a/packages/@aws-cdk/aws-logs/README.md +++ b/packages/@aws-cdk/aws-logs/README.md @@ -163,6 +163,9 @@ const mf = new logs.MetricFilter(this, 'MetricFilter', { metricName: 'Latency', filterPattern: logs.FilterPattern.exists('$.latency'), metricValue: '$.latency', + dimensions: { + ErrorCode: '$.errorCode', + } }); //expose a metric from the metric filter diff --git a/packages/@aws-cdk/aws-logs/lib/log-group.ts b/packages/@aws-cdk/aws-logs/lib/log-group.ts index 3776b0c28f83a..f13741077af6b 100644 --- a/packages/@aws-cdk/aws-logs/lib/log-group.ts +++ b/packages/@aws-cdk/aws-logs/lib/log-group.ts @@ -537,4 +537,12 @@ export interface MetricFilterOptions { * @default No metric emitted. */ readonly defaultValue?: number; + + /** + * The fields to use as dimensions for the metric. One metric filter can include as many as three dimensions. + * + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-logs-metricfilter-metrictransformation.html#cfn-logs-metricfilter-metrictransformation-dimensions + * @default - No dimensions attached to metrics. + */ + readonly dimensions?: Record; } diff --git a/packages/@aws-cdk/aws-logs/lib/metric-filter.ts b/packages/@aws-cdk/aws-logs/lib/metric-filter.ts index e9b076b55c8a3..69efc0059f123 100644 --- a/packages/@aws-cdk/aws-logs/lib/metric-filter.ts +++ b/packages/@aws-cdk/aws-logs/lib/metric-filter.ts @@ -28,6 +28,10 @@ export class MetricFilter extends Resource { this.metricName = props.metricName; this.metricNamespace = props.metricNamespace; + if (Object.keys(props.dimensions ?? {}).length > 3) { + throw new Error('MetricFilter only supports a maximum of 3 Dimensions'); + } + // It looks odd to map this object to a singleton list, but that's how // we're supposed to do it according to the docs. // @@ -44,6 +48,7 @@ export class MetricFilter extends Resource { metricName: props.metricName, metricValue: props.metricValue ?? '1', defaultValue: props.defaultValue, + dimensions: props.dimensions ? Object.entries(props.dimensions).map(([key, value]) => ({ key, value })) : undefined, }], }); } diff --git a/packages/@aws-cdk/aws-logs/test/integ.metricfilter-dimensions.ts b/packages/@aws-cdk/aws-logs/test/integ.metricfilter-dimensions.ts new file mode 100644 index 0000000000000..542f481be5817 --- /dev/null +++ b/packages/@aws-cdk/aws-logs/test/integ.metricfilter-dimensions.ts @@ -0,0 +1,32 @@ +import { App, RemovalPolicy, Stack, StackProps } from '@aws-cdk/core'; +import { IntegTest } from '@aws-cdk/integ-tests'; +import { FilterPattern, LogGroup, MetricFilter } from '../lib'; + +class TestStack extends Stack { + constructor(scope: App, id: string, props?: StackProps) { + super(scope, id, props); + + const logGroup = new LogGroup(this, 'LogGroup', { + removalPolicy: RemovalPolicy.DESTROY, + }); + + new MetricFilter(this, 'MetricFilter', { + logGroup, + metricNamespace: 'MyApp', + metricName: 'Latency', + filterPattern: FilterPattern.exists('$.latency'), + metricValue: '$.latency', + dimensions: { + ErrorCode: '$.errorCode', + }, + }); + } +} + +const app = new App(); +const testCase = new TestStack(app, 'aws-cdk-metricfilter-dimensions-integ'); + +new IntegTest(app, 'metricfilter-dimensions', { + testCases: [testCase], +}); +app.synth(); diff --git a/packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/aws-cdk-metricfilter-dimensions-integ.assets.json b/packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/aws-cdk-metricfilter-dimensions-integ.assets.json new file mode 100644 index 0000000000000..5be191e7f4c29 --- /dev/null +++ b/packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/aws-cdk-metricfilter-dimensions-integ.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "3d99811cf4d8b2d453d889e936569b925ead97bdb93a86d122b34d68818be01d": { + "source": { + "path": "aws-cdk-metricfilter-dimensions-integ.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "3d99811cf4d8b2d453d889e936569b925ead97bdb93a86d122b34d68818be01d.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/aws-cdk-metricfilter-dimensions-integ.template.json b/packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/aws-cdk-metricfilter-dimensions-integ.template.json new file mode 100644 index 0000000000000..b2b3588df8f3f --- /dev/null +++ b/packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/aws-cdk-metricfilter-dimensions-integ.template.json @@ -0,0 +1,68 @@ +{ + "Resources": { + "LogGroupF5B46931": { + "Type": "AWS::Logs::LogGroup", + "Properties": { + "RetentionInDays": 731 + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "MetricFilter1B93B6E5": { + "Type": "AWS::Logs::MetricFilter", + "Properties": { + "FilterPattern": "{ $.latency = \"*\" }", + "LogGroupName": { + "Ref": "LogGroupF5B46931" + }, + "MetricTransformations": [ + { + "Dimensions": [ + { + "Key": "ErrorCode", + "Value": "$.errorCode" + } + ], + "MetricName": "Latency", + "MetricNamespace": "MyApp", + "MetricValue": "$.latency" + } + ] + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/cdk.out new file mode 100644 index 0000000000000..8ecc185e9dbee --- /dev/null +++ b/packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/integ.json b/packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/integ.json new file mode 100644 index 0000000000000..eff8bab92b5dc --- /dev/null +++ b/packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "21.0.0", + "testCases": { + "metricfilter-dimensions/DefaultTest": { + "stacks": [ + "aws-cdk-metricfilter-dimensions-integ" + ], + "assertionStack": "metricfilter-dimensions/DefaultTest/DeployAssert", + "assertionStackName": "metricfilterdimensionsDefaultTestDeployAssertF7E39B09" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/manifest.json new file mode 100644 index 0000000000000..906179f65fb53 --- /dev/null +++ b/packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/manifest.json @@ -0,0 +1,117 @@ +{ + "version": "21.0.0", + "artifacts": { + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + }, + "aws-cdk-metricfilter-dimensions-integ.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-cdk-metricfilter-dimensions-integ.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-cdk-metricfilter-dimensions-integ": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-cdk-metricfilter-dimensions-integ.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/3d99811cf4d8b2d453d889e936569b925ead97bdb93a86d122b34d68818be01d.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-cdk-metricfilter-dimensions-integ.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "aws-cdk-metricfilter-dimensions-integ.assets" + ], + "metadata": { + "/aws-cdk-metricfilter-dimensions-integ/LogGroup/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "LogGroupF5B46931" + } + ], + "/aws-cdk-metricfilter-dimensions-integ/MetricFilter/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MetricFilter1B93B6E5" + } + ], + "/aws-cdk-metricfilter-dimensions-integ/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-cdk-metricfilter-dimensions-integ/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-cdk-metricfilter-dimensions-integ" + }, + "metricfilterdimensionsDefaultTestDeployAssertF7E39B09.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "metricfilterdimensionsDefaultTestDeployAssertF7E39B09.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "metricfilterdimensionsDefaultTestDeployAssertF7E39B09": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "metricfilterdimensionsDefaultTestDeployAssertF7E39B09.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "metricfilterdimensionsDefaultTestDeployAssertF7E39B09.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "metricfilterdimensionsDefaultTestDeployAssertF7E39B09.assets" + ], + "metadata": { + "/metricfilter-dimensions/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/metricfilter-dimensions/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "metricfilter-dimensions/DefaultTest/DeployAssert" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/metricfilterdimensionsDefaultTestDeployAssertF7E39B09.assets.json b/packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/metricfilterdimensionsDefaultTestDeployAssertF7E39B09.assets.json new file mode 100644 index 0000000000000..9c8f92b52716e --- /dev/null +++ b/packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/metricfilterdimensionsDefaultTestDeployAssertF7E39B09.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "metricfilterdimensionsDefaultTestDeployAssertF7E39B09.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/metricfilterdimensionsDefaultTestDeployAssertF7E39B09.template.json b/packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/metricfilterdimensionsDefaultTestDeployAssertF7E39B09.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/metricfilterdimensionsDefaultTestDeployAssertF7E39B09.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/tree.json b/packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/tree.json new file mode 100644 index 0000000000000..53e7571726531 --- /dev/null +++ b/packages/@aws-cdk/aws-logs/test/metricfilter-dimensions.integ.snapshot/tree.json @@ -0,0 +1,131 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.95" + } + }, + "aws-cdk-metricfilter-dimensions-integ": { + "id": "aws-cdk-metricfilter-dimensions-integ", + "path": "aws-cdk-metricfilter-dimensions-integ", + "children": { + "LogGroup": { + "id": "LogGroup", + "path": "aws-cdk-metricfilter-dimensions-integ/LogGroup", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-metricfilter-dimensions-integ/LogGroup/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Logs::LogGroup", + "aws:cdk:cloudformation:props": { + "retentionInDays": 731 + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-logs.CfnLogGroup", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-logs.LogGroup", + "version": "0.0.0" + } + }, + "MetricFilter": { + "id": "MetricFilter", + "path": "aws-cdk-metricfilter-dimensions-integ/MetricFilter", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-metricfilter-dimensions-integ/MetricFilter/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Logs::MetricFilter", + "aws:cdk:cloudformation:props": { + "filterPattern": "{ $.latency = \"*\" }", + "logGroupName": { + "Ref": "LogGroupF5B46931" + }, + "metricTransformations": [ + { + "metricNamespace": "MyApp", + "metricName": "Latency", + "metricValue": "$.latency", + "dimensions": [ + { + "key": "ErrorCode", + "value": "$.errorCode" + } + ] + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-logs.CfnMetricFilter", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-logs.MetricFilter", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "metricfilter-dimensions": { + "id": "metricfilter-dimensions", + "path": "metricfilter-dimensions", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "metricfilter-dimensions/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "metricfilter-dimensions/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.95" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "metricfilter-dimensions/DefaultTest/DeployAssert", + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTest", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-logs/test/metricfilter.lit.integ.snapshot/tree.json b/packages/@aws-cdk/aws-logs/test/metricfilter.lit.integ.snapshot/tree.json index 6a97a05861cfe..e24af4a1b21ce 100644 --- a/packages/@aws-cdk/aws-logs/test/metricfilter.lit.integ.snapshot/tree.json +++ b/packages/@aws-cdk/aws-logs/test/metricfilter.lit.integ.snapshot/tree.json @@ -30,13 +30,13 @@ } }, "constructInfo": { - "fqn": "@aws-cdk/aws-logs.CfnLogGroup", + "fqn": "@aws-cdk/core.CfnResource", "version": "0.0.0" } } }, "constructInfo": { - "fqn": "@aws-cdk/aws-logs.LogGroup", + "fqn": "@aws-cdk/core.Resource", "version": "0.0.0" } }, @@ -64,13 +64,13 @@ } }, "constructInfo": { - "fqn": "@aws-cdk/aws-logs.CfnMetricFilter", + "fqn": "@aws-cdk/core.CfnResource", "version": "0.0.0" } } }, "constructInfo": { - "fqn": "@aws-cdk/aws-logs.MetricFilter", + "fqn": "@aws-cdk/core.Resource", "version": "0.0.0" } } diff --git a/packages/@aws-cdk/aws-logs/test/metricfilter.test.ts b/packages/@aws-cdk/aws-logs/test/metricfilter.test.ts index 6ab67899db2ea..88a925a488afc 100644 --- a/packages/@aws-cdk/aws-logs/test/metricfilter.test.ts +++ b/packages/@aws-cdk/aws-logs/test/metricfilter.test.ts @@ -30,6 +30,62 @@ describe('metric filter', () => { }); }); + test('with dimensions', () => { + // GIVEN + const stack = new Stack(); + const logGroup = new LogGroup(stack, 'LogGroup'); + + // WHEN + new MetricFilter(stack, 'Subscription', { + logGroup, + metricNamespace: 'AWS/Test', + metricName: 'Latency', + metricValue: '$.latency', + filterPattern: FilterPattern.exists('$.latency'), + dimensions: { + Foo: 'Bar', + }, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Logs::MetricFilter', { + MetricTransformations: [{ + MetricNamespace: 'AWS/Test', + MetricName: 'Latency', + MetricValue: '$.latency', + Dimensions: [ + { + Key: 'Foo', + Value: 'Bar', + }, + ], + }], + FilterPattern: '{ $.latency = "*" }', + LogGroupName: { Ref: 'LogGroupF5B46931' }, + }); + }); + + test('should throw with more than 3 dimensions', () => { + // GIVEN + const stack = new Stack(); + const logGroup = new LogGroup(stack, 'LogGroup'); + + // WHEN + expect(() => new MetricFilter(stack, 'Subscription', { + logGroup, + metricNamespace: 'AWS/Test', + metricName: 'Latency', + metricValue: '$.latency', + filterPattern: FilterPattern.exists('$.latency'), + dimensions: { + Foo: 'Bar', + Bar: 'Baz', + Baz: 'Qux', + Qux: 'Quux', + }, + })).toThrow(/MetricFilter only supports a maximum of 3 Dimensions/); + }); + test('metric filter exposes metric', () => { // GIVEN const stack = new Stack(); From 58eeda9e2969d1f74c78c12c51336696e069dc4d Mon Sep 17 00:00:00 2001 From: Neil Kuan <46012524+neilkuan@users.noreply.github.com> Date: Tue, 27 Sep 2022 20:22:57 +0800 Subject: [PATCH 13/24] chore(apprunner): support all runtimes (#22247) ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-apprunner/lib/service.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/@aws-cdk/aws-apprunner/lib/service.ts b/packages/@aws-cdk/aws-apprunner/lib/service.ts index cddaf2210a6de..d4d6865a81693 100644 --- a/packages/@aws-cdk/aws-apprunner/lib/service.ts +++ b/packages/@aws-cdk/aws-apprunner/lib/service.ts @@ -92,11 +92,32 @@ export class Memory { * The code runtimes */ export class Runtime { + + /** + * CORRETTO 8 + */ + public static readonly CORRETTO_8 = Runtime.of('CORRETTO_8') + + /** + * CORRETTO 11 + */ + public static readonly CORRETTO_11 = Runtime.of('CORRETTO_11') + /** * NodeJS 12 */ public static readonly NODEJS_12 = Runtime.of('NODEJS_12') + /** + * NodeJS 14 + */ + public static readonly NODEJS_14 = Runtime.of('NODEJS_14') + + /** + * NodeJS 16 + */ + public static readonly NODEJS_16 = Runtime.of('NODEJS_16') + /** * Python 3 */ From fcb311d615422b76f18b6be60dd466b315fcd6b0 Mon Sep 17 00:00:00 2001 From: Marcio Cruz de Almeida <67694075+marciocadev@users.noreply.github.com> Date: Tue, 27 Sep 2022 10:24:37 -0300 Subject: [PATCH 14/24] feat(dynamodb): Changes how metricForOperation methods are used (#22097) # Description This PR is changing `metricThrottledRequestsForOperation` and `metricSystemErrorForOperations` to receive the `OperationsMetricsOptions` parameter to standardize these calls and adds `metricThrottledRequestsForOperation` to the ITable interface I also change the `metricThrottledRequests` to deprecated Closes #21963 ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *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-dynamodb/README.md | 23 ++ packages/@aws-cdk/aws-dynamodb/lib/table.ts | 63 +++- packages/@aws-cdk/aws-dynamodb/package.json | 3 +- .../alarm-metrics.assets.json | 19 ++ .../alarm-metrics.template.json | 187 ++++++++++++ ...efaultTestDeployAssert8721BBC0.assets.json | 19 ++ ...aultTestDeployAssert8721BBC0.template.json | 36 +++ .../cdk.out | 1 + .../integ.json | 12 + .../manifest.json | 123 ++++++++ .../tree.json | 276 ++++++++++++++++++ .../aws-dynamodb/test/dynamodb.test.ts | 91 ++++++ .../test/integ.dynamodb.alarm-metrics.ts | 42 +++ 13 files changed, 880 insertions(+), 15 deletions(-) create mode 100644 packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/alarm-metrics.assets.json create mode 100644 packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/alarm-metrics.template.json create mode 100644 packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/alarmmetricsintegDefaultTestDeployAssert8721BBC0.assets.json create mode 100644 packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/alarmmetricsintegDefaultTestDeployAssert8721BBC0.template.json create mode 100644 packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/integ.json create mode 100644 packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/manifest.json create mode 100644 packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/tree.json create mode 100644 packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.alarm-metrics.ts diff --git a/packages/@aws-cdk/aws-dynamodb/README.md b/packages/@aws-cdk/aws-dynamodb/README.md index f9b5771953b14..bc7ca16e5b182 100644 --- a/packages/@aws-cdk/aws-dynamodb/README.md +++ b/packages/@aws-cdk/aws-dynamodb/README.md @@ -208,3 +208,26 @@ const table = new dynamodb.Table(this, 'Table', { kinesisStream: stream, }); ``` + +## Alarm metrics + +Alarms can be configured on the DynamoDB table to captured metric data + +```ts +import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; + +const table = new dynamodb.Table(this, 'Table', { + partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING }, +}); + +const metric = table.metricThrottledRequestsForOperations({ + operations: [dynamodb.Operation.PUT_ITEM], + period: Duration.minutes(1), +}); + +new cloudwatch.Alarm(stack, 'Alarm', { + metric: metric, + evaluationPeriods: 1, + threshold: 1, +}); +``` diff --git a/packages/@aws-cdk/aws-dynamodb/lib/table.ts b/packages/@aws-cdk/aws-dynamodb/lib/table.ts index f518ea9a4914a..539761ba96032 100644 --- a/packages/@aws-cdk/aws-dynamodb/lib/table.ts +++ b/packages/@aws-cdk/aws-dynamodb/lib/table.ts @@ -36,6 +36,11 @@ export interface SystemErrorsForOperationsMetricOptions extends cloudwatch.Metri } +/** + * Options for configuring metrics that considers multiple operations. + */ +export interface OperationsMetricOptions extends SystemErrorsForOperationsMetricOptions {} + /** * Supported DynamoDB table operations. */ @@ -534,9 +539,18 @@ export interface ITable extends IResource { * * @param props properties of a metric * + * @deprecated use `metricThrottledRequestsForOperations` */ metricThrottledRequests(props?: cloudwatch.MetricOptions): cloudwatch.Metric; + /** + * Metric for throttled requests + * + * @param props properties of a metric + * + */ + metricThrottledRequestsForOperations(props?: OperationsMetricOptions): cloudwatch.IMetric; + /** * Metric for the successful request latency * @@ -869,18 +883,6 @@ abstract class TableBase extends Resource implements ITable { return this.metric('ThrottledRequests', { statistic: 'sum', ...props }); } - /** - * How many requests are throttled on this table, for the given operation - * - * Default: sum over 5 minutes - */ - public metricThrottledRequestsForOperation(operation: string, props?: cloudwatch.MetricOptions): cloudwatch.Metric { - return new cloudwatch.Metric({ - ...DynamoDBMetrics.throttledRequestsSum({ Operation: operation, TableName: this.tableName }), - ...props, - }).attachTo(this); - } - /** * Metric for the successful request latency this table. * @@ -904,6 +906,29 @@ abstract class TableBase extends Resource implements ITable { }).attachTo(this); } + /** + * How many requests are throttled on this table, for the given operation + * + * Default: sum over 5 minutes + */ + public metricThrottledRequestsForOperation(operation: string, props?: cloudwatch.MetricOptions): cloudwatch.Metric { + return new cloudwatch.Metric({ + ...DynamoDBMetrics.throttledRequestsSum({ Operation: operation, TableName: this.tableName }), + ...props, + }).attachTo(this); + } + + /** + * How many requests are throttled on this table. + * + * This will sum errors across all possible operations. + * Note that by default, each individual metric will be calculated as a sum over a period of 5 minutes. + * You can customize this by using the `statistic` and `period` properties. + */ + public metricThrottledRequestsForOperations(props?: OperationsMetricOptions): cloudwatch.IMetric { + return this.sumMetricsForOperations('ThrottledRequests', 'Sum of throttled requests across all operations', props); + } + /** * Metric for the system errors this table. * @@ -912,20 +937,30 @@ abstract class TableBase extends Resource implements ITable { * You can customize this by using the `statistic` and `period` properties. */ public metricSystemErrorsForOperations(props?: SystemErrorsForOperationsMetricOptions): cloudwatch.IMetric { + return this.sumMetricsForOperations('SystemErrors', 'Sum of errors across all operations', props); + } + /** + * Create a math expression for operations. + * + * @param metricName The metric name. + * @param expressionLabel Label for expression + * @param props operation list + */ + private sumMetricsForOperations(metricName: string, expressionLabel: string, props?: OperationsMetricOptions): cloudwatch.IMetric { if (props?.dimensions?.Operation) { throw new Error("The Operation dimension is not supported. Use the 'operations' property."); } const operations = props?.operations ?? Object.values(Operation); - const values = this.createMetricsForOperations('SystemErrors', operations, { statistic: 'sum', ...props }); + const values = this.createMetricsForOperations(metricName, operations, { statistic: 'sum', ...props }); const sum = new cloudwatch.MathExpression({ expression: `${Object.keys(values).join(' + ')}`, usingMetrics: { ...values }, color: props?.color, - label: 'Sum of errors across all operations', + label: expressionLabel, period: props?.period, }); diff --git a/packages/@aws-cdk/aws-dynamodb/package.json b/packages/@aws-cdk/aws-dynamodb/package.json index d3518666a91f8..7f39e462f3158 100644 --- a/packages/@aws-cdk/aws-dynamodb/package.json +++ b/packages/@aws-cdk/aws-dynamodb/package.json @@ -82,8 +82,9 @@ "devDependencies": { "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", - "@aws-cdk/integ-runner": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", + "@aws-cdk/integ-runner": "0.0.0", + "@aws-cdk/integ-tests": "0.0.0", "@aws-cdk/pkglint": "0.0.0", "@types/aws-lambda": "^8.10.104", "@types/jest": "^27.5.2", diff --git a/packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/alarm-metrics.assets.json b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/alarm-metrics.assets.json new file mode 100644 index 0000000000000..84cb2270266dc --- /dev/null +++ b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/alarm-metrics.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "fbae493d10d1cf974dda665d5d00077b8ac81e2d8edeec024da94102b7db2cc8": { + "source": { + "path": "alarm-metrics.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "fbae493d10d1cf974dda665d5d00077b8ac81e2d8edeec024da94102b7db2cc8.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/alarm-metrics.template.json b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/alarm-metrics.template.json new file mode 100644 index 0000000000000..c76f714107830 --- /dev/null +++ b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/alarm-metrics.template.json @@ -0,0 +1,187 @@ +{ + "Resources": { + "TableCD117FA1": { + "Type": "AWS::DynamoDB::Table", + "Properties": { + "KeySchema": [ + { + "AttributeName": "metric", + "KeyType": "HASH" + } + ], + "AttributeDefinitions": [ + { + "AttributeName": "metric", + "AttributeType": "S" + } + ], + "ProvisionedThroughput": { + "ReadCapacityUnits": 5, + "WriteCapacityUnits": 5 + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "TableThrottleAlarm606592BC": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "ComparisonOperator": "GreaterThanOrEqualToThreshold", + "EvaluationPeriods": 1, + "Metrics": [ + { + "Expression": "putitem + scan", + "Id": "expr_1", + "Label": "Sum of throttled requests across all operations" + }, + { + "Id": "putitem", + "MetricStat": { + "Metric": { + "Dimensions": [ + { + "Name": "Operation", + "Value": "PutItem" + }, + { + "Name": "TableName", + "Value": { + "Ref": "TableCD117FA1" + } + } + ], + "MetricName": "ThrottledRequests", + "Namespace": "AWS/DynamoDB" + }, + "Period": 60, + "Stat": "Sum" + }, + "ReturnData": false + }, + { + "Id": "scan", + "MetricStat": { + "Metric": { + "Dimensions": [ + { + "Name": "Operation", + "Value": "Scan" + }, + { + "Name": "TableName", + "Value": { + "Ref": "TableCD117FA1" + } + } + ], + "MetricName": "ThrottledRequests", + "Namespace": "AWS/DynamoDB" + }, + "Period": 60, + "Stat": "Sum" + }, + "ReturnData": false + } + ], + "Threshold": 1 + } + }, + "TableErrorAlarm12A4E2F3": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "ComparisonOperator": "GreaterThanOrEqualToThreshold", + "EvaluationPeriods": 1, + "Metrics": [ + { + "Expression": "putitem + scan", + "Id": "expr_1", + "Label": "Sum of errors across all operations" + }, + { + "Id": "putitem", + "MetricStat": { + "Metric": { + "Dimensions": [ + { + "Name": "Operation", + "Value": "PutItem" + }, + { + "Name": "TableName", + "Value": { + "Ref": "TableCD117FA1" + } + } + ], + "MetricName": "SystemErrors", + "Namespace": "AWS/DynamoDB" + }, + "Period": 60, + "Stat": "Sum" + }, + "ReturnData": false + }, + { + "Id": "scan", + "MetricStat": { + "Metric": { + "Dimensions": [ + { + "Name": "Operation", + "Value": "Scan" + }, + { + "Name": "TableName", + "Value": { + "Ref": "TableCD117FA1" + } + } + ], + "MetricName": "SystemErrors", + "Namespace": "AWS/DynamoDB" + }, + "Period": 60, + "Stat": "Sum" + }, + "ReturnData": false + } + ], + "Threshold": 1 + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/alarmmetricsintegDefaultTestDeployAssert8721BBC0.assets.json b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/alarmmetricsintegDefaultTestDeployAssert8721BBC0.assets.json new file mode 100644 index 0000000000000..90682b0b0fe2b --- /dev/null +++ b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/alarmmetricsintegDefaultTestDeployAssert8721BBC0.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "alarmmetricsintegDefaultTestDeployAssert8721BBC0.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/alarmmetricsintegDefaultTestDeployAssert8721BBC0.template.json b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/alarmmetricsintegDefaultTestDeployAssert8721BBC0.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/alarmmetricsintegDefaultTestDeployAssert8721BBC0.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/cdk.out new file mode 100644 index 0000000000000..8ecc185e9dbee --- /dev/null +++ b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/integ.json b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/integ.json new file mode 100644 index 0000000000000..c32af717a0060 --- /dev/null +++ b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "21.0.0", + "testCases": { + "alarm-metrics-integ/DefaultTest": { + "stacks": [ + "alarm-metrics" + ], + "assertionStack": "alarm-metrics-integ/DefaultTest/DeployAssert", + "assertionStackName": "alarmmetricsintegDefaultTestDeployAssert8721BBC0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/manifest.json new file mode 100644 index 0000000000000..02db269fe97ae --- /dev/null +++ b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/manifest.json @@ -0,0 +1,123 @@ +{ + "version": "21.0.0", + "artifacts": { + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + }, + "alarm-metrics.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "alarm-metrics.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "alarm-metrics": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "alarm-metrics.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/fbae493d10d1cf974dda665d5d00077b8ac81e2d8edeec024da94102b7db2cc8.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "alarm-metrics.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "alarm-metrics.assets" + ], + "metadata": { + "/alarm-metrics/Table/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "TableCD117FA1" + } + ], + "/alarm-metrics/TableThrottleAlarm/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "TableThrottleAlarm606592BC" + } + ], + "/alarm-metrics/TableErrorAlarm/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "TableErrorAlarm12A4E2F3" + } + ], + "/alarm-metrics/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/alarm-metrics/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "alarm-metrics" + }, + "alarmmetricsintegDefaultTestDeployAssert8721BBC0.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "alarmmetricsintegDefaultTestDeployAssert8721BBC0.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "alarmmetricsintegDefaultTestDeployAssert8721BBC0": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "alarmmetricsintegDefaultTestDeployAssert8721BBC0.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "alarmmetricsintegDefaultTestDeployAssert8721BBC0.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "alarmmetricsintegDefaultTestDeployAssert8721BBC0.assets" + ], + "metadata": { + "/alarm-metrics-integ/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/alarm-metrics-integ/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "alarm-metrics-integ/DefaultTest/DeployAssert" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/tree.json b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/tree.json new file mode 100644 index 0000000000000..b79705862e78b --- /dev/null +++ b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.alarm-metrics.integ.snapshot/tree.json @@ -0,0 +1,276 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.102" + } + }, + "alarm-metrics": { + "id": "alarm-metrics", + "path": "alarm-metrics", + "children": { + "Table": { + "id": "Table", + "path": "alarm-metrics/Table", + "children": { + "Resource": { + "id": "Resource", + "path": "alarm-metrics/Table/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::DynamoDB::Table", + "aws:cdk:cloudformation:props": { + "keySchema": [ + { + "attributeName": "metric", + "keyType": "HASH" + } + ], + "attributeDefinitions": [ + { + "attributeName": "metric", + "attributeType": "S" + } + ], + "provisionedThroughput": { + "readCapacityUnits": 5, + "writeCapacityUnits": 5 + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-dynamodb.CfnTable", + "version": "0.0.0" + } + }, + "ScalingRole": { + "id": "ScalingRole", + "path": "alarm-metrics/Table/ScalingRole", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-dynamodb.Table", + "version": "0.0.0" + } + }, + "TableThrottleAlarm": { + "id": "TableThrottleAlarm", + "path": "alarm-metrics/TableThrottleAlarm", + "children": { + "Resource": { + "id": "Resource", + "path": "alarm-metrics/TableThrottleAlarm/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::CloudWatch::Alarm", + "aws:cdk:cloudformation:props": { + "comparisonOperator": "GreaterThanOrEqualToThreshold", + "evaluationPeriods": 1, + "metrics": [ + { + "expression": "putitem + scan", + "id": "expr_1", + "label": "Sum of throttled requests across all operations" + }, + { + "metricStat": { + "metric": { + "metricName": "ThrottledRequests", + "namespace": "AWS/DynamoDB", + "dimensions": [ + { + "name": "Operation", + "value": "PutItem" + }, + { + "name": "TableName", + "value": { + "Ref": "TableCD117FA1" + } + } + ] + }, + "period": 60, + "stat": "Sum" + }, + "id": "putitem", + "returnData": false + }, + { + "metricStat": { + "metric": { + "metricName": "ThrottledRequests", + "namespace": "AWS/DynamoDB", + "dimensions": [ + { + "name": "Operation", + "value": "Scan" + }, + { + "name": "TableName", + "value": { + "Ref": "TableCD117FA1" + } + } + ] + }, + "period": 60, + "stat": "Sum" + }, + "id": "scan", + "returnData": false + } + ], + "threshold": 1 + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-cloudwatch.CfnAlarm", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-cloudwatch.Alarm", + "version": "0.0.0" + } + }, + "TableErrorAlarm": { + "id": "TableErrorAlarm", + "path": "alarm-metrics/TableErrorAlarm", + "children": { + "Resource": { + "id": "Resource", + "path": "alarm-metrics/TableErrorAlarm/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::CloudWatch::Alarm", + "aws:cdk:cloudformation:props": { + "comparisonOperator": "GreaterThanOrEqualToThreshold", + "evaluationPeriods": 1, + "metrics": [ + { + "expression": "putitem + scan", + "id": "expr_1", + "label": "Sum of errors across all operations" + }, + { + "metricStat": { + "metric": { + "metricName": "SystemErrors", + "namespace": "AWS/DynamoDB", + "dimensions": [ + { + "name": "Operation", + "value": "PutItem" + }, + { + "name": "TableName", + "value": { + "Ref": "TableCD117FA1" + } + } + ] + }, + "period": 60, + "stat": "Sum" + }, + "id": "putitem", + "returnData": false + }, + { + "metricStat": { + "metric": { + "metricName": "SystemErrors", + "namespace": "AWS/DynamoDB", + "dimensions": [ + { + "name": "Operation", + "value": "Scan" + }, + { + "name": "TableName", + "value": { + "Ref": "TableCD117FA1" + } + } + ] + }, + "period": 60, + "stat": "Sum" + }, + "id": "scan", + "returnData": false + } + ], + "threshold": 1 + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-cloudwatch.CfnAlarm", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-cloudwatch.Alarm", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "alarm-metrics-integ": { + "id": "alarm-metrics-integ", + "path": "alarm-metrics-integ", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "alarm-metrics-integ/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "alarm-metrics-integ/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.102" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "alarm-metrics-integ/DefaultTest/DeployAssert", + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTest", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts index 72a0de9f1491c..35145edd0d57c 100644 --- a/packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts +++ b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts @@ -1,5 +1,6 @@ import { Annotations, Match, Template } from '@aws-cdk/assertions'; import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; +import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import * as iam from '@aws-cdk/aws-iam'; import * as kinesis from '@aws-cdk/aws-kinesis'; import * as kms from '@aws-cdk/aws-kms'; @@ -3009,6 +3010,96 @@ test('L1 inside L2 expects removalpolicy to have been set', () => { }).toThrow(/is a stateful resource type/); }); +test('System errors metrics', () => { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'Stack'); + + // WHEN + const table = new Table(stack, 'Table', { + partitionKey: { name: 'metric', type: AttributeType.STRING }, + }); + const metricTableThrottled = table.metricSystemErrorsForOperations({ + operations: [Operation.SCAN], + period: Duration.minutes(1), + }); + new cloudwatch.Alarm(stack, 'TableErrorAlarm', { + metric: metricTableThrottled, + evaluationPeriods: 1, + threshold: 1, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { + Metrics: Match.arrayWith([ + Match.objectLike({ + Expression: 'scan', + }), + Match.objectLike({ + MetricStat: Match.objectLike({ + Metric: Match.objectLike({ + Dimensions: Match.arrayWith([ + Match.objectLike({ + Name: 'Operation', + }), + Match.objectLike({ + Name: 'TableName', + }), + ]), + MetricName: 'SystemErrors', + Namespace: 'AWS/DynamoDB', + }), + }), + }), + ]), + }); +}); + +test('Throttled requests metrics', () => { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'Stack'); + + // WHEN + const table = new Table(stack, 'Table', { + partitionKey: { name: 'metric', type: AttributeType.STRING }, + }); + const metricTableThrottled = table.metricThrottledRequestsForOperations({ + operations: [Operation.PUT_ITEM], + period: Duration.minutes(1), + }); + new cloudwatch.Alarm(stack, 'TableThrottleAlarm', { + metric: metricTableThrottled, + evaluationPeriods: 1, + threshold: 1, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { + Metrics: Match.arrayWith([ + Match.objectLike({ + Expression: 'putitem', + }), + Match.objectLike({ + MetricStat: Match.objectLike({ + Metric: Match.objectLike({ + Dimensions: Match.arrayWith([ + Match.objectLike({ + Name: 'Operation', + }), + Match.objectLike({ + Name: 'TableName', + }), + ]), + MetricName: 'ThrottledRequests', + Namespace: 'AWS/DynamoDB', + }), + }), + }), + ]), + }); +}); + function testGrant(expectedActions: string[], invocation: (user: iam.IPrincipal, table: Table) => void) { // GIVEN const stack = new Stack(); diff --git a/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.alarm-metrics.ts b/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.alarm-metrics.ts new file mode 100644 index 0000000000000..df7494b4b388a --- /dev/null +++ b/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.alarm-metrics.ts @@ -0,0 +1,42 @@ +import { Alarm } from '@aws-cdk/aws-cloudwatch'; +import { App, Duration, Stack, StackProps } from '@aws-cdk/core'; +import { IntegTest } from '@aws-cdk/integ-tests'; +import { Construct } from 'constructs'; +import { AttributeType, Operation, Table } from '../lib'; + +export class TestStack extends Stack { + constructor(scope: Construct, id: string, props?: StackProps) { + super(scope, id, props); + + const table = new Table(this, 'Table', { + partitionKey: { name: 'metric', type: AttributeType.STRING }, + }); + const metricTableThrottled = table.metricThrottledRequestsForOperations({ + operations: [Operation.PUT_ITEM, Operation.SCAN], + period: Duration.minutes(1), + }); + new Alarm(this, 'TableThrottleAlarm', { + metric: metricTableThrottled, + evaluationPeriods: 1, + threshold: 1, + }); + const metricTableError = table.metricSystemErrorsForOperations({ + operations: [Operation.PUT_ITEM, Operation.SCAN], + period: Duration.minutes(1), + }); + new Alarm(this, 'TableErrorAlarm', { + metric: metricTableError, + evaluationPeriods: 1, + threshold: 1, + }); + } +} + +const app = new App(); +const stack = new TestStack(app, 'alarm-metrics'); + +new IntegTest(app, 'alarm-metrics-integ', { + testCases: [stack], +}); + +app.synth(); \ No newline at end of file From d87a651608d23f3bfc3c178093d92b5bdda71084 Mon Sep 17 00:00:00 2001 From: Ryan Batchelder Date: Tue, 27 Sep 2022 14:32:45 -0400 Subject: [PATCH 15/24] feat(backup): add copy actions to backup plan rules (#22244) This PR adds the ability to specify copy actions on backup plan rules, to copy recovery points to a different vault. ```ts declare const plan: backup.BackupPlan; declare const secondaryVault: backup.BackupVault plan.addRule(new backup.BackupPlanRule({ copyActions: [{ destinationBackupVault: secondaryVault, moveToColdStorageAfter: Duration.days(30), deleteAfter: Duration.days(120), }] })); ``` The naming and types for `moveToColdStorageAfter` and `deleteAfter` are consistent with the parent `Rule` type to avoid confusion. This closes #22173. Note: While working on this, I discovered that there's a gap in the validation for `Rule` where if both `moveToColdStorageAfter` and `deleteAfter` are specified, then `deleteAfter` must be at least 90 days later than the `moveToColdStorageAfter` point. I've added validation for this in the `copyActions` validation, but I think fixing this for the rest of `Rule` probably warrants a separate bug fix issue and PR. ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *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-backup/README.md | 15 ++++ packages/@aws-cdk/aws-backup/lib/plan.ts | 13 +++- packages/@aws-cdk/aws-backup/lib/rule.ts | 47 ++++++++++++- .../cdk-backup.assets.json | 6 +- .../cdk-backup.template.json | 34 +++++++++ .../test/backup.integ.snapshot/cdk.out | 2 +- .../test/backup.integ.snapshot/integ.json | 2 +- .../test/backup.integ.snapshot/manifest.json | 10 ++- .../test/backup.integ.snapshot/tree.json | 64 +++++++++++++++-- .../@aws-cdk/aws-backup/test/integ.backup.ts | 14 ++++ .../@aws-cdk/aws-backup/test/plan.test.ts | 70 +++++++++++++++++++ 11 files changed, 261 insertions(+), 16 deletions(-) diff --git a/packages/@aws-cdk/aws-backup/README.md b/packages/@aws-cdk/aws-backup/README.md index d15bc5d6b4e30..1bc6cec09476b 100644 --- a/packages/@aws-cdk/aws-backup/README.md +++ b/packages/@aws-cdk/aws-backup/README.md @@ -99,6 +99,21 @@ plan.addRule(new backup.BackupPlanRule({ })); ``` +Rules can also specify to copy recovery points to another Backup Vault using `copyActions`. Copied recovery points can +optionally have `moveToColdStorageAfter` and `deleteAfter` configured. + +```ts +declare const plan: backup.BackupPlan; +declare const secondaryVault: backup.BackupVault; +plan.addRule(new backup.BackupPlanRule({ + copyActions: [{ + destinationBackupVault: secondaryVault, + moveToColdStorageAfter: Duration.days(30), + deleteAfter: Duration.days(120), + }] +})); +``` + Ready-made rules are also available: ```ts diff --git a/packages/@aws-cdk/aws-backup/lib/plan.ts b/packages/@aws-cdk/aws-backup/lib/plan.ts index 632d823727b36..fd80dbb20d6b9 100644 --- a/packages/@aws-cdk/aws-backup/lib/plan.ts +++ b/packages/@aws-cdk/aws-backup/lib/plan.ts @@ -1,7 +1,7 @@ import { IResource, Lazy, Resource } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnBackupPlan } from './backup.generated'; -import { BackupPlanRule } from './rule'; +import { BackupPlanCopyActionProps, BackupPlanRule } from './rule'; import { BackupSelection, BackupSelectionOptions } from './selection'; import { BackupVault, IBackupVault } from './vault'; @@ -191,9 +191,20 @@ export class BackupPlan extends Resource implements IBackupPlan { startWindowMinutes: rule.props.startWindow?.toMinutes(), enableContinuousBackup: rule.props.enableContinuousBackup, targetBackupVault: vault.backupVaultName, + copyActions: rule.props.copyActions?.map(this.planCopyActions), }); } + private planCopyActions(props: BackupPlanCopyActionProps): CfnBackupPlan.CopyActionResourceTypeProperty { + return { + destinationBackupVaultArn: props.destinationBackupVault.backupVaultArn, + lifecycle: (props.deleteAfter || props.moveToColdStorageAfter) && { + deleteAfterDays: props.deleteAfter?.toDays(), + moveToColdStorageAfterDays: props.moveToColdStorageAfter?.toDays(), + }, + }; + } + /** * The backup vault where backups are stored if not defined at * the rule level diff --git a/packages/@aws-cdk/aws-backup/lib/rule.ts b/packages/@aws-cdk/aws-backup/lib/rule.ts index 295d2560911fc..a879fafd17d83 100644 --- a/packages/@aws-cdk/aws-backup/lib/rule.ts +++ b/packages/@aws-cdk/aws-backup/lib/rule.ts @@ -1,5 +1,5 @@ import * as events from '@aws-cdk/aws-events'; -import { Duration } from '@aws-cdk/core'; +import { Duration, Token } from '@aws-cdk/core'; import { IBackupVault } from './vault'; /** @@ -70,6 +70,38 @@ export interface BackupPlanRuleProps { * @default false */ readonly enableContinuousBackup?: boolean; + + /** + * Copy operations to perform on recovery points created by this rule + * + * @default - no copy actions + */ + readonly copyActions?: BackupPlanCopyActionProps[]; +} + +/** + * Properties for a BackupPlanCopyAction + */ +export interface BackupPlanCopyActionProps { + /** + * Destination Vault for recovery points to be copied into + */ + readonly destinationBackupVault: IBackupVault; + + /** + * Specifies the duration after creation that a copied recovery point is deleted from the destination vault. + * Must be at least 90 days greater than `moveToColdStorageAfter`, if specified. + * + * @default - recovery point is never deleted + */ + readonly deleteAfter?: Duration; + + /** + * Specifies the duration after creation that a copied recovery point is moved to cold storage. + * + * @default - recovery point is never moved to cold storage + */ + readonly moveToColdStorageAfter?: Duration; } /** @@ -185,6 +217,19 @@ export class BackupPlanRule { throw new Error(`'deleteAfter' must be between 1 and 35 days if 'enableContinuousBackup' is enabled, but got ${props.deleteAfter.toHumanString()}`); } + if (props.copyActions && props.copyActions.length > 0) { + props.copyActions.forEach(copyAction => { + if (copyAction.deleteAfter && !Token.isUnresolved(copyAction.deleteAfter) && + copyAction.moveToColdStorageAfter && !Token.isUnresolved(copyAction.moveToColdStorageAfter) && + copyAction.deleteAfter.toDays() < copyAction.moveToColdStorageAfter.toDays() + 90) { + throw new Error([ + '\'deleteAfter\' must at least 90 days later than corresponding \'moveToColdStorageAfter\'', + `received 'deleteAfter: ${copyAction.deleteAfter.toDays()}' and 'moveToColdStorageAfter: ${copyAction.moveToColdStorageAfter.toDays()}'`, + ].join('\n')); + } + }); + } + this.props = { ...props, deleteAfter, diff --git a/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/cdk-backup.assets.json b/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/cdk-backup.assets.json index 56aefc86c1e8e..3222720cf6f11 100644 --- a/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/cdk-backup.assets.json +++ b/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/cdk-backup.assets.json @@ -1,7 +1,7 @@ { - "version": "20.0.0", + "version": "21.0.0", "files": { - "8001d34381bcb57b7b2a8fb3ade1e27b0ea7c1819c1d3973537e2cb5aa604ce7": { + "14e034eeffbdd95a18b6c1a8c7a4876e1dfbedde51220bb1a196a337a6848c16": { "source": { "path": "cdk-backup.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "8001d34381bcb57b7b2a8fb3ade1e27b0ea7c1819c1d3973537e2cb5aa604ce7.json", + "objectKey": "14e034eeffbdd95a18b6c1a8c7a4876e1dfbedde51220bb1a196a337a6848c16.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/cdk-backup.template.json b/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/cdk-backup.template.json index 2c36c669d9a98..e2cf214d34d87 100644 --- a/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/cdk-backup.template.json +++ b/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/cdk-backup.template.json @@ -39,6 +39,17 @@ "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, + "SecondaryVault67665B5E": { + "Type": "AWS::Backup::BackupVault", + "Properties": { + "BackupVaultName": "cdkbackupSecondaryVaultA01C2A0E", + "LockConfiguration": { + "MinRetentionDays": 5 + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "PlanDAF4E53A": { "Type": "AWS::Backup::BackupPlan", "Properties": { @@ -84,6 +95,29 @@ "BackupVaultName" ] } + }, + { + "CopyActions": [ + { + "DestinationBackupVaultArn": { + "Fn::GetAtt": [ + "SecondaryVault67665B5E", + "BackupVaultArn" + ] + }, + "Lifecycle": { + "DeleteAfterDays": 120, + "MoveToColdStorageAfterDays": 30 + } + } + ], + "RuleName": "PlanRule3", + "TargetBackupVault": { + "Fn::GetAtt": [ + "Vault23237E5B", + "BackupVaultName" + ] + } } ] } diff --git a/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/cdk.out index 588d7b269d34f..8ecc185e9dbee 100644 --- a/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/cdk.out @@ -1 +1 @@ -{"version":"20.0.0"} \ No newline at end of file +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/integ.json b/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/integ.json index c432d2f16d1fa..dedd47fcf7e75 100644 --- a/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/integ.json +++ b/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "20.0.0", + "version": "21.0.0", "testCases": { "integ.backup": { "stacks": [ diff --git a/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/manifest.json index cce1a09cbac7b..b08a4b8df2d42 100644 --- a/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "20.0.0", + "version": "21.0.0", "artifacts": { "Tree": { "type": "cdk:tree", @@ -23,7 +23,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/8001d34381bcb57b7b2a8fb3ade1e27b0ea7c1819c1d3973537e2cb5aa604ce7.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/14e034eeffbdd95a18b6c1a8c7a4876e1dfbedde51220bb1a196a337a6848c16.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -57,6 +57,12 @@ "data": "Vault23237E5B" } ], + "/cdk-backup/SecondaryVault/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "SecondaryVault67665B5E" + } + ], "/cdk-backup/Plan/Resource": [ { "type": "aws:cdk:logicalId", diff --git a/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/tree.json b/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/tree.json index 37053efb47cae..2b8102553e225 100644 --- a/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/tree.json +++ b/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/tree.json @@ -9,7 +9,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.85" + "version": "10.1.102" } }, "cdk-backup": { @@ -53,8 +53,8 @@ "id": "ScalingRole", "path": "cdk-backup/Table/ScalingRole", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" } } }, @@ -102,6 +102,33 @@ "version": "0.0.0" } }, + "SecondaryVault": { + "id": "SecondaryVault", + "path": "cdk-backup/SecondaryVault", + "children": { + "Resource": { + "id": "Resource", + "path": "cdk-backup/SecondaryVault/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Backup::BackupVault", + "aws:cdk:cloudformation:props": { + "backupVaultName": "cdkbackupSecondaryVaultA01C2A0E", + "lockConfiguration": { + "minRetentionDays": 5 + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-backup.CfnBackupVault", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-backup.BackupVault", + "version": "0.0.0" + } + }, "Plan": { "id": "Plan", "path": "cdk-backup/Plan", @@ -154,6 +181,29 @@ "BackupVaultName" ] } + }, + { + "ruleName": "PlanRule3", + "targetBackupVault": { + "Fn::GetAtt": [ + "Vault23237E5B", + "BackupVaultName" + ] + }, + "copyActions": [ + { + "destinationBackupVaultArn": { + "Fn::GetAtt": [ + "SecondaryVault67665B5E", + "BackupVaultArn" + ] + }, + "lifecycle": { + "deleteAfterDays": 120, + "moveToColdStorageAfterDays": 30 + } + } + ] } ] } @@ -314,14 +364,14 @@ } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-backup/test/integ.backup.ts b/packages/@aws-cdk/aws-backup/test/integ.backup.ts index 57de1d51750fc..ef1bbff7251b2 100644 --- a/packages/@aws-cdk/aws-backup/test/integ.backup.ts +++ b/packages/@aws-cdk/aws-backup/test/integ.backup.ts @@ -25,6 +25,12 @@ class TestStack extends Stack { minRetention: Duration.days(5), }, }); + const secondaryVault = new backup.BackupVault(this, 'SecondaryVault', { + removalPolicy: RemovalPolicy.DESTROY, + lockConfiguration: { + minRetention: Duration.days(5), + }, + }); const plan = backup.BackupPlan.dailyWeeklyMonthly5YearRetention(this, 'Plan', vault); plan.addSelection('Selection', { @@ -33,6 +39,14 @@ class TestStack extends Stack { backup.BackupResource.fromTag('stage', 'prod'), // Resources that are tagged stage=prod ], }); + + plan.addRule(new backup.BackupPlanRule({ + copyActions: [{ + destinationBackupVault: secondaryVault, + moveToColdStorageAfter: Duration.days(30), + deleteAfter: Duration.days(120), + }], + })); } } diff --git a/packages/@aws-cdk/aws-backup/test/plan.test.ts b/packages/@aws-cdk/aws-backup/test/plan.test.ts index 77674a8223fb9..80f3ed4321427 100644 --- a/packages/@aws-cdk/aws-backup/test/plan.test.ts +++ b/packages/@aws-cdk/aws-backup/test/plan.test.ts @@ -286,6 +286,56 @@ test('automatically creates a new vault', () => { }); }); +test('create a plan and add rule to copy to a different vault', () => { + // GIVEN + const primaryVault = new BackupVault(stack, 'PrimaryVault'); + const secondaryVault = new BackupVault(stack, 'SecondaryVault'); + + // WHEN + new BackupPlan(stack, 'Plan', { + backupVault: primaryVault, + backupPlanRules: [ + new BackupPlanRule({ + copyActions: [{ + destinationBackupVault: secondaryVault, + deleteAfter: Duration.days(120), + moveToColdStorageAfter: Duration.days(30), + }], + }), + ], + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Backup::BackupPlan', { + BackupPlan: { + BackupPlanName: 'Plan', + BackupPlanRule: [ + { + RuleName: 'PlanRule0', + TargetBackupVault: { + 'Fn::GetAtt': [ + 'PrimaryVault9BBEBB0D', + 'BackupVaultName', + ], + }, + CopyActions: [{ + DestinationBackupVaultArn: { + 'Fn::GetAtt': [ + 'SecondaryVault67665B5E', + 'BackupVaultArn', + ], + }, + Lifecycle: { + DeleteAfterDays: 120, + MoveToColdStorageAfterDays: 30, + }, + }], + }, + ], + }, + }); +}); + test('throws when deleteAfter is not greater than moveToColdStorageAfter', () => { expect(() => new BackupPlanRule({ deleteAfter: Duration.days(5), @@ -331,3 +381,23 @@ test('throws when deleteAfter is greater than 35 in combination with enableConti deleteAfter: Duration.days(36), })).toThrow(/'deleteAfter' must be between 1 and 35 days if 'enableContinuousBackup' is enabled, but got 36 days/); }); + +test('throws when deleteAfter is not greater than moveToColdStorageAfter in a copy action', () => { + expect(() => new BackupPlanRule({ + copyActions: [{ + destinationBackupVault: new BackupVault(stack, 'Vault'), + deleteAfter: Duration.days(5), + moveToColdStorageAfter: Duration.days(6), + }], + })).toThrow(/deleteAfter' must at least 90 days later than corresponding 'moveToColdStorageAfter'\nreceived 'deleteAfter: 5' and 'moveToColdStorageAfter: 6'/); +}); + +test('throws when deleteAfter is not greater than 90 days past moveToColdStorageAfter parameter in a copy action', () => { + expect(() => new BackupPlanRule({ + copyActions: [{ + destinationBackupVault: new BackupVault(stack, 'Vault'), + deleteAfter: Duration.days(45), + moveToColdStorageAfter: Duration.days(30), + }], + })).toThrow(/'deleteAfter' must at least 90 days later than corresponding 'moveToColdStorageAfter'\nreceived 'deleteAfter: 45' and 'moveToColdStorageAfter: 30'/); +}); From e4e0fa77f46ffc73a54cb6f1e37e75786eceb647 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy <36202692+kaizencc@users.noreply.github.com> Date: Tue, 27 Sep 2022 16:37:47 -0400 Subject: [PATCH 16/24] chore: ignore bots in merit badger (#22252) dependabot is still a `distinguished contributor`. I'm convinced that this will solve it. --- .github/workflows/github-merit-badger.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/github-merit-badger.yml b/.github/workflows/github-merit-badger.yml index 71c2e3c5fa0a0..c7c2cfd3fdaa6 100644 --- a/.github/workflows/github-merit-badger.yml +++ b/.github/workflows/github-merit-badger.yml @@ -6,7 +6,6 @@ on: jobs: call-action: - if: ${{ github.actor != 'dependabot[bot]' }} && ${{ github.actor != 'mergify[bot]' }} runs-on: ubuntu-latest permissions: pull-requests: write @@ -18,4 +17,4 @@ jobs: badges: '[beginning-contributor,repeat-contributor,valued-contributor,admired-contributor,star-contributor,distinguished-contributor]' thresholds: '[0,3,6,13,25,50]' badge-type: 'achievement' - ignore-usernames: '[RomainMuller,rix0rrr,Jerry-AWS,MrArnoldPalmer,iliapolo,otaviomacedo,madeline-k,kaizencc,comcalvi,corymhall,peterwoodworth,ryparker,TheRealAmazonKendra,vinayak-kukreja,Naumel,mrgrain,pahud,cgarvis,aws-cdk-automation]' + ignore-usernames: '[RomainMuller,rix0rrr,Jerry-AWS,MrArnoldPalmer,iliapolo,otaviomacedo,madeline-k,kaizencc,comcalvi,corymhall,peterwoodworth,ryparker,TheRealAmazonKendra,vinayak-kukreja,Naumel,mrgrain,pahud,cgarvis,aws-cdk-automation,dependabot[bot],mergify[bot]]' From 40ddfdd681b5fb3d06e5fe3ea5d38e8d286e7218 Mon Sep 17 00:00:00 2001 From: Mitchell Valine Date: Tue, 27 Sep 2022 14:55:08 -0700 Subject: [PATCH 17/24] chore: remove usage of Node12 runtime in tests (#22256) Removes usage of NodeJS 12 runtime in some of our tests in preparation for deprecation of the runtime. Tests already cover Node 14 and 16 so removal is all that was necessary. ---- ### All Submissions: * [X] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../test/integ.runtime.inlinecode.ts | 7 -- ...-cdk-lambda-runtime-inlinecode.assets.json | 4 +- ...dk-lambda-runtime-inlinecode.template.json | 55 ----------- .../manifest.json | 38 ++++---- .../tree.json | 93 +------------------ 5 files changed, 22 insertions(+), 175 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/test/integ.runtime.inlinecode.ts b/packages/@aws-cdk/aws-lambda/test/integ.runtime.inlinecode.ts index 25177767ca2b3..cc15712cc4d1e 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.runtime.inlinecode.ts +++ b/packages/@aws-cdk/aws-lambda/test/integ.runtime.inlinecode.ts @@ -15,13 +15,6 @@ const app = new App(); const stack = new Stack(app, 'aws-cdk-lambda-runtime-inlinecode'); -const node12xfn = new Function(stack, 'NODEJS_12_X', { - code: new InlineCode('exports.handler = async function(event) { return "success" }'), - handler: 'index.handler', - runtime: Runtime.NODEJS_12_X, -}); -new CfnOutput(stack, 'NODEJS_12_X-functionName', { value: node12xfn.functionName }); - const python37 = new Function(stack, 'PYTHON_3_7', { code: new InlineCode('def handler(event, context):\n return "success"'), handler: 'index.handler', diff --git a/packages/@aws-cdk/aws-lambda/test/runtime.inlinecode.integ.snapshot/aws-cdk-lambda-runtime-inlinecode.assets.json b/packages/@aws-cdk/aws-lambda/test/runtime.inlinecode.integ.snapshot/aws-cdk-lambda-runtime-inlinecode.assets.json index ab7c438558f12..4eb46a8ef9093 100644 --- a/packages/@aws-cdk/aws-lambda/test/runtime.inlinecode.integ.snapshot/aws-cdk-lambda-runtime-inlinecode.assets.json +++ b/packages/@aws-cdk/aws-lambda/test/runtime.inlinecode.integ.snapshot/aws-cdk-lambda-runtime-inlinecode.assets.json @@ -1,7 +1,7 @@ { "version": "21.0.0", "files": { - "fc0190b17e2248f645e6fb64b3fcc344774325a53c839f3e3d06cf7c1ba8b516": { + "33c56e02291eaea4afa147afc5d42ce6d80c4f482906b78fa47ddbe2582f19bc": { "source": { "path": "aws-cdk-lambda-runtime-inlinecode.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "fc0190b17e2248f645e6fb64b3fcc344774325a53c839f3e3d06cf7c1ba8b516.json", + "objectKey": "33c56e02291eaea4afa147afc5d42ce6d80c4f482906b78fa47ddbe2582f19bc.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk/aws-lambda/test/runtime.inlinecode.integ.snapshot/aws-cdk-lambda-runtime-inlinecode.template.json b/packages/@aws-cdk/aws-lambda/test/runtime.inlinecode.integ.snapshot/aws-cdk-lambda-runtime-inlinecode.template.json index f662b0219245b..373feab3c118f 100644 --- a/packages/@aws-cdk/aws-lambda/test/runtime.inlinecode.integ.snapshot/aws-cdk-lambda-runtime-inlinecode.template.json +++ b/packages/@aws-cdk/aws-lambda/test/runtime.inlinecode.integ.snapshot/aws-cdk-lambda-runtime-inlinecode.template.json @@ -1,55 +1,5 @@ { "Resources": { - "NODEJS12XServiceRole59E71436": { - "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" - ] - ] - } - ] - } - }, - "NODEJS12X8B8075A4": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "ZipFile": "exports.handler = async function(event) { return \"success\" }" - }, - "Role": { - "Fn::GetAtt": [ - "NODEJS12XServiceRole59E71436", - "Arn" - ] - }, - "Handler": "index.handler", - "Runtime": "nodejs12.x" - }, - "DependsOn": [ - "NODEJS12XServiceRole59E71436" - ] - }, "PYTHON37ServiceRoleDE7E561E": { "Type": "AWS::IAM::Role", "Properties": { @@ -302,11 +252,6 @@ } }, "Outputs": { - "NODEJS12XfunctionName": { - "Value": { - "Ref": "NODEJS12X8B8075A4" - } - }, "PYTHON37functionName": { "Value": { "Ref": "PYTHON37D3A10E04" diff --git a/packages/@aws-cdk/aws-lambda/test/runtime.inlinecode.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-lambda/test/runtime.inlinecode.integ.snapshot/manifest.json index 73c76fe8d3f9c..73f920768ee1a 100644 --- a/packages/@aws-cdk/aws-lambda/test/runtime.inlinecode.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-lambda/test/runtime.inlinecode.integ.snapshot/manifest.json @@ -23,7 +23,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/fc0190b17e2248f645e6fb64b3fcc344774325a53c839f3e3d06cf7c1ba8b516.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/33c56e02291eaea4afa147afc5d42ce6d80c4f482906b78fa47ddbe2582f19bc.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -39,24 +39,6 @@ "aws-cdk-lambda-runtime-inlinecode.assets" ], "metadata": { - "/aws-cdk-lambda-runtime-inlinecode/NODEJS_12_X/ServiceRole/Resource": [ - { - "type": "aws:cdk:logicalId", - "data": "NODEJS12XServiceRole59E71436" - } - ], - "/aws-cdk-lambda-runtime-inlinecode/NODEJS_12_X/Resource": [ - { - "type": "aws:cdk:logicalId", - "data": "NODEJS12X8B8075A4" - } - ], - "/aws-cdk-lambda-runtime-inlinecode/NODEJS_12_X-functionName": [ - { - "type": "aws:cdk:logicalId", - "data": "NODEJS12XfunctionName" - } - ], "/aws-cdk-lambda-runtime-inlinecode/PYTHON_3_7/ServiceRole/Resource": [ { "type": "aws:cdk:logicalId", @@ -158,6 +140,24 @@ "type": "aws:cdk:logicalId", "data": "CheckBootstrapVersion" } + ], + "NODEJS12XServiceRole59E71436": [ + { + "type": "aws:cdk:logicalId", + "data": "NODEJS12XServiceRole59E71436", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] + } + ], + "NODEJS12X8B8075A4": [ + { + "type": "aws:cdk:logicalId", + "data": "NODEJS12X8B8075A4", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] + } ] }, "displayName": "aws-cdk-lambda-runtime-inlinecode" diff --git a/packages/@aws-cdk/aws-lambda/test/runtime.inlinecode.integ.snapshot/tree.json b/packages/@aws-cdk/aws-lambda/test/runtime.inlinecode.integ.snapshot/tree.json index ba8003992ebeb..42a8a90058e96 100644 --- a/packages/@aws-cdk/aws-lambda/test/runtime.inlinecode.integ.snapshot/tree.json +++ b/packages/@aws-cdk/aws-lambda/test/runtime.inlinecode.integ.snapshot/tree.json @@ -9,104 +9,13 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.85" + "version": "10.1.108" } }, "aws-cdk-lambda-runtime-inlinecode": { "id": "aws-cdk-lambda-runtime-inlinecode", "path": "aws-cdk-lambda-runtime-inlinecode", "children": { - "NODEJS_12_X": { - "id": "NODEJS_12_X", - "path": "aws-cdk-lambda-runtime-inlinecode/NODEJS_12_X", - "children": { - "ServiceRole": { - "id": "ServiceRole", - "path": "aws-cdk-lambda-runtime-inlinecode/NODEJS_12_X/ServiceRole", - "children": { - "Resource": { - "id": "Resource", - "path": "aws-cdk-lambda-runtime-inlinecode/NODEJS_12_X/ServiceRole/Resource", - "attributes": { - "aws:cdk:cloudformation:type": "AWS::IAM::Role", - "aws:cdk:cloudformation:props": { - "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" - ] - ] - } - ] - } - }, - "constructInfo": { - "fqn": "@aws-cdk/aws-iam.CfnRole", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "@aws-cdk/aws-iam.Role", - "version": "0.0.0" - } - }, - "Resource": { - "id": "Resource", - "path": "aws-cdk-lambda-runtime-inlinecode/NODEJS_12_X/Resource", - "attributes": { - "aws:cdk:cloudformation:type": "AWS::Lambda::Function", - "aws:cdk:cloudformation:props": { - "code": { - "zipFile": "exports.handler = async function(event) { return \"success\" }" - }, - "role": { - "Fn::GetAtt": [ - "NODEJS12XServiceRole59E71436", - "Arn" - ] - }, - "handler": "index.handler", - "runtime": "nodejs12.x" - } - }, - "constructInfo": { - "fqn": "@aws-cdk/aws-lambda.CfnFunction", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "@aws-cdk/aws-lambda.Function", - "version": "0.0.0" - } - }, - "NODEJS_12_X-functionName": { - "id": "NODEJS_12_X-functionName", - "path": "aws-cdk-lambda-runtime-inlinecode/NODEJS_12_X-functionName", - "constructInfo": { - "fqn": "@aws-cdk/core.CfnOutput", - "version": "0.0.0" - } - }, "PYTHON_3_7": { "id": "PYTHON_3_7", "path": "aws-cdk-lambda-runtime-inlinecode/PYTHON_3_7", From c425e8ca1a3d296eb6a7fd7e005d07c1eadd16aa Mon Sep 17 00:00:00 2001 From: Calvin Combs <66279577+comcalvi@users.noreply.github.com> Date: Tue, 27 Sep 2022 23:10:31 -0600 Subject: [PATCH 18/24] fix(cli): SSO credentials do not work when using a proxy (#22115) Passes `httpOptions` through to the SDK, which now recognizes `httpOptions`. Enables SSO to work with proxies. This was tested manually. Fixes #21328. ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *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-amplify/package.json | 2 +- packages/@aws-cdk/aws-apigateway/package.json | 1 - packages/@aws-cdk/aws-cloudfront-origins/package.json | 2 +- packages/@aws-cdk/aws-cloudfront/package.json | 2 +- packages/@aws-cdk/aws-cloudtrail/package.json | 2 +- packages/@aws-cdk/aws-codebuild/package.json | 2 +- packages/@aws-cdk/aws-codecommit/package.json | 2 +- packages/@aws-cdk/aws-dynamodb/package.json | 2 +- packages/@aws-cdk/aws-eks/package.json | 2 +- packages/@aws-cdk/aws-events-targets/package.json | 2 +- packages/@aws-cdk/aws-events/package.json | 2 -- .../aws-globalaccelerator-endpoints/package.json | 2 +- packages/@aws-cdk/aws-logs/package.json | 2 +- packages/@aws-cdk/aws-redshift/package.json | 2 +- packages/@aws-cdk/aws-route53/package.json | 2 +- packages/@aws-cdk/aws-sqs/package.json | 2 +- packages/@aws-cdk/custom-resources/package.json | 2 +- packages/@aws-cdk/integ-runner/THIRD_PARTY_LICENSES | 2 +- packages/@aws-cdk/integ-tests/package.json | 2 +- packages/@aws-cdk/pipelines/package.json | 2 +- packages/@aws-cdk/triggers/package.json | 2 +- packages/aws-cdk/THIRD_PARTY_LICENSES | 2 +- .../aws-cdk/lib/api/aws-auth/awscli-compatible.ts | 11 +++++++---- packages/aws-cdk/package.json | 2 +- packages/cdk-assets/package.json | 2 +- yarn.lock | 8 ++++---- 26 files changed, 33 insertions(+), 33 deletions(-) diff --git a/packages/@aws-cdk/aws-amplify/package.json b/packages/@aws-cdk/aws-amplify/package.json index 93f42ccab64cc..f14005fc7c46f 100644 --- a/packages/@aws-cdk/aws-amplify/package.json +++ b/packages/@aws-cdk/aws-amplify/package.json @@ -88,7 +88,7 @@ "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", "@types/jest": "^27.5.2", - "aws-sdk": "^2.848.0" + "aws-sdk": "^2.1211.0" }, "dependencies": { "@aws-cdk/aws-codebuild": "0.0.0", diff --git a/packages/@aws-cdk/aws-apigateway/package.json b/packages/@aws-cdk/aws-apigateway/package.json index 03ffea77f6e62..b6f3c28297160 100644 --- a/packages/@aws-cdk/aws-apigateway/package.json +++ b/packages/@aws-cdk/aws-apigateway/package.json @@ -84,7 +84,6 @@ "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/integ-runner": "0.0.0", - "@aws-cdk/integ-tests": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", "@types/jest": "^27.5.2" diff --git a/packages/@aws-cdk/aws-cloudfront-origins/package.json b/packages/@aws-cdk/aws-cloudfront-origins/package.json index 143fca34ea916..030de64af9b5e 100644 --- a/packages/@aws-cdk/aws-cloudfront-origins/package.json +++ b/packages/@aws-cdk/aws-cloudfront-origins/package.json @@ -85,7 +85,7 @@ "@aws-cdk/integ-tests": "0.0.0", "@aws-cdk/pkglint": "0.0.0", "@types/jest": "^27.5.2", - "aws-sdk": "^2.848.0" + "aws-sdk": "^2.1211.0" }, "dependencies": { "@aws-cdk/aws-apigateway": "0.0.0", diff --git a/packages/@aws-cdk/aws-cloudfront/package.json b/packages/@aws-cdk/aws-cloudfront/package.json index 7268f2b59cb50..3eb58a20b10c7 100644 --- a/packages/@aws-cdk/aws-cloudfront/package.json +++ b/packages/@aws-cdk/aws-cloudfront/package.json @@ -87,7 +87,7 @@ "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", "@types/jest": "^27.5.2", - "aws-sdk": "^2.848.0", + "aws-sdk": "^2.1211.0", "jest": "^27.5.1" }, "dependencies": { diff --git a/packages/@aws-cdk/aws-cloudtrail/package.json b/packages/@aws-cdk/aws-cloudtrail/package.json index 3363e15dafe52..17df9eddd0812 100644 --- a/packages/@aws-cdk/aws-cloudtrail/package.json +++ b/packages/@aws-cdk/aws-cloudtrail/package.json @@ -87,7 +87,7 @@ "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", "@types/jest": "^27.5.2", - "aws-sdk": "^2.848.0", + "aws-sdk": "^2.1211.0", "jest": "^27.5.1" }, "dependencies": { diff --git a/packages/@aws-cdk/aws-codebuild/package.json b/packages/@aws-cdk/aws-codebuild/package.json index b9c15e0b82297..bf7fc7931f72f 100644 --- a/packages/@aws-cdk/aws-codebuild/package.json +++ b/packages/@aws-cdk/aws-codebuild/package.json @@ -93,7 +93,7 @@ "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", "@types/jest": "^27.5.2", - "aws-sdk": "^2.848.0", + "aws-sdk": "^2.1211.0", "jest": "^27.5.1" }, "dependencies": { diff --git a/packages/@aws-cdk/aws-codecommit/package.json b/packages/@aws-cdk/aws-codecommit/package.json index e115ab261cf07..bde04cf3ebe28 100644 --- a/packages/@aws-cdk/aws-codecommit/package.json +++ b/packages/@aws-cdk/aws-codecommit/package.json @@ -93,7 +93,7 @@ "@aws-cdk/cloud-assembly-schema": "0.0.0", "@aws-cdk/pkglint": "0.0.0", "@types/jest": "^27.5.2", - "aws-sdk": "^2.848.0", + "aws-sdk": "^2.1211.0", "jest": "^27.5.1" }, "dependencies": { diff --git a/packages/@aws-cdk/aws-dynamodb/package.json b/packages/@aws-cdk/aws-dynamodb/package.json index 7f39e462f3158..06cc628f7d660 100644 --- a/packages/@aws-cdk/aws-dynamodb/package.json +++ b/packages/@aws-cdk/aws-dynamodb/package.json @@ -89,7 +89,7 @@ "@types/aws-lambda": "^8.10.104", "@types/jest": "^27.5.2", "@types/sinon": "^9.0.11", - "aws-sdk": "^2.848.0", + "aws-sdk": "^2.1211.0", "aws-sdk-mock": "5.6.0", "jest": "^27.5.1", "sinon": "^9.2.4", diff --git a/packages/@aws-cdk/aws-eks/package.json b/packages/@aws-cdk/aws-eks/package.json index c7d323a3dff64..ee31ab3715eb3 100644 --- a/packages/@aws-cdk/aws-eks/package.json +++ b/packages/@aws-cdk/aws-eks/package.json @@ -90,7 +90,7 @@ "@types/jest": "^27.5.2", "@types/sinon": "^9.0.11", "@types/yaml": "1.9.6", - "aws-sdk": "^2.848.0", + "aws-sdk": "^2.1211.0", "cdk8s": "^2.4.33", "cdk8s-plus-21": "^2.0.0-beta.12", "jest": "^27.5.1", diff --git a/packages/@aws-cdk/aws-events-targets/package.json b/packages/@aws-cdk/aws-events-targets/package.json index 4ea0fc4de7f0b..70c65b6b65abc 100644 --- a/packages/@aws-cdk/aws-events-targets/package.json +++ b/packages/@aws-cdk/aws-events-targets/package.json @@ -89,7 +89,7 @@ "@aws-cdk/integ-runner": "0.0.0", "@aws-cdk/pkglint": "0.0.0", "@types/jest": "^27.5.2", - "aws-sdk": "^2.848.0", + "aws-sdk": "^2.1211.0", "aws-sdk-mock": "5.6.0", "jest": "^27.5.1" }, diff --git a/packages/@aws-cdk/aws-events/package.json b/packages/@aws-cdk/aws-events/package.json index be6e389a3bc0e..072721b200824 100644 --- a/packages/@aws-cdk/aws-events/package.json +++ b/packages/@aws-cdk/aws-events/package.json @@ -87,8 +87,6 @@ "@aws-cdk/integ-runner": "0.0.0", "@aws-cdk/integ-tests": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", - "@aws-cdk/integ-runner": "0.0.0", - "@aws-cdk/integ-tests": "0.0.0", "@aws-cdk/pkglint": "0.0.0", "@types/jest": "^27.5.2", "jest": "^27.5.1" diff --git a/packages/@aws-cdk/aws-globalaccelerator-endpoints/package.json b/packages/@aws-cdk/aws-globalaccelerator-endpoints/package.json index 6039144f28c3c..945f8cd6ea48d 100644 --- a/packages/@aws-cdk/aws-globalaccelerator-endpoints/package.json +++ b/packages/@aws-cdk/aws-globalaccelerator-endpoints/package.json @@ -81,7 +81,7 @@ "@aws-cdk/integ-runner": "0.0.0", "@aws-cdk/pkglint": "0.0.0", "@types/jest": "^27.5.2", - "aws-sdk": "^2.848.0", + "aws-sdk": "^2.1211.0", "aws-sdk-mock": "5.6.0", "jest": "^27.5.1" }, diff --git a/packages/@aws-cdk/aws-logs/package.json b/packages/@aws-cdk/aws-logs/package.json index 130e85cc553cd..d74bc2de6aa09 100644 --- a/packages/@aws-cdk/aws-logs/package.json +++ b/packages/@aws-cdk/aws-logs/package.json @@ -89,7 +89,7 @@ "@types/aws-lambda": "^8.10.104", "@types/jest": "^27.5.2", "@types/sinon": "^9.0.11", - "aws-sdk": "^2.848.0", + "aws-sdk": "^2.1211.0", "aws-sdk-mock": "5.6.0", "jest": "^27.5.1", "nock": "^13.2.9", diff --git a/packages/@aws-cdk/aws-redshift/package.json b/packages/@aws-cdk/aws-redshift/package.json index 017153eab6a0d..7d3a9b832f79e 100644 --- a/packages/@aws-cdk/aws-redshift/package.json +++ b/packages/@aws-cdk/aws-redshift/package.json @@ -87,7 +87,7 @@ "@aws-cdk/pkglint": "0.0.0", "@aws-cdk/integ-tests": "0.0.0", "@types/jest": "^27.5.2", - "aws-sdk": "^2.848.0", + "aws-sdk": "^2.1211.0", "jest": "^27.5.1" }, "dependencies": { diff --git a/packages/@aws-cdk/aws-route53/package.json b/packages/@aws-cdk/aws-route53/package.json index 290d3bbfb8a1b..ddc18b7c5ef3d 100644 --- a/packages/@aws-cdk/aws-route53/package.json +++ b/packages/@aws-cdk/aws-route53/package.json @@ -87,7 +87,7 @@ "@aws-cdk/pkglint": "0.0.0", "@types/aws-lambda": "^8.10.104", "@types/jest": "^27.5.2", - "aws-sdk": "^2.848.0", + "aws-sdk": "^2.1211.0", "jest": "^27.5.1" }, "dependencies": { diff --git a/packages/@aws-cdk/aws-sqs/package.json b/packages/@aws-cdk/aws-sqs/package.json index 9dac4bc5cc743..04f5057ddb54f 100644 --- a/packages/@aws-cdk/aws-sqs/package.json +++ b/packages/@aws-cdk/aws-sqs/package.json @@ -87,7 +87,7 @@ "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", "@types/jest": "^27.5.2", - "aws-sdk": "^2.848.0", + "aws-sdk": "^2.1211.0", "jest": "^27.5.1" }, "dependencies": { diff --git a/packages/@aws-cdk/custom-resources/package.json b/packages/@aws-cdk/custom-resources/package.json index 8f45d7ea2d7ea..1af94d610e99b 100644 --- a/packages/@aws-cdk/custom-resources/package.json +++ b/packages/@aws-cdk/custom-resources/package.json @@ -93,7 +93,7 @@ "@types/fs-extra": "^8.1.2", "@types/jest": "^27.5.2", "@types/sinon": "^9.0.11", - "aws-sdk": "^2.848.0", + "aws-sdk": "^2.1211.0", "aws-sdk-mock": "5.6.0", "fs-extra": "^9.1.0", "nock": "^13.2.9", diff --git a/packages/@aws-cdk/integ-runner/THIRD_PARTY_LICENSES b/packages/@aws-cdk/integ-runner/THIRD_PARTY_LICENSES index 01288804a3ecc..b8726a7138d0e 100644 --- a/packages/@aws-cdk/integ-runner/THIRD_PARTY_LICENSES +++ b/packages/@aws-cdk/integ-runner/THIRD_PARTY_LICENSES @@ -156,7 +156,7 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH RE ---------------- -** aws-sdk@2.1220.0 - https://www.npmjs.com/package/aws-sdk/v/2.1220.0 | Apache-2.0 +** aws-sdk@2.1219.0 - https://www.npmjs.com/package/aws-sdk/v/2.1219.0 | Apache-2.0 AWS SDK for JavaScript Copyright 2012-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/@aws-cdk/integ-tests/package.json b/packages/@aws-cdk/integ-tests/package.json index 477d73321010a..762647ff39582 100644 --- a/packages/@aws-cdk/integ-tests/package.json +++ b/packages/@aws-cdk/integ-tests/package.json @@ -68,7 +68,7 @@ "@types/fs-extra": "^8.1.2", "@types/jest": "^27.5.2", "@types/node": "^14.18.29", - "aws-sdk": "^2.1093.0", + "aws-sdk": "^2.1211.0", "aws-sdk-mock": "5.6.0", "jest": "^27.5.1", "nock": "^13.2.9", diff --git a/packages/@aws-cdk/pipelines/package.json b/packages/@aws-cdk/pipelines/package.json index 8737aaf3b2008..286070734f9e0 100644 --- a/packages/@aws-cdk/pipelines/package.json +++ b/packages/@aws-cdk/pipelines/package.json @@ -51,7 +51,7 @@ "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", "@types/jest": "^27.5.2", - "aws-sdk": "^2.848.0" + "aws-sdk": "^2.1211.0" }, "peerDependencies": { "@aws-cdk/aws-codebuild": "0.0.0", diff --git a/packages/@aws-cdk/triggers/package.json b/packages/@aws-cdk/triggers/package.json index 3bb54b26788ed..e3d732a28e934 100644 --- a/packages/@aws-cdk/triggers/package.json +++ b/packages/@aws-cdk/triggers/package.json @@ -77,7 +77,7 @@ "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/integ-runner": "0.0.0", "@aws-cdk/aws-sns": "0.0.0", - "aws-sdk": "^2.848.0", + "aws-sdk": "^2.1211.0", "@aws-cdk/pkglint": "0.0.0", "@types/jest": "^27.5.2", "jest": "^27.5.1" diff --git a/packages/aws-cdk/THIRD_PARTY_LICENSES b/packages/aws-cdk/THIRD_PARTY_LICENSES index e8c1e3af5684b..e01cf25bb95c8 100644 --- a/packages/aws-cdk/THIRD_PARTY_LICENSES +++ b/packages/aws-cdk/THIRD_PARTY_LICENSES @@ -268,7 +268,7 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH RE ---------------- -** aws-sdk@2.1220.0 - https://www.npmjs.com/package/aws-sdk/v/2.1220.0 | Apache-2.0 +** aws-sdk@2.1219.0 - https://www.npmjs.com/package/aws-sdk/v/2.1219.0 | Apache-2.0 AWS SDK for JavaScript Copyright 2012-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/aws-cdk/lib/api/aws-auth/awscli-compatible.ts b/packages/aws-cdk/lib/api/aws-auth/awscli-compatible.ts index d7fe6cf902f5b..6999c892779cf 100644 --- a/packages/aws-cdk/lib/api/aws-auth/awscli-compatible.ts +++ b/packages/aws-cdk/lib/api/aws-auth/awscli-compatible.ts @@ -41,7 +41,7 @@ export class AwsCliCompatible { // we use that to the exclusion of everything else (note: this does not apply // to AWS_PROFILE, environment credentials still take precedence over AWS_PROFILE) if (options.profile) { - return new AWS.CredentialProviderChain(iniFileCredentialFactories(options.profile)); + return new AWS.CredentialProviderChain(iniFileCredentialFactories(options.profile, options.httpOptions)); } const implicitProfile = process.env.AWS_PROFILE || process.env.AWS_DEFAULT_PROFILE || 'default'; @@ -49,7 +49,7 @@ export class AwsCliCompatible { const sources = [ () => new AWS.EnvironmentCredentials('AWS'), () => new AWS.EnvironmentCredentials('AMAZON'), - ...iniFileCredentialFactories(implicitProfile), + ...iniFileCredentialFactories(implicitProfile, options.httpOptions), ]; if (options.containerCreds ?? hasEcsCredentials()) { @@ -75,10 +75,13 @@ export class AwsCliCompatible { }); } - function iniFileCredentialFactories(theProfile: string) { + function iniFileCredentialFactories(theProfile: string, theHttpOptions?: AWS.HTTPOptions) { return [ () => profileCredentials(theProfile), - () => new AWS.SsoCredentials({ profile: theProfile }), + () => new AWS.SsoCredentials({ + profile: theProfile, + httpOptions: theHttpOptions, + }), () => new AWS.ProcessCredentials({ profile: theProfile }), ]; } diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index 2865fd762751d..dcd2f52dd01df 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -97,7 +97,7 @@ "@aws-cdk/region-info": "0.0.0", "@jsii/check-node": "1.68.0", "archiver": "^5.3.1", - "aws-sdk": "^2.1093.0", + "aws-sdk": "^2.1211.0", "camelcase": "^6.3.0", "cdk-assets": "0.0.0", "chokidar": "^3.5.3", diff --git a/packages/cdk-assets/package.json b/packages/cdk-assets/package.json index b05136e5d93c6..77dd3bd010629 100644 --- a/packages/cdk-assets/package.json +++ b/packages/cdk-assets/package.json @@ -47,7 +47,7 @@ "@aws-cdk/cloud-assembly-schema": "0.0.0", "@aws-cdk/cx-api": "0.0.0", "archiver": "^5.3.1", - "aws-sdk": "^2.1093.0", + "aws-sdk": "^2.1211.0", "glob": "^7.2.3", "mime": "^2.6.0", "yargs": "^16.2.0" diff --git a/yarn.lock b/yarn.lock index 921f5114273de..a1aa3e8496b03 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2626,10 +2626,10 @@ aws-sdk-mock@5.6.0: sinon "^11.1.1" traverse "^0.6.6" -aws-sdk@^2.1093.0, aws-sdk@^2.596.0, aws-sdk@^2.848.0, aws-sdk@^2.928.0: - version "2.1220.0" - resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1220.0.tgz#5cec163ee7799875e484afcdde93c4b1e713c64c" - integrity sha512-l8KEOo2Cb066lLPhVDucx6JuNUrMVmZW937htKagf8rcADCbM0ySIGGKWbi2E8l44ptX3/tDOFnyiSislolATQ== +aws-sdk@^2.1211.0, aws-sdk@^2.596.0, aws-sdk@^2.928.0: + version "2.1219.0" + resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1219.0.tgz#96d056fc4ebfd8417308f18a34f127dbaafc022e" + integrity sha512-KOGA0E3wZ/Zom1VDAd4ttsaq2LAVECXdHUs/i8OyJkuR3vSvmKQa/BOH4baIBNt4VMS062FhPA29UtT1YPTlwQ== dependencies: buffer "4.9.2" events "1.1.1" From a8f63124c6ec8130071950f1d9c765ac6631409b Mon Sep 17 00:00:00 2001 From: Richard Li <742829+rli@users.noreply.github.com> Date: Wed, 28 Sep 2022 05:31:30 -0700 Subject: [PATCH 19/24] chore(codebuild): add support for Windows Server Core 2019 2.0 CodeBuild image (#22259) Available per docs: https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-available.html ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *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/lib/project.ts | 10 ++++++++++ packages/@aws-cdk/aws-codebuild/test/project.test.ts | 1 + 2 files changed, 11 insertions(+) diff --git a/packages/@aws-cdk/aws-codebuild/lib/project.ts b/packages/@aws-cdk/aws-codebuild/lib/project.ts index af1404692a582..dedad72aa99fe 100644 --- a/packages/@aws-cdk/aws-codebuild/lib/project.ts +++ b/packages/@aws-cdk/aws-codebuild/lib/project.ts @@ -1925,6 +1925,16 @@ export class WindowsBuildImage implements IBuildImage { imageType: WindowsImageType.SERVER_2019, }); + /** + * The standard CodeBuild image `aws/codebuild/windows-base:2019-2.0`, which is + * based off Windows Server Core 2019. + */ + public static readonly WIN_SERVER_CORE_2019_BASE_2_0: IBuildImage = new WindowsBuildImage({ + imageId: 'aws/codebuild/windows-base:2019-2.0', + imagePullPrincipalType: ImagePullPrincipalType.CODEBUILD, + imageType: WindowsImageType.SERVER_2019, + }); + /** * @returns a Windows build image from a Docker Hub image. */ diff --git a/packages/@aws-cdk/aws-codebuild/test/project.test.ts b/packages/@aws-cdk/aws-codebuild/test/project.test.ts index 101ebd49b6f2d..b5e3fa61d7432 100644 --- a/packages/@aws-cdk/aws-codebuild/test/project.test.ts +++ b/packages/@aws-cdk/aws-codebuild/test/project.test.ts @@ -795,6 +795,7 @@ describe('Environment', () => { test.each([ ['Standard 6.0', codebuild.LinuxBuildImage.STANDARD_6_0, 'aws/codebuild/standard:6.0'], ['Amazon Linux 4.0', codebuild.LinuxBuildImage.AMAZON_LINUX_2_4, 'aws/codebuild/amazonlinux2-x86_64-standard:4.0'], + ['Windows Server Core 2019 2.0', codebuild.WindowsBuildImage.WIN_SERVER_CORE_2019_BASE_2_0, 'aws/codebuild/windows-base:2019-2.0'], ])('has build image for %s', (_, buildImage, expected) => { // GIVEN const stack = new cdk.Stack(); From eacf17347d773dfd5d576745da24861177253e47 Mon Sep 17 00:00:00 2001 From: Tom Keller <1083460+kellertk@users.noreply.github.com> Date: Wed, 28 Sep 2022 10:36:30 -0400 Subject: [PATCH 20/24] chore: migrate merit badger to official repo (#22261) The GitHub Merit Badger is now located in `aws-github-ops`. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .github/workflows/github-merit-badger.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github-merit-badger.yml b/.github/workflows/github-merit-badger.yml index c7c2cfd3fdaa6..29e2df649f483 100644 --- a/.github/workflows/github-merit-badger.yml +++ b/.github/workflows/github-merit-badger.yml @@ -10,7 +10,7 @@ jobs: permissions: pull-requests: write steps: - - uses: kaizencc/github-merit-badger@main + - uses: aws-github-ops/github-merit-badger@main id: merit-badger with: github-token: ${{ secrets.GITHUB_TOKEN }} From 0620a2b44b4665f42e74a36d67d246ef734c02e9 Mon Sep 17 00:00:00 2001 From: AWS CDK Team Date: Wed, 28 Sep 2022 15:37:34 +0000 Subject: [PATCH 21/24] chore(release): 2.44.0 --- CHANGELOG.v2.alpha.md | 8 ++++++++ CHANGELOG.v2.md | 25 +++++++++++++++++++++++++ version.v2.json | 4 ++-- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.v2.alpha.md b/CHANGELOG.v2.alpha.md index 08548b9394b52..bd32608800e35 100644 --- a/CHANGELOG.v2.alpha.md +++ b/CHANGELOG.v2.alpha.md @@ -2,6 +2,14 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [2.44.0-alpha.0](https://github.com/aws/aws-cdk/compare/v2.43.1-alpha.0...v2.44.0-alpha.0) (2022-09-28) + + +### Features + +* **integ-tests:** chain assertion api calls ([#22196](https://github.com/aws/aws-cdk/issues/22196)) ([530e07b](https://github.com/aws/aws-cdk/commit/530e07bdc87ab94bbd5ed28debac98400a8152cc)) +* **neptune:** introduce metric method to cluster and instance ([#21995](https://github.com/aws/aws-cdk/issues/21995)) ([02ed837](https://github.com/aws/aws-cdk/commit/02ed8371276d504ba9fe09687d45409ad7cca288)), closes [#20248](https://github.com/aws/aws-cdk/issues/20248) + ## [2.43.1-alpha.0](https://github.com/aws/aws-cdk/compare/v2.43.0-alpha.0...v2.43.1-alpha.0) (2022-09-23) ## [2.43.0-alpha.0](https://github.com/aws/aws-cdk/compare/v2.42.1-alpha.0...v2.43.0-alpha.0) (2022-09-21) diff --git a/CHANGELOG.v2.md b/CHANGELOG.v2.md index 2c926fc39721d..009aa8a98341f 100644 --- a/CHANGELOG.v2.md +++ b/CHANGELOG.v2.md @@ -2,6 +2,31 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [2.44.0](https://github.com/aws/aws-cdk/compare/v2.43.1...v2.44.0) (2022-09-28) + + +### Features + +* **assets:** support drop-in docker replacements by setting `$CDK_DOCKER` ([#21838](https://github.com/aws/aws-cdk/issues/21838)) ([d52310e](https://github.com/aws/aws-cdk/commit/d52310ea2104dd1ed13761944d078ffce46a299f)), closes [40aws-cdk/core/lib/bundling.ts#L523](https://github.com/40aws-cdk/core/lib/bundling.ts/issues/L523) [#21836](https://github.com/aws/aws-cdk/issues/21836) +* **backup:** add copy actions to backup plan rules ([#22244](https://github.com/aws/aws-cdk/issues/22244)) ([d87a651](https://github.com/aws/aws-cdk/commit/d87a651608d23f3bfc3c178093d92b5bdda71084)), closes [#22173](https://github.com/aws/aws-cdk/issues/22173) +* **cfnspec:** cloudformation spec v89.0.0 ([#22232](https://github.com/aws/aws-cdk/issues/22232)) ([953d684](https://github.com/aws/aws-cdk/commit/953d6841fa3ed43258d0454e245cebcab6323e0d)) +* **cli:** `cdk deploy --method=direct` is faster ([#22079](https://github.com/aws/aws-cdk/issues/22079)) ([dd6ead4](https://github.com/aws/aws-cdk/commit/dd6ead447a80cdec3379a3ced2e04b7d15f9c55d)) +* **cloudwatch:** add gauge widget ([#22213](https://github.com/aws/aws-cdk/issues/22213)) ([d9f0e80](https://github.com/aws/aws-cdk/commit/d9f0e809d583d23cb83b4e2855574675a669c33f)), closes [#22136](https://github.com/aws/aws-cdk/issues/22136) +* **core:** 'postCliContext' property allows context that cannot be overridden by the CLI ([#21743](https://github.com/aws/aws-cdk/issues/21743)) ([a618096](https://github.com/aws/aws-cdk/commit/a618096432a27a808a0352ea186fe1e4db2911c4)) +* **dynamodb:** Changes how metricForOperation methods are used ([#22097](https://github.com/aws/aws-cdk/issues/22097)) ([fcb311d](https://github.com/aws/aws-cdk/commit/fcb311d615422b76f18b6be60dd466b315fcd6b0)), closes [#21963](https://github.com/aws/aws-cdk/issues/21963) +* **logs:** add dimensions to metric filter ([#21654](https://github.com/aws/aws-cdk/issues/21654)) ([f834a45](https://github.com/aws/aws-cdk/commit/f834a4537643b32131076111be0693c6f8f96b24)), closes [/github.com/aws/aws-cdk/issues/16999#issuecomment-1005172655](https://github.com/aws//github.com/aws/aws-cdk/issues/16999/issues/issuecomment-1005172655) [#16999](https://github.com/aws/aws-cdk/issues/16999) +* **pipelines:** allow disabling use of change sets ([#21619](https://github.com/aws/aws-cdk/issues/21619)) ([05723e7](https://github.com/aws/aws-cdk/commit/05723e74cc0e760f570c36ec02a70e8936287814)), closes [#20827](https://github.com/aws/aws-cdk/issues/20827) +* **s3-deployment:** extract flag to disable automatic unzipping ([#21805](https://github.com/aws/aws-cdk/issues/21805)) ([91898b5](https://github.com/aws/aws-cdk/commit/91898b51573c0bfd0f26ae7610feb6a400bc8159)), closes [#8065](https://github.com/aws/aws-cdk/issues/8065) + + +### Bug Fixes + +* **aws-elasticloadbalancingv2:** Validation for interval and timeout of application-target-group ([#22225](https://github.com/aws/aws-cdk/issues/22225)) ([6128e39](https://github.com/aws/aws-cdk/commit/6128e3908f4f6b6a1db66ebf7f77b6c966d1f9e7)) +* **cli:** SSO credentials do not work when using a proxy ([#22115](https://github.com/aws/aws-cdk/issues/22115)) ([c425e8c](https://github.com/aws/aws-cdk/commit/c425e8ca1a3d296eb6a7fd7e005d07c1eadd16aa)), closes [#21328](https://github.com/aws/aws-cdk/issues/21328) +* **elbv2:** Use correct format for parsing imported target group ARNs ([#22153](https://github.com/aws/aws-cdk/issues/22153)) ([4704d4c](https://github.com/aws/aws-cdk/commit/4704d4c4ac065634dbada3732193a6753369dd12)) +* **rds:** changing engine versions would fail to update on DBInstances that were part of a DBCluster ([#22185](https://github.com/aws/aws-cdk/issues/22185)) ([c070ace](https://github.com/aws/aws-cdk/commit/c070acea1b12ec4f73c7d2087c5408d7e38a90a3)), closes [#21758](https://github.com/aws/aws-cdk/issues/21758) [#22180](https://github.com/aws/aws-cdk/issues/22180) +* cannot use values that return an instance of a deprecated class for non TS / JS language ([#22204](https://github.com/aws/aws-cdk/issues/22204)) ([4cad2cf](https://github.com/aws/aws-cdk/commit/4cad2cf7e1ca41dedae6adc8866792e5f71b2123)) + ## [2.43.1](https://github.com/aws/aws-cdk/compare/v2.43.0...v2.43.1) (2022-09-23) diff --git a/version.v2.json b/version.v2.json index 7d3475748c006..d48962f88e81c 100644 --- a/version.v2.json +++ b/version.v2.json @@ -1,4 +1,4 @@ { - "version": "2.43.1", - "alphaVersion": "2.43.1-alpha.0" + "version": "2.44.0", + "alphaVersion": "2.44.0-alpha.0" } \ No newline at end of file From f7bbc943f00f3e0ceeb0ed03ec03bb36af5b3cb9 Mon Sep 17 00:00:00 2001 From: Cory Hall <43035978+corymhall@users.noreply.github.com> Date: Thu, 29 Sep 2022 08:32:39 -0400 Subject: [PATCH 22/24] fix(ec2): cannot allow all ipv6 traffic (#22279) This PR fixes an issue where it was impossible to add ipv6 egress rules without an escape hatch. The SecurityGroup construct will now track ipv6 and ipv4 separately. This matches the default behavior of CloudFormation and the underlying EC2 API. By default when you create a SecurityGroup (either via CFN or console) it will create an allow all ipv4 rule. If you later add a more specific rule CloudFormation will remove the ipv4 allow all rule. Since the default behavior is to _not_ add an allow all for ipv6, the SecurityGroup construct will also not add it by default. There is an edge case that this does break, but I'm not sure if it is a valid case. Previously it would have been possible to do the following (only allow all ipv6 outbound). But we don't want to allow that for the same reason we don't allow it for ipv4 (it will be overwritten by more restrictive rules). ```ts const sg = new SecurityGroup(this, 'Sg', { allowAllOutbound: false, }); sg.addEgressRule(Peer.anyIpv6(), Port.allTraffic()); ``` fixes #7094 ---- ### All Submissions: * [ ] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-ec2/lib/security-group.ts | 81 ++++++++++++++++++- .../aws-ec2/test/security-group.test.ts | 66 ++++++++++++++- 2 files changed, 140 insertions(+), 7 deletions(-) diff --git a/packages/@aws-cdk/aws-ec2/lib/security-group.ts b/packages/@aws-cdk/aws-ec2/lib/security-group.ts index c2ce04b51bb13..c5c0ad3753df1 100644 --- a/packages/@aws-cdk/aws-ec2/lib/security-group.ts +++ b/packages/@aws-cdk/aws-ec2/lib/security-group.ts @@ -63,6 +63,7 @@ abstract class SecurityGroupBase extends Resource implements ISecurityGroup { public abstract readonly securityGroupId: string; public abstract readonly allowAllOutbound: boolean; + public abstract readonly allowAllIpv6Outbound: boolean; public readonly canInlineRule = false; public readonly connections: Connections = new Connections({ securityGroups: [this] }); @@ -237,10 +238,25 @@ export interface SecurityGroupProps { * outbound traffic. If this is set to false, no outbound traffic will be allowed by * default and all egress traffic must be explicitly authorized. * + * To allow all ipv6 traffic use allowAllIpv6Outbound + * * @default true */ readonly allowAllOutbound?: boolean; + /** + * Whether to allow all outbound ipv6 traffic by default. + * + * If this is set to true, there will only be a single egress rule which allows all + * outbound ipv6 traffic. If this is set to false, no outbound traffic will be allowed by + * default and all egress ipv6 traffic must be explicitly authorized. + * + * To allow all ipv4 traffic use allowAllOutbound + * + * @default false + */ + readonly allowAllIpv6Outbound?: boolean; + /** * Whether to disable inline ingress and egress rule optimization. * @@ -274,6 +290,17 @@ export interface SecurityGroupImportOptions { */ readonly allowAllOutbound?: boolean; + /** + * Mark the SecurityGroup as having been created allowing all outbound ipv6 traffic + * + * Only if this is set to false will egress rules for ipv6 be added to this security + * group. Be aware, this would undo any potential "all outbound traffic" + * default. + * + * @default false + */ + readonly allowAllIpv6Outbound?: boolean; + /** * If a SecurityGroup is mutable CDK can add rules to existing groups * @@ -360,6 +387,7 @@ export class SecurityGroup extends SecurityGroupBase { class MutableImport extends SecurityGroupBase { public securityGroupId = securityGroupId; public allowAllOutbound = options.allowAllOutbound ?? true; + public allowAllIpv6Outbound = options.allowAllIpv6Outbound ?? false; public addEgressRule(peer: IPeer, connection: Port, description?: string, remoteRule?: boolean) { // Only if allowAllOutbound has been disabled @@ -372,6 +400,7 @@ export class SecurityGroup extends SecurityGroupBase { class ImmutableImport extends SecurityGroupBase { public securityGroupId = securityGroupId; public allowAllOutbound = options.allowAllOutbound ?? true; + public allowAllIpv6Outbound = options.allowAllIpv6Outbound ?? false; public addEgressRule(_peer: IPeer, _connection: Port, _description?: string, _remoteRule?: boolean) { // do nothing @@ -441,6 +470,11 @@ export class SecurityGroup extends SecurityGroupBase { */ public readonly allowAllOutbound: boolean; + /** + * Whether the SecurityGroup has been configured to allow all outbound ipv6 traffic + */ + public readonly allowAllIpv6Outbound: boolean; + private readonly securityGroup: CfnSecurityGroup; private readonly directIngressRules: CfnSecurityGroup.IngressProperty[] = []; private readonly directEgressRules: CfnSecurityGroup.EgressProperty[] = []; @@ -458,6 +492,7 @@ export class SecurityGroup extends SecurityGroupBase { const groupDescription = props.description || this.node.path; this.allowAllOutbound = props.allowAllOutbound !== false; + this.allowAllIpv6Outbound = props.allowAllIpv6Outbound ?? false; this.disableInlineRules = props.disableInlineRules !== undefined ? !!props.disableInlineRules : @@ -476,6 +511,7 @@ export class SecurityGroup extends SecurityGroupBase { this.securityGroupName = this.securityGroup.ref; this.addDefaultEgressRule(); + this.addDefaultIpv6EgressRule(); } public addIngressRule(peer: IPeer, connection: Port, description?: string, remoteRule?: boolean) { @@ -496,7 +532,9 @@ export class SecurityGroup extends SecurityGroupBase { } public addEgressRule(peer: IPeer, connection: Port, description?: string, remoteRule?: boolean) { - if (this.allowAllOutbound) { + const isIpv6 = peer.toEgressRuleConfig().hasOwnProperty('cidrIpv6'); + + if (!isIpv6 && this.allowAllOutbound) { // In the case of "allowAllOutbound", we don't add any more rules. There // is only one rule which allows all traffic and that subsumes any other // rule. @@ -504,13 +542,23 @@ export class SecurityGroup extends SecurityGroupBase { Annotations.of(this).addWarning('Ignoring Egress rule since \'allowAllOutbound\' is set to true; To add customized rules, set allowAllOutbound=false on the SecurityGroup'); } return; - } else { + } else if (!isIpv6 && !this.allowAllOutbound) { // Otherwise, if the bogus rule exists we can now remove it because the // presence of any other rule will get rid of EC2's implicit "all // outbound" rule anyway. this.removeNoTrafficRule(); } + if (isIpv6 && this.allowAllIpv6Outbound) { + // In the case of "allowAllIpv6Outbound", we don't add any more rules. There + // is only one rule which allows all traffic and that subsumes any other + // rule. + if (!remoteRule) { // Warn only if addEgressRule() was explicitely called + Annotations.of(this).addWarning('Ignoring Egress rule since \'allowAllIpv6Outbound\' is set to true; To add customized rules, set allowAllIpv6Outbound=false on the SecurityGroup'); + } + return; + } + if (!peer.canInlineRule || !connection.canInlineRule || this.disableInlineRules) { super.addEgressRule(peer, connection, description, remoteRule); return; @@ -532,7 +580,7 @@ export class SecurityGroup extends SecurityGroupBase { // to "allOutbound=true" mode, because we might have already emitted // EgressRule objects (which count as rules added later) and there's no way // to recall those. Better to prevent this for now. - throw new Error('Cannot add an "all traffic" egress rule in this way; set allowAllOutbound=true on the SecurityGroup instead.'); + throw new Error('Cannot add an "all traffic" egress rule in this way; set allowAllOutbound=true (for ipv6) or allowAllIpv6Outbound=true (for ipv6) on the SecurityGroup instead.'); } this.addDirectEgressRule(rule); @@ -596,6 +644,31 @@ export class SecurityGroup extends SecurityGroupBase { } } + /** + * Add a allow all ipv6 egress rule to the securityGroup + * + * This depends on allowAllIpv6Outbound: + * + * - If allowAllIpv6Outbound is true, we will add an allow all rule. + * - If allowAllOutbound is false, we don't do anything since EC2 does not add + * a default allow all ipv6 rule. + */ + private addDefaultIpv6EgressRule() { + const description = 'Allow all outbound ipv6 traffic by default'; + const peer = Peer.anyIpv6(); + if (this.allowAllIpv6Outbound) { + if (this.disableInlineRules) { + super.addEgressRule(peer, Port.allTraffic(), description, false); + } else { + this.directEgressRules.push({ + ipProtocol: '-1', + cidrIp: peer.uniqueId, + description, + }); + } + } + } + /** * Remove the bogus rule if it exists */ @@ -721,7 +794,7 @@ function egressRulesEqual(a: CfnSecurityGroup.EgressProperty, b: CfnSecurityGrou * Whether this rule refers to all traffic */ function isAllTrafficRule(rule: any) { - return rule.cidrIp === '0.0.0.0/0' && rule.ipProtocol === '-1'; + return (rule.cidrIp === '0.0.0.0/0' || rule.cidrIpv6 === '::/0') && rule.ipProtocol === '-1'; } /** diff --git a/packages/@aws-cdk/aws-ec2/test/security-group.test.ts b/packages/@aws-cdk/aws-ec2/test/security-group.test.ts index 4624d2a648b72..bee7ec9cceede 100644 --- a/packages/@aws-cdk/aws-ec2/test/security-group.test.ts +++ b/packages/@aws-cdk/aws-ec2/test/security-group.test.ts @@ -24,7 +24,59 @@ describe('security group', () => { }, ], }); + }); + + test('security group can allows all ipv6 outbound traffic by default', () => { + // GIVEN + const stack = new Stack(); + const vpc = new Vpc(stack, 'VPC'); + + // WHEN + new SecurityGroup(stack, 'SG1', { vpc, allowAllIpv6Outbound: true }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroup', { + SecurityGroupEgress: [ + { + CidrIp: '0.0.0.0/0', + Description: 'Allow all outbound traffic by default', + IpProtocol: '-1', + }, + { + CidrIp: '::/0', + Description: 'Allow all outbound ipv6 traffic by default', + IpProtocol: '-1', + }, + ], + }); + }); + + test('can add ipv6 rules even if allowAllOutbound=true', () => { + // GIVEN + const stack = new Stack(); + const vpc = new Vpc(stack, 'VPC'); + // WHEN + const sg = new SecurityGroup(stack, 'SG1', { vpc }); + sg.addEgressRule(Peer.ipv6('2001:db8::/128'), Port.tcp(80)); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroup', { + SecurityGroupEgress: [ + { + CidrIp: '0.0.0.0/0', + Description: 'Allow all outbound traffic by default', + IpProtocol: '-1', + }, + { + CidrIpv6: '2001:db8::/128', + Description: 'from 2001:db8::/128:80', + FromPort: 80, + ToPort: 80, + IpProtocol: 'tcp', + }, + ], + }); }); @@ -96,8 +148,6 @@ describe('security group', () => { }, ], }); - - }); test('all outbound rule cannot be added after creation', () => { @@ -110,8 +160,18 @@ describe('security group', () => { expect(() => { sg.addEgressRule(Peer.anyIpv4(), Port.allTraffic(), 'All traffic'); }).toThrow(/Cannot add/); + }); + test('all ipv6 outbound rule cannot be added after creation', () => { + // GIVEN + const stack = new Stack(); + const vpc = new Vpc(stack, 'VPC'); + // WHEN + const sg = new SecurityGroup(stack, 'SG1', { vpc, allowAllOutbound: false }); + expect(() => { + sg.addEgressRule(Peer.anyIpv6(), Port.allTraffic(), 'All traffic'); + }).toThrow(/Cannot add/); }); test('immutable imports do not add rules', () => { @@ -171,7 +231,7 @@ describe('security group', () => { // GIVEN const stack = new Stack(undefined, 'TestStack', { env: { account: '12345678', region: 'dummy' } }); const vpc = new Vpc(stack, 'VPC'); - const sg = new SecurityGroup(stack, 'SG', { vpc }); + const sg = new SecurityGroup(stack, 'SG', { vpc, allowAllIpv6Outbound: true }); const peers = [ new SecurityGroup(stack, 'PeerGroup', { vpc }), From 6840d8e43381793bd7a51191bddaffc4cb6641d6 Mon Sep 17 00:00:00 2001 From: Clare Liguori Date: Thu, 29 Sep 2022 08:32:38 -0700 Subject: [PATCH 23/24] feat(codedeploy): CodeDeploy deployment config constructs for Lambda and ECS (#22159) CloudFormation now supports Lambda and ECS in the `AWS::CodeDeploy::DeploymentConfig` resource type. This PR adds L2 constructs specific to ECS and Lambda for that resource type. ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### New Features * [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *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-codedeploy/README.md | 163 +++++++++++---- .../lib/base-deployment-config.ts | 171 +++++++++++++++ .../lib/ecs/deployment-config.ts | 71 ++++--- .../aws-codedeploy/lib/host-health-config.ts | 36 ++++ packages/@aws-cdk/aws-codedeploy/lib/index.ts | 3 + .../lib/lambda/custom-deployment-config.ts | 11 + .../lib/lambda/deployment-config.ts | 99 +++++---- .../lib/server/deployment-config.ts | 114 +++------- .../lib/traffic-routing-config.ts | 194 ++++++++++++++++++ packages/@aws-cdk/aws-codedeploy/package.json | 27 +-- .../test/ecs/application.test.ts | 9 + ...efaultTestDeployAssertA6573788.assets.json | 19 ++ ...aultTestDeployAssertA6573788.template.json | 36 ++++ .../aws-cdk-codedeploy-ecs-config.assets.json | 19 ++ ...ws-cdk-codedeploy-ecs-config.template.json | 51 +++++ .../deployment-config.integ.snapshot/cdk.out | 1 + .../integ.json | 12 ++ .../manifest.json | 111 ++++++++++ .../tree.json | 99 +++++++++ .../test/ecs/deployment-config.test.ts | 142 +++++++++++++ .../test/ecs/integ.deployment-config.ts | 19 ++ .../test/lambda/application.test.ts | 9 + .../lambda/custom-deployment-config.test.ts | 15 +- ...efaultTestDeployAssert161B09F6.assets.json | 19 ++ ...aultTestDeployAssert161B09F6.template.json | 36 ++++ ...s-cdk-codedeploy-lambda-config.assets.json | 19 ++ ...cdk-codedeploy-lambda-config.template.json | 51 +++++ .../deployment-config.integ.snapshot/cdk.out | 1 + .../integ.json | 12 ++ .../manifest.json | 111 ++++++++++ .../tree.json | 99 +++++++++ .../test/lambda/deployment-config.test.ts | 142 +++++++++++++ .../test/lambda/deployment-group.test.ts | 29 +++ .../test/lambda/integ.deployment-config.ts | 19 ++ 34 files changed, 1753 insertions(+), 216 deletions(-) create mode 100644 packages/@aws-cdk/aws-codedeploy/lib/base-deployment-config.ts create mode 100644 packages/@aws-cdk/aws-codedeploy/lib/host-health-config.ts create mode 100644 packages/@aws-cdk/aws-codedeploy/lib/traffic-routing-config.ts create mode 100644 packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/EcsDeploymentConfigTestDefaultTestDeployAssertA6573788.assets.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/EcsDeploymentConfigTestDefaultTestDeployAssertA6573788.template.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/aws-cdk-codedeploy-ecs-config.assets.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/aws-cdk-codedeploy-ecs-config.template.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/integ.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/manifest.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/tree.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.test.ts create mode 100644 packages/@aws-cdk/aws-codedeploy/test/ecs/integ.deployment-config.ts create mode 100644 packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6.assets.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6.template.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/aws-cdk-codedeploy-lambda-config.assets.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/aws-cdk-codedeploy-lambda-config.template.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/integ.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/manifest.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/tree.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.test.ts create mode 100644 packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-config.ts diff --git a/packages/@aws-cdk/aws-codedeploy/README.md b/packages/@aws-cdk/aws-codedeploy/README.md index 2608c706022ea..ec5449698294a 100644 --- a/packages/@aws-cdk/aws-codedeploy/README.md +++ b/packages/@aws-cdk/aws-codedeploy/README.md @@ -15,7 +15,7 @@ AWS CodeDeploy is a deployment service that automates application deployments to Amazon EC2 instances, on-premises instances, serverless Lambda functions, or Amazon ECS services. -The CDK currently supports Amazon EC2, on-premise and AWS Lambda applications. +The CDK currently supports Amazon EC2, on-premise, AWS Lambda, and Amazon ECS applications. ## EC2/on-premise Applications @@ -143,7 +143,7 @@ const deploymentGroup = new codedeploy.ServerDeploymentGroup(this, 'DeploymentGr }); ``` -## Deployment Configurations +## EC2/on-premise Deployment Configurations You can also pass a Deployment Configuration when creating the Deployment Group: @@ -226,41 +226,6 @@ In order to deploy a new version of this function: 2. Re-deploy the stack (this will trigger a deployment). 3. Monitor the CodeDeploy deployment as traffic shifts between the versions. - -### Create a custom Deployment Config - -CodeDeploy for Lambda comes with built-in configurations for traffic shifting. -If you want to specify your own strategy, -you can do so with the CustomLambdaDeploymentConfig construct, -letting you specify precisely how fast a new function version is deployed. - -```ts -const config = new codedeploy.CustomLambdaDeploymentConfig(this, 'CustomConfig', { - type: codedeploy.CustomLambdaDeploymentConfigType.CANARY, - interval: Duration.minutes(1), - percentage: 5, -}); - -declare const application: codedeploy.LambdaApplication; -declare const alias: lambda.Alias; -const deploymentGroup = new codedeploy.LambdaDeploymentGroup(this, 'BlueGreenDeployment', { - application, - alias, - deploymentConfig: config, -}); -``` - -You can specify a custom name for your deployment config, but if you do you will not be able to update the interval/percentage through CDK. - -```ts -const config = new codedeploy.CustomLambdaDeploymentConfig(this, 'CustomConfig', { - type: codedeploy.CustomLambdaDeploymentConfigType.CANARY, - interval: Duration.minutes(1), - percentage: 5, - deploymentConfigName: 'MyDeploymentConfig', -}); -``` - ### Rollbacks and Alarms CodeDeploy will roll back if the deployment fails. You can optionally trigger a rollback when one or more alarms are in a failed state: @@ -327,3 +292,127 @@ const deploymentGroup = codedeploy.LambdaDeploymentGroup.fromLambdaDeploymentGro deploymentGroupName: 'MyExistingDeploymentGroup', }); ``` + +## Lambda Deployment Configurations + +CodeDeploy for Lambda comes with predefined configurations for traffic shifting. +The predefined configurations are available as LambdaDeploymentConfig constants. + +```ts +const config = codedeploy.LambdaDeploymentConfig.CANARY_10PERCENT_30MINUTES; + +declare const application: codedeploy.LambdaApplication; +declare const alias: lambda.Alias; +const deploymentGroup = new codedeploy.LambdaDeploymentGroup(this, 'BlueGreenDeployment', { + application, + alias, + deploymentConfig: config, +}); +``` + +If you want to specify your own strategy, +you can do so with the LambdaDeploymentConfig construct, +letting you specify precisely how fast a new function version is deployed. + +```ts +const config = new codedeploy.LambdaDeploymentConfig(this, 'CustomConfig', { + trafficRoutingConfig: new codedeploy.TimeBasedCanaryTrafficRoutingConfig({ + interval: cdk.Duration.minutes(15), + percentage: 5, + }), +}); + +declare const application: codedeploy.LambdaApplication; +declare const alias: lambda.Alias; +const deploymentGroup = new codedeploy.LambdaDeploymentGroup(this, 'BlueGreenDeployment', { + application, + alias, + deploymentConfig: config, +}); +``` + +You can specify a custom name for your deployment config, but if you do you will not be able to update the interval/percentage through CDK. + +```ts +const config = new codedeploy.LambdaDeploymentConfig(this, 'CustomConfig', { + trafficRoutingConfig: new codedeploy.TimeBasedCanaryTrafficRoutingConfig({ + interval: cdk.Duration.minutes(15), + percentage: 5, + }), + deploymentConfigName: 'MyDeploymentConfig', +}); +``` + +To import an already existing Deployment Config: + +```ts +const deploymentConfig = codedeploy.LambdaDeploymentConfig.fromLambdaDeploymentConfigName( + this, + 'ExistingDeploymentConfiguration', + 'MyExistingDeploymentConfiguration', +); +``` + +## ECS Applications + +To create a new CodeDeploy Application that deploys an ECS service: + +```ts +const application = new codedeploy.EcsApplication(this, 'CodeDeployApplication', { + applicationName: 'MyApplication', // optional property +}); +``` + +To import an already existing Application: + +```ts +const application = codedeploy.EcsApplication.fromEcsApplicationName( + this, + 'ExistingCodeDeployApplication', + 'MyExistingApplication', +); +``` + +## ECS Deployment Configurations + +CodeDeploy for ECS comes with predefined configurations for traffic shifting. +The predefined configurations are available as LambdaDeploymentConfig constants. + +```ts +const config = codedeploy.EcsDeploymentConfig.CANARY_10PERCENT_5MINUTES; +``` + +If you want to specify your own strategy, +you can do so with the EcsDeploymentConfig construct, +letting you specify precisely how fast an ECS service is deployed. + +```ts +new codedeploy.EcsDeploymentConfig(this, 'CustomConfig', { + trafficRoutingConfig: new codedeploy.TimeBasedCanaryTrafficRoutingConfig({ + interval: cdk.Duration.minutes(15), + percentage: 5, + }), +}); +``` + +You can specify a custom name for your deployment config, but if you do you will not be able to update the interval/percentage through CDK. + +```ts +const config = new codedeploy.EcsDeploymentConfig(this, 'CustomConfig', { + trafficRoutingConfig: new codedeploy.TimeBasedCanaryTrafficRoutingConfig({ + interval: cdk.Duration.minutes(15), + percentage: 5, + }), + deploymentConfigName: 'MyDeploymentConfig', +}); +``` + +Or import an existing one: + +```ts +const deploymentConfig = codedeploy.EcsDeploymentConfig.fromEcsDeploymentConfigName( + this, + 'ExistingDeploymentConfiguration', + 'MyExistingDeploymentConfiguration', +); +``` diff --git a/packages/@aws-cdk/aws-codedeploy/lib/base-deployment-config.ts b/packages/@aws-cdk/aws-codedeploy/lib/base-deployment-config.ts new file mode 100644 index 0000000000000..0cd04ae3628c5 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/lib/base-deployment-config.ts @@ -0,0 +1,171 @@ +import { ArnFormat, Resource, Stack } from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import { CfnDeploymentConfig } from './codedeploy.generated'; +import { MinimumHealthyHosts } from './host-health-config'; +import { TrafficRouting } from './traffic-routing-config'; +import { arnForDeploymentConfig, validateName } from './utils'; + +/** + * The base class for ServerDeploymentConfig, EcsDeploymentConfig, + * and LambdaDeploymentConfig deployment configurations. + */ +export interface IBaseDeploymentConfig { + /** + * The physical, human-readable name of the Deployment Configuration. + * @attribute + */ + readonly deploymentConfigName: string; + + /** + * The ARN of the Deployment Configuration. + * @attribute + */ + readonly deploymentConfigArn: string; +} + +/** + * Construction properties of {@link BaseDeploymentConfig}. + */ +export interface BaseDeploymentConfigOptions { + /** + * The physical, human-readable name of the Deployment Configuration. + * @default - automatically generated name + */ + readonly deploymentConfigName?: string; +} + +/** + * The compute platform of a deployment configuration + */ +export enum ComputePlatform { + /** + * The deployment will target EC2 instances or on-premise servers + */ + SERVER = 'Server', + + /** + * The deployment will target a Lambda function + */ + LAMBDA = 'Lambda', + + /** + * The deployment will target an ECS server + */ + ECS = 'ECS' +} + +/** + * Complete base deployment config properties that are required to be supplied by the implementation + * of the BaseDeploymentConfig class. + */ +export interface BaseDeploymentConfigProps extends BaseDeploymentConfigOptions { + /** + * The destination compute platform for the deployment. + * + * @default ComputePlatform.Server + */ + readonly computePlatform?: ComputePlatform; + + /** + * The configuration that specifies how traffic is shifted during a deployment. + * Only applicable to ECS and Lambda deployments, and must not be specified for Server deployments. + * @default None + */ + readonly trafficRouting?: TrafficRouting; + + /** + * Minimum number of healthy hosts. + * @default None + */ + readonly minimumHealthyHosts?: MinimumHealthyHosts; +} + +/** + * The base class for ServerDeploymentConfig, EcsDeploymentConfig, + * and LambdaDeploymentConfig deployment configurations. + * + * @resource AWS::CodeDeploy::DeploymentConfig + */ +export abstract class BaseDeploymentConfig extends Resource implements IBaseDeploymentConfig { + /** + * Import a custom Deployment Configuration for a Deployment Group defined outside the CDK. + * + * @param scope the parent Construct for this new Construct + * @param id the logical ID of this new Construct + * @param deploymentConfigName the name of the referenced custom Deployment Configuration + * @returns a Construct representing a reference to an existing custom Deployment Configuration + */ + protected static fromDeploymentConfigName(scope: Construct, id: string, deploymentConfigName: string): IBaseDeploymentConfig { + ignore(id); + const arn = Stack.of(scope).formatArn({ + service: 'codedeploy', + resource: 'deploymentconfig', + resourceName: deploymentConfigName, + arnFormat: ArnFormat.COLON_RESOURCE_NAME, + }); + return { + deploymentConfigName: deploymentConfigName, + deploymentConfigArn: arn, + }; + } + + /** + * This method should be used only for static references to predefined deployment configurations, + * like EcsDeploymentConfig.ALL_AT_ONCE + * @param name the name of the referenced custom Deployment Configuration + * @returns a reference to an existing custom Deployment Configuration + */ + protected static deploymentConfig(name: string): IBaseDeploymentConfig { + return { + deploymentConfigName: name, + deploymentConfigArn: arnForDeploymentConfig(name), + }; + } + + /** + * The name of the deployment config + * @attribute + */ + public readonly deploymentConfigName: string; + + /** + * The arn of the deployment config + * @attribute + */ + public readonly deploymentConfigArn: string; + + public constructor(scope: Construct, id: string, props?: BaseDeploymentConfigProps) { + super(scope, id, { + physicalName: props?.deploymentConfigName, + }); + + // Traffic routing is not applicable to Server-based deployment configs + if (props?.trafficRouting && (props?.computePlatform === undefined || props?.computePlatform === ComputePlatform.SERVER)) { + throw new Error('Traffic routing config must not be specified for a Server-base deployment configuration'); + } + + // Minimum healthy hosts is only applicable to Server-based deployment configs + if (props?.minimumHealthyHosts && props?.computePlatform && props?.computePlatform !== ComputePlatform.SERVER) { + throw new Error('Minimum healthy hosts config must only be specified for a Server-base deployment configuration'); + } + + const resource = new CfnDeploymentConfig(this, 'Resource', { + deploymentConfigName: this.physicalName, + computePlatform: props?.computePlatform, + trafficRoutingConfig: props?.trafficRouting?.bind(this), + minimumHealthyHosts: props?.minimumHealthyHosts?._json, + }); + + this.deploymentConfigName = this.getResourceNameAttribute(resource.ref); + this.deploymentConfigArn = this.getResourceArnAttribute(arnForDeploymentConfig(resource.ref), { + service: 'codedeploy', + resource: 'deploymentconfig', + resourceName: this.physicalName, + arnFormat: ArnFormat.COLON_RESOURCE_NAME, + }); + + this.node.addValidation({ validate: () => validateName('Deployment config', this.physicalName) }); + } +} + +function ignore(_x: any) { return; } diff --git a/packages/@aws-cdk/aws-codedeploy/lib/ecs/deployment-config.ts b/packages/@aws-cdk/aws-codedeploy/lib/ecs/deployment-config.ts index f9f41e12202a7..cd1f96aa50945 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/ecs/deployment-config.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/ecs/deployment-config.ts @@ -1,52 +1,69 @@ import { Construct } from 'constructs'; -import { arnForDeploymentConfig } from '../utils'; +import { BaseDeploymentConfig, BaseDeploymentConfigOptions, ComputePlatform, IBaseDeploymentConfig } from '../base-deployment-config'; +import { TrafficRouting } from '../traffic-routing-config'; /** * The Deployment Configuration of an ECS Deployment Group. + * + * If you're managing the Deployment Configuration alongside the rest of your CDK resources, + * use the {@link EcsDeploymentConfig} class. + * + * If you want to reference an already existing deployment configuration, + * or one defined in a different CDK Stack, + * use the {@link EcsDeploymentConfig#fromEcsDeploymentConfigName} method. + * * The default, pre-defined Configurations are available as constants on the {@link EcsDeploymentConfig} class * (for example, `EcsDeploymentConfig.AllAtOnce`). - * - * Note: CloudFormation does not currently support creating custom ECS configs outside - * of using a custom resource. You can import custom deployment config created outside the - * CDK or via a custom resource with {@link EcsDeploymentConfig#fromEcsDeploymentConfigName}. */ -export interface IEcsDeploymentConfig { - readonly deploymentConfigName: string; - readonly deploymentConfigArn: string; +export interface IEcsDeploymentConfig extends IBaseDeploymentConfig { +} + +/** + * Construction properties of {@link EcsDeploymentConfig}. + */ +export interface EcsDeploymentConfigProps extends BaseDeploymentConfigOptions { + /** + * The configuration that specifies how traffic is shifted from the 'blue' + * target group to the 'green' target group during a deployment. + * @default AllAtOnce + */ + readonly trafficRouting?: TrafficRouting; } /** * A custom Deployment Configuration for an ECS Deployment Group. * - * Note: This class currently stands as namespaced container of the default configurations - * until CloudFormation supports custom ECS Deployment Configs. Until then it is closed - * (private constructor) and does not extend {@link Construct} - * * @resource AWS::CodeDeploy::DeploymentConfig */ -export class EcsDeploymentConfig { - public static readonly ALL_AT_ONCE = deploymentConfig('CodeDeployDefault.ECSAllAtOnce'); +export class EcsDeploymentConfig extends BaseDeploymentConfig implements IEcsDeploymentConfig { + /** CodeDeploy predefined deployment configuration that shifts all traffic to the updated ECS task set at once. */ + public static readonly ALL_AT_ONCE = EcsDeploymentConfig.deploymentConfig('CodeDeployDefault.ECSAllAtOnce'); + /** CodeDeploy predefined deployment configuration that shifts 10 percent of traffic every minute until all traffic is shifted. */ + public static readonly LINEAR_10PERCENT_EVERY_1MINUTES = EcsDeploymentConfig.deploymentConfig('CodeDeployDefault.ECSLinear10PercentEvery1Minutes'); + /** CodeDeploy predefined deployment configuration that shifts 10 percent of traffic every three minutes until all traffic is shifted. */ + public static readonly LINEAR_10PERCENT_EVERY_3MINUTES = EcsDeploymentConfig.deploymentConfig('CodeDeployDefault.ECSLinear10PercentEvery3Minutes'); + /** CodeDeploy predefined deployment configuration that shifts 10 percent of traffic in the first increment. The remaining 90 percent is deployed five minutes later. */ + public static readonly CANARY_10PERCENT_5MINUTES = EcsDeploymentConfig.deploymentConfig('CodeDeployDefault.ECSCanary10Percent5Minutes'); + /** CodeDeploy predefined deployment configuration that shifts 10 percent of traffic in the first increment. The remaining 90 percent is deployed 15 minutes later. */ + public static readonly CANARY_10PERCENT_15MINUTES = EcsDeploymentConfig.deploymentConfig('CodeDeployDefault.ECSCanary10Percent15Minutes'); /** * Import a custom Deployment Configuration for an ECS Deployment Group defined outside the CDK. * - * @param _scope the parent Construct for this new Construct - * @param _id the logical ID of this new Construct + * @param scope the parent Construct for this new Construct + * @param id the logical ID of this new Construct * @param ecsDeploymentConfigName the name of the referenced custom Deployment Configuration * @returns a Construct representing a reference to an existing custom Deployment Configuration */ - public static fromEcsDeploymentConfigName(_scope: Construct, _id: string, ecsDeploymentConfigName: string): IEcsDeploymentConfig { - return deploymentConfig(ecsDeploymentConfigName); + public static fromEcsDeploymentConfigName(scope: Construct, id: string, ecsDeploymentConfigName: string): IEcsDeploymentConfig { + return this.fromDeploymentConfigName(scope, id, ecsDeploymentConfigName); } - private constructor() { - // nothing to do until CFN supports custom ECS deployment configurations + public constructor(scope: Construct, id: string, props?: EcsDeploymentConfigProps) { + super(scope, id, { + ...props, + computePlatform: ComputePlatform.ECS, + trafficRouting: props?.trafficRouting ?? TrafficRouting.allAtOnce(), + }); } } - -function deploymentConfig(name: string): IEcsDeploymentConfig { - return { - deploymentConfigName: name, - deploymentConfigArn: arnForDeploymentConfig(name), - }; -} diff --git a/packages/@aws-cdk/aws-codedeploy/lib/host-health-config.ts b/packages/@aws-cdk/aws-codedeploy/lib/host-health-config.ts new file mode 100644 index 0000000000000..26117518ed090 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/lib/host-health-config.ts @@ -0,0 +1,36 @@ +import { CfnDeploymentConfig } from './codedeploy.generated'; + +/** + * Minimum number of healthy hosts for a server deployment. + */ +export class MinimumHealthyHosts { + + /** + * The minimum healhty hosts threshold expressed as an absolute number. + */ + public static count(value: number): MinimumHealthyHosts { + return new MinimumHealthyHosts({ + type: 'HOST_COUNT', + value, + }); + } + + /** + * The minmum healhty hosts threshold expressed as a percentage of the fleet. + */ + public static percentage(value: number): MinimumHealthyHosts { + return new MinimumHealthyHosts({ + type: 'FLEET_PERCENT', + value, + }); + } + + private constructor(private readonly json: CfnDeploymentConfig.MinimumHealthyHostsProperty) { } + + /** + * @internal + */ + public get _json() { + return this.json; + } +} diff --git a/packages/@aws-cdk/aws-codedeploy/lib/index.ts b/packages/@aws-cdk/aws-codedeploy/lib/index.ts index 147f27580b874..47dd10cc39190 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/index.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/index.ts @@ -1,4 +1,7 @@ +export * from './base-deployment-config'; +export * from './host-health-config'; export * from './rollback-config'; +export * from './traffic-routing-config'; export * from './ecs'; export * from './lambda'; export * from './server'; diff --git a/packages/@aws-cdk/aws-codedeploy/lib/lambda/custom-deployment-config.ts b/packages/@aws-cdk/aws-codedeploy/lib/lambda/custom-deployment-config.ts index 726a7781c2dd3..fe4ceeb249af2 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/lambda/custom-deployment-config.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/lambda/custom-deployment-config.ts @@ -6,26 +6,31 @@ import { ILambdaDeploymentConfig } from './deployment-config'; /** * Lambda Deployment config type + * @deprecated Use `LambdaDeploymentConfig` */ export enum CustomLambdaDeploymentConfigType { /** * Canary deployment type + * @deprecated Use `LambdaDeploymentConfig` */ CANARY = 'Canary', /** * Linear deployment type + * @deprecated Use `LambdaDeploymentConfig` */ LINEAR = 'Linear' } /** * Properties of a reference to a CodeDeploy Lambda Deployment Configuration. + * @deprecated Use `LambdaDeploymentConfig` */ export interface CustomLambdaDeploymentConfigProps { /** * The type of deployment config, either CANARY or LINEAR + * @deprecated Use `LambdaDeploymentConfig` */ readonly type: CustomLambdaDeploymentConfigType; @@ -33,6 +38,7 @@ export interface CustomLambdaDeploymentConfigProps { * The integer percentage of traffic to shift: * - For LINEAR, the percentage to shift every interval * - For CANARY, the percentage to shift until the interval passes, before the full deployment + * @deprecated Use `LambdaDeploymentConfig` */ readonly percentage: number; @@ -40,6 +46,7 @@ export interface CustomLambdaDeploymentConfigProps { * The interval, in number of minutes: * - For LINEAR, how frequently additional traffic is shifted * - For CANARY, how long to shift traffic before the full deployment + * @deprecated Use `LambdaDeploymentConfig` */ readonly interval: Duration; @@ -47,6 +54,7 @@ export interface CustomLambdaDeploymentConfigProps { * The verbatim name of the deployment config. Must be unique per account/region. * Other parameters cannot be updated if this name is provided. * @default - automatically generated name + * @deprecated Use `LambdaDeploymentConfig` */ readonly deploymentConfigName?: string; } @@ -54,18 +62,21 @@ export interface CustomLambdaDeploymentConfigProps { /** * A custom Deployment Configuration for a Lambda Deployment Group. * @resource AWS::CodeDeploy::DeploymentGroup + * @deprecated CloudFormation now supports Lambda deployment configurations without custom resources. Use {@link LambdaDeploymentConfig}. */ export class CustomLambdaDeploymentConfig extends Resource implements ILambdaDeploymentConfig { /** * The name of the deployment config * @attribute + * @deprecated Use `LambdaDeploymentConfig` */ public readonly deploymentConfigName: string; /** * The arn of the deployment config * @attribute + * @deprecated Use `LambdaDeploymentConfig` */ public readonly deploymentConfigArn: string; diff --git a/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-config.ts b/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-config.ts index 594d994942f46..18690f239d820 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-config.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-config.ts @@ -1,18 +1,21 @@ import { Construct } from 'constructs'; -import { arnForDeploymentConfig } from '../utils'; +import { BaseDeploymentConfig, BaseDeploymentConfigOptions, ComputePlatform, IBaseDeploymentConfig } from '../base-deployment-config'; +import { TrafficRouting } from '../traffic-routing-config'; /** * The Deployment Configuration of a Lambda Deployment Group. + * + * If you're managing the Deployment Configuration alongside the rest of your CDK resources, + * use the {@link LambdaDeploymentConfig} class. + * + * If you want to reference an already existing deployment configuration, + * or one defined in a different CDK Stack, + * use the {@link LambdaDeploymentConfig#fromLambdaDeploymentConfigName} method. + * * The default, pre-defined Configurations are available as constants on the {@link LambdaDeploymentConfig} class * (`LambdaDeploymentConfig.AllAtOnce`, `LambdaDeploymentConfig.Canary10Percent30Minutes`, etc.). - * - * Note: CloudFormation does not currently support creating custom lambda configs outside - * of using a custom resource. You can import custom deployment config created outside the - * CDK or via a custom resource with {@link LambdaDeploymentConfig#import}. */ -export interface ILambdaDeploymentConfig { - readonly deploymentConfigName: string; - readonly deploymentConfigArn: string; +export interface ILambdaDeploymentConfig extends IBaseDeploymentConfig { } /** @@ -28,46 +31,72 @@ export interface LambdaDeploymentConfigImportProps { readonly deploymentConfigName: string; } +/** + * Construction properties of {@link LambdaDeploymentConfig}. + */ +export interface LambdaDeploymentConfigProps extends BaseDeploymentConfigOptions { + /** + * The configuration that specifies how traffic is shifted from the 'blue' + * target group to the 'green' target group during a deployment. + * @default AllAtOnce + */ + readonly trafficRouting?: TrafficRouting; +} + /** * A custom Deployment Configuration for a Lambda Deployment Group. - * - * Note: This class currently stands as namespaced container of the default configurations - * until CloudFormation supports custom Lambda Deployment Configs. Until then it is closed - * (private constructor) and does not extend {@link Construct} - * * @resource AWS::CodeDeploy::DeploymentConfig */ -export class LambdaDeploymentConfig { - public static readonly ALL_AT_ONCE = deploymentConfig('CodeDeployDefault.LambdaAllAtOnce'); - public static readonly CANARY_10PERCENT_30MINUTES = deploymentConfig('CodeDeployDefault.LambdaCanary10Percent30Minutes'); - public static readonly CANARY_10PERCENT_5MINUTES = deploymentConfig('CodeDeployDefault.LambdaCanary10Percent5Minutes'); - public static readonly CANARY_10PERCENT_10MINUTES = deploymentConfig('CodeDeployDefault.LambdaCanary10Percent10Minutes'); - public static readonly CANARY_10PERCENT_15MINUTES = deploymentConfig('CodeDeployDefault.LambdaCanary10Percent15Minutes'); - public static readonly LINEAR_10PERCENT_EVERY_10MINUTES = deploymentConfig('CodeDeployDefault.LambdaLinear10PercentEvery10Minutes'); - public static readonly LINEAR_10PERCENT_EVERY_1MINUTE = deploymentConfig('CodeDeployDefault.LambdaLinear10PercentEvery1Minute'); - public static readonly LINEAR_10PERCENT_EVERY_2MINUTES = deploymentConfig('CodeDeployDefault.LambdaLinear10PercentEvery2Minutes'); - public static readonly LINEAR_10PERCENT_EVERY_3MINUTES = deploymentConfig('CodeDeployDefault.LambdaLinear10PercentEvery3Minutes'); +export class LambdaDeploymentConfig extends BaseDeploymentConfig implements ILambdaDeploymentConfig { + /** CodeDeploy predefined deployment configuration that shifts all traffic to the updated Lambda function at once. */ + public static readonly ALL_AT_ONCE = LambdaDeploymentConfig.deploymentConfig('CodeDeployDefault.LambdaAllAtOnce'); + /** CodeDeploy predefined deployment configuration that shifts 10 percent of traffic in the first increment. The remaining 90 percent is deployed 30 minutes later. */ + public static readonly CANARY_10PERCENT_30MINUTES = LambdaDeploymentConfig.deploymentConfig('CodeDeployDefault.LambdaCanary10Percent30Minutes'); + /** CodeDeploy predefined deployment configuration that shifts 10 percent of traffic in the first increment. The remaining 90 percent is deployed five minutes later. */ + public static readonly CANARY_10PERCENT_5MINUTES = LambdaDeploymentConfig.deploymentConfig('CodeDeployDefault.LambdaCanary10Percent5Minutes'); + /** CodeDeploy predefined deployment configuration that shifts 10 percent of traffic in the first increment. The remaining 90 percent is deployed 10 minutes later. */ + public static readonly CANARY_10PERCENT_10MINUTES = LambdaDeploymentConfig.deploymentConfig('CodeDeployDefault.LambdaCanary10Percent10Minutes'); + /** CodeDeploy predefined deployment configuration that shifts 10 percent of traffic in the first increment. The remaining 90 percent is deployed 15 minutes later. */ + public static readonly CANARY_10PERCENT_15MINUTES = LambdaDeploymentConfig.deploymentConfig('CodeDeployDefault.LambdaCanary10Percent15Minutes'); + /** CodeDeploy predefined deployment configuration that shifts 10 percent of traffic every 10 minutes until all traffic is shifted. */ + public static readonly LINEAR_10PERCENT_EVERY_10MINUTES = LambdaDeploymentConfig.deploymentConfig('CodeDeployDefault.LambdaLinear10PercentEvery10Minutes'); + /** CodeDeploy predefined deployment configuration that shifts 10 percent of traffic every minute until all traffic is shifted. */ + public static readonly LINEAR_10PERCENT_EVERY_1MINUTE = LambdaDeploymentConfig.deploymentConfig('CodeDeployDefault.LambdaLinear10PercentEvery1Minute'); + /** CodeDeploy predefined deployment configuration that shifts 10 percent of traffic every two minutes until all traffic is shifted. */ + public static readonly LINEAR_10PERCENT_EVERY_2MINUTES = LambdaDeploymentConfig.deploymentConfig('CodeDeployDefault.LambdaLinear10PercentEvery2Minutes'); + /** CodeDeploy predefined deployment configuration that shifts 10 percent of traffic every three minutes until all traffic is shifted. */ + public static readonly LINEAR_10PERCENT_EVERY_3MINUTES = LambdaDeploymentConfig.deploymentConfig('CodeDeployDefault.LambdaLinear10PercentEvery3Minutes'); + + /** + * Import a Deployment Configuration for a Lambda Deployment Group defined outside the CDK. + * + * @param scope the parent Construct for this new Construct + * @param id the logical ID of this new Construct + * @param lambdaDeploymentConfigName the name of the Lambda Deployment Configuration to import + * @returns a Construct representing a reference to an existing Lambda Deployment Configuration + */ + public static fromLambdaDeploymentConfigName(scope: Construct, id: string, lambdaDeploymentConfigName: string): ILambdaDeploymentConfig { + return this.fromDeploymentConfigName(scope, id, lambdaDeploymentConfigName); + } /** - * Import a custom Deployment Configuration for a Lambda Deployment Group defined outside the CDK. + * Import a Deployment Configuration for a Lambda Deployment Group defined outside the CDK. * * @param _scope the parent Construct for this new Construct * @param _id the logical ID of this new Construct * @param props the properties of the referenced custom Deployment Configuration * @returns a Construct representing a reference to an existing custom Deployment Configuration + * @deprecated use `fromLambdaDeploymentConfigName` */ - public static import(_scope:Construct, _id: string, props: LambdaDeploymentConfigImportProps): ILambdaDeploymentConfig { - return deploymentConfig(props.deploymentConfigName); + public static import(_scope: Construct, _id: string, props: LambdaDeploymentConfigImportProps): ILambdaDeploymentConfig { + return this.fromLambdaDeploymentConfigName(_scope, _id, props.deploymentConfigName); } - private constructor() { - // nothing to do until CFN supports custom lambda deployment configurations + public constructor(scope: Construct, id: string, props?: LambdaDeploymentConfigProps) { + super(scope, id, { + ...props, + computePlatform: ComputePlatform.LAMBDA, + trafficRouting: props?.trafficRouting ?? TrafficRouting.allAtOnce(), + }); } } - -function deploymentConfig(name: string): ILambdaDeploymentConfig { - return { - deploymentConfigName: name, - deploymentConfigArn: arnForDeploymentConfig(name), - }; -} diff --git a/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-config.ts b/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-config.ts index 7ddb0ec1d37d9..97a9357cbe0bc 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-config.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-config.ts @@ -1,7 +1,6 @@ -import * as cdk from '@aws-cdk/core'; import { Construct } from 'constructs'; -import { CfnDeploymentConfig } from '../codedeploy.generated'; -import { arnForDeploymentConfig, validateName } from '../utils'; +import { BaseDeploymentConfig, BaseDeploymentConfigOptions, IBaseDeploymentConfig } from '../base-deployment-config'; +import { MinimumHealthyHosts } from '../host-health-config'; /** * The Deployment Configuration of an EC2/on-premise Deployment Group. @@ -10,64 +9,13 @@ import { arnForDeploymentConfig, validateName } from '../utils'; * To create a custom Deployment Configuration, * instantiate the {@link ServerDeploymentConfig} Construct. */ -export interface IServerDeploymentConfig { - /** - * @attribute - */ - readonly deploymentConfigName: string; - - /** - * @attribute - */ - readonly deploymentConfigArn: string; -} - -/** - * Minimum number of healthy hosts for a server deployment. - */ -export class MinimumHealthyHosts { - - /** - * The minimum healhty hosts threshold expressed as an absolute number. - */ - public static count(value: number): MinimumHealthyHosts { - return new MinimumHealthyHosts({ - type: 'HOST_COUNT', - value, - }); - } - - /** - * The minmum healhty hosts threshold expressed as a percentage of the fleet. - */ - public static percentage(value: number): MinimumHealthyHosts { - return new MinimumHealthyHosts({ - type: 'FLEET_PERCENT', - value, - }); - } - - private constructor(private readonly json: CfnDeploymentConfig.MinimumHealthyHostsProperty) { } - - /** - * @internal - */ - public get _json() { - return this.json; - } +export interface IServerDeploymentConfig extends IBaseDeploymentConfig { } /** * Construction properties of {@link ServerDeploymentConfig}. */ -export interface ServerDeploymentConfigProps { - /** - * The physical, human-readable name of the Deployment Configuration. - * - * @default a name will be auto-generated - */ - readonly deploymentConfigName?: string; - +export interface ServerDeploymentConfigProps extends BaseDeploymentConfigOptions { /** * Minimum number of healthy hosts. */ @@ -79,10 +27,25 @@ export interface ServerDeploymentConfigProps { * * @resource AWS::CodeDeploy::DeploymentConfig */ -export class ServerDeploymentConfig extends cdk.Resource implements IServerDeploymentConfig { - public static readonly ONE_AT_A_TIME = deploymentConfig('CodeDeployDefault.OneAtATime'); - public static readonly HALF_AT_A_TIME = deploymentConfig('CodeDeployDefault.HalfAtATime'); - public static readonly ALL_AT_ONCE = deploymentConfig('CodeDeployDefault.AllAtOnce'); +export class ServerDeploymentConfig extends BaseDeploymentConfig implements IServerDeploymentConfig { + /** + * The CodeDeployDefault.OneAtATime predefined deployment configuration for EC2/on-premises compute platform + * + * @see https://docs.aws.amazon.com/codedeploy/latest/userguide/deployment-configurations.html#deployment-configuration-server + */ + public static readonly ONE_AT_A_TIME = ServerDeploymentConfig.deploymentConfig('CodeDeployDefault.OneAtATime'); + /** + * The CodeDeployDefault.HalfAtATime predefined deployment configuration for EC2/on-premises compute platform + * + * @see https://docs.aws.amazon.com/codedeploy/latest/userguide/deployment-configurations.html#deployment-configuration-server + */ + public static readonly HALF_AT_A_TIME = ServerDeploymentConfig.deploymentConfig('CodeDeployDefault.HalfAtATime'); + /** + * The CodeDeployDefault.AllAtOnce predefined deployment configuration for EC2/on-premises compute platform + * + * @see https://docs.aws.amazon.com/codedeploy/latest/userguide/deployment-configurations.html#deployment-configuration-server + */ + public static readonly ALL_AT_ONCE = ServerDeploymentConfig.deploymentConfig('CodeDeployDefault.AllAtOnce'); /** * Import a custom Deployment Configuration for an EC2/on-premise Deployment Group defined either outside the CDK app, @@ -97,37 +60,10 @@ export class ServerDeploymentConfig extends cdk.Resource implements IServerDeplo scope: Construct, id: string, serverDeploymentConfigName: string): IServerDeploymentConfig { - - ignore(scope); - ignore(id); - return deploymentConfig(serverDeploymentConfigName); + return this.fromDeploymentConfigName(scope, id, serverDeploymentConfigName); } - public readonly deploymentConfigName: string; - public readonly deploymentConfigArn: string; - constructor(scope: Construct, id: string, props: ServerDeploymentConfigProps) { - super(scope, id, { - physicalName: props.deploymentConfigName, - }); - - const resource = new CfnDeploymentConfig(this, 'Resource', { - deploymentConfigName: this.physicalName, - minimumHealthyHosts: props.minimumHealthyHosts._json, - }); - - this.deploymentConfigName = resource.ref; - this.deploymentConfigArn = arnForDeploymentConfig(this.deploymentConfigName); - - this.node.addValidation({ validate: () => validateName('Deployment config', this.physicalName) }); + super(scope, id, props); } } - -function deploymentConfig(name: string): IServerDeploymentConfig { - return { - deploymentConfigName: name, - deploymentConfigArn: arnForDeploymentConfig(name), - }; -} - -function ignore(_x: any) { return; } diff --git a/packages/@aws-cdk/aws-codedeploy/lib/traffic-routing-config.ts b/packages/@aws-cdk/aws-codedeploy/lib/traffic-routing-config.ts new file mode 100644 index 0000000000000..2aa86c8e1f40f --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/lib/traffic-routing-config.ts @@ -0,0 +1,194 @@ +import { Duration } from '@aws-cdk/core'; +import { Construct } from 'constructs'; + +/** + * Represents the structure to pass into the underlying CfnDeploymentConfig class. + */ +export interface TrafficRoutingConfig { + /** + * The type of traffic shifting ( `TimeBasedCanary` or `TimeBasedLinear` ) used by a deployment configuration. + */ + readonly type: string; + + /** + * A configuration that shifts traffic from one version of a Lambda function or ECS task set to another in two increments. + * @default none + */ + readonly timeBasedCanary?: CanaryTrafficRoutingConfig; + + /** + * A configuration that shifts traffic from one version of a Lambda function or Amazon ECS task set to another in equal increments, with an equal number of minutes between each increment. + * @default none + */ + readonly timeBasedLinear?: LinearTrafficRoutingConfig; +} + +/** + * Represents the configuration specific to canary traffic shifting. + */ +export interface CanaryTrafficRoutingConfig { + /** + * The number of minutes between the first and second traffic shifts of a `TimeBasedCanary` deployment. + */ + readonly canaryInterval: number; + + /** + * The percentage of traffic to shift in the first increment of a `TimeBasedCanary` deployment. + */ + readonly canaryPercentage: number; +} + +/** + * Represents the configuration specific to linear traffic shifting. + */ +export interface LinearTrafficRoutingConfig { + /** + * The number of minutes between each incremental traffic shift of a `TimeBasedLinear` deployment. + */ + readonly linearInterval: number; + + /** + * The percentage of traffic that is shifted at the start of each increment of a `TimeBasedLinear` deployment. + */ + readonly linearPercentage: number; +} + +/** + * Represents how traffic is shifted during a CodeDeploy deployment. + */ +export abstract class TrafficRouting { + /** + * Shifts 100% of traffic in a single shift. + */ + public static allAtOnce(): TrafficRouting { + return new AllAtOnceTrafficRouting(); + } + + /** + * Shifts a specified percentage of traffic, waits for a specified amount of time, then shifts the rest of traffic. + */ + public static timeBasedCanary(props: TimeBasedCanaryTrafficRoutingProps): TrafficRouting { + return new TimeBasedCanaryTrafficRouting(props); + } + + /** + * Keeps shifting a specified percentage of traffic until reaching 100%, waiting for a specified amount of time in between each traffic shift. + */ + public static timeBasedLinear(props: TimeBasedLinearTrafficRoutingProps): TrafficRouting { + return new TimeBasedLinearTrafficRouting(props); + } + + /** + * Returns the traffic routing configuration. + */ + public abstract bind(scope: Construct): TrafficRoutingConfig; +} + +/** + * Common properties of traffic shifting routing configurations + */ +export interface BaseTrafficShiftingConfigProps { + /** + * The amount of time between traffic shifts. + */ + readonly interval: Duration; + + /** + * The percentage to increase traffic on each traffic shift. + */ + readonly percentage: number; +} + +/** + * Define a traffic routing config of type 'AllAtOnce'. + */ +export class AllAtOnceTrafficRouting extends TrafficRouting { + constructor() { + super(); + } + + /** + * Return a TrafficRoutingConfig of type `AllAtOnce`. + */ + bind(_scope: Construct): TrafficRoutingConfig { + return { + type: 'AllAtOnce', + }; + } +} + +/** + * Construction properties for {@link TimeBasedCanaryTrafficRouting}. + */ +export interface TimeBasedCanaryTrafficRoutingProps extends BaseTrafficShiftingConfigProps {} + +/** + * Define a traffic routing config of type 'TimeBasedCanary'. + */ +export class TimeBasedCanaryTrafficRouting extends TrafficRouting { + /** + * The amount of time between additional traffic shifts. + */ + readonly interval: Duration; + /** + * The percentage to increase traffic on each traffic shift. + */ + readonly percentage: number; + + constructor(props: TimeBasedCanaryTrafficRoutingProps) { + super(); + this.interval = props.interval; + this.percentage = props.percentage; + } + + /** + * Return a TrafficRoutingConfig of type `TimeBasedCanary`. + */ + bind(_scope: Construct): TrafficRoutingConfig { + return { + type: 'TimeBasedCanary', + timeBasedCanary: { + canaryInterval: this.interval.toMinutes(), + canaryPercentage: this.percentage, + }, + }; + } +} + +/** + * Construction properties for {@link TimeBasedLinearTrafficRouting}. + */ +export interface TimeBasedLinearTrafficRoutingProps extends BaseTrafficShiftingConfigProps {} + +/** + * Define a traffic routing config of type 'TimeBasedLinear'. + */ +export class TimeBasedLinearTrafficRouting extends TrafficRouting { + /** + * The amount of time between additional traffic shifts. + */ + readonly interval: Duration; + /** + * The percentage to increase traffic on each traffic shift. + */ + readonly percentage: number; + + constructor(props: TimeBasedLinearTrafficRoutingProps) { + super(); + this.interval = props.interval; + this.percentage = props.percentage; + } + + /** + * Return a TrafficRoutingConfig of type `TimeBasedLinear`. + */ + bind(_scope: Construct): TrafficRoutingConfig { + return { + type: 'TimeBasedLinear', + timeBasedLinear: { + linearInterval: this.interval.toMinutes(), + linearPercentage: this.percentage, + }, + }; + } +} diff --git a/packages/@aws-cdk/aws-codedeploy/package.json b/packages/@aws-cdk/aws-codedeploy/package.json index 9077c1ba954d2..4cb2301eb5303 100644 --- a/packages/@aws-cdk/aws-codedeploy/package.json +++ b/packages/@aws-cdk/aws-codedeploy/package.json @@ -86,6 +86,7 @@ "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/integ-runner": "0.0.0", + "@aws-cdk/integ-tests": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", "@types/jest": "^27.5.2", @@ -124,32 +125,23 @@ "awslint": { "exclude": [ "construct-interface-extends-iconstruct:@aws-cdk/aws-codedeploy.IServerDeploymentConfig", + "construct-interface-extends-iconstruct:@aws-cdk/aws-codedeploy.ILambdaDeploymentConfig", + "construct-interface-extends-iconstruct:@aws-cdk/aws-codedeploy.IEcsDeploymentConfig", "resource-interface-extends-resource:@aws-cdk/aws-codedeploy.IServerDeploymentConfig", - "docs-public-apis:@aws-cdk/aws-codedeploy.ServerDeploymentConfig.deploymentConfigArn", + "resource-interface-extends-resource:@aws-cdk/aws-codedeploy.ILambdaDeploymentConfig", + "resource-interface-extends-resource:@aws-cdk/aws-codedeploy.IEcsDeploymentConfig", + "no-static-import:@aws-cdk/aws-codedeploy.LambdaDeploymentConfig.import", "docs-public-apis:@aws-cdk/aws-codedeploy.IServerDeploymentGroup.role", "docs-public-apis:@aws-cdk/aws-codedeploy.InstanceTagSet.instanceTagGroups", "docs-public-apis:@aws-cdk/aws-codedeploy.LambdaApplication.applicationArn", "docs-public-apis:@aws-cdk/aws-codedeploy.LambdaApplication.applicationName", - "docs-public-apis:@aws-cdk/aws-codedeploy.LambdaDeploymentConfig.ALL_AT_ONCE", - "docs-public-apis:@aws-cdk/aws-codedeploy.LambdaDeploymentConfig.CANARY_10PERCENT_10MINUTES", - "docs-public-apis:@aws-cdk/aws-codedeploy.LambdaDeploymentConfig.CANARY_10PERCENT_15MINUTES", - "docs-public-apis:@aws-cdk/aws-codedeploy.LambdaDeploymentConfig.CANARY_10PERCENT_30MINUTES", - "docs-public-apis:@aws-cdk/aws-codedeploy.LambdaDeploymentConfig.CANARY_10PERCENT_5MINUTES", - "docs-public-apis:@aws-cdk/aws-codedeploy.LambdaDeploymentConfig.LINEAR_10PERCENT_EVERY_10MINUTES", - "docs-public-apis:@aws-cdk/aws-codedeploy.LambdaDeploymentConfig.LINEAR_10PERCENT_EVERY_1MINUTE", - "docs-public-apis:@aws-cdk/aws-codedeploy.LambdaDeploymentConfig.LINEAR_10PERCENT_EVERY_2MINUTES", - "docs-public-apis:@aws-cdk/aws-codedeploy.LambdaDeploymentConfig.LINEAR_10PERCENT_EVERY_3MINUTES", "docs-public-apis:@aws-cdk/aws-codedeploy.LambdaDeploymentGroup", "docs-public-apis:@aws-cdk/aws-codedeploy.LambdaDeploymentGroup.role", "docs-public-apis:@aws-cdk/aws-codedeploy.LoadBalancer.generation", "docs-public-apis:@aws-cdk/aws-codedeploy.LoadBalancer.name", "docs-public-apis:@aws-cdk/aws-codedeploy.ServerApplication.applicationArn", "docs-public-apis:@aws-cdk/aws-codedeploy.ServerApplication.applicationName", - "docs-public-apis:@aws-cdk/aws-codedeploy.ServerDeploymentConfig.ALL_AT_ONCE", - "docs-public-apis:@aws-cdk/aws-codedeploy.ServerDeploymentConfig.HALF_AT_A_TIME", - "docs-public-apis:@aws-cdk/aws-codedeploy.ServerDeploymentConfig.ONE_AT_A_TIME", "docs-public-apis:@aws-cdk/aws-codedeploy.IServerDeploymentGroup.autoScalingGroups", - "docs-public-apis:@aws-cdk/aws-codedeploy.ServerDeploymentConfig.deploymentConfigName", "docs-public-apis:@aws-cdk/aws-codedeploy.ServerDeploymentGroup.application", "docs-public-apis:@aws-cdk/aws-codedeploy.ServerDeploymentGroup.deploymentConfig", "docs-public-apis:@aws-cdk/aws-codedeploy.ServerDeploymentGroup.deploymentGroupArn", @@ -158,12 +150,8 @@ "docs-public-apis:@aws-cdk/aws-codedeploy.ServerDeploymentGroup.role", "docs-public-apis:@aws-cdk/aws-codedeploy.ILambdaApplication.applicationArn", "docs-public-apis:@aws-cdk/aws-codedeploy.ILambdaApplication.applicationName", - "docs-public-apis:@aws-cdk/aws-codedeploy.ILambdaDeploymentConfig.deploymentConfigArn", - "docs-public-apis:@aws-cdk/aws-codedeploy.ILambdaDeploymentConfig.deploymentConfigName", "docs-public-apis:@aws-cdk/aws-codedeploy.IServerApplication.applicationArn", "docs-public-apis:@aws-cdk/aws-codedeploy.IServerApplication.applicationName", - "docs-public-apis:@aws-cdk/aws-codedeploy.IServerDeploymentConfig.deploymentConfigArn", - "docs-public-apis:@aws-cdk/aws-codedeploy.IServerDeploymentConfig.deploymentConfigName", "docs-public-apis:@aws-cdk/aws-codedeploy.IServerDeploymentGroup", "docs-public-apis:@aws-cdk/aws-codedeploy.IServerDeploymentGroup.application", "docs-public-apis:@aws-cdk/aws-codedeploy.IServerDeploymentGroup.deploymentConfig", @@ -171,11 +159,8 @@ "docs-public-apis:@aws-cdk/aws-codedeploy.IServerDeploymentGroup.deploymentGroupName", "docs-public-apis:@aws-cdk/aws-codedeploy.EcsApplication.applicationArn", "docs-public-apis:@aws-cdk/aws-codedeploy.EcsApplication.applicationName", - "docs-public-apis:@aws-cdk/aws-codedeploy.EcsDeploymentConfig.ALL_AT_ONCE", "docs-public-apis:@aws-cdk/aws-codedeploy.IEcsApplication.applicationArn", "docs-public-apis:@aws-cdk/aws-codedeploy.IEcsApplication.applicationName", - "docs-public-apis:@aws-cdk/aws-codedeploy.IEcsDeploymentConfig.deploymentConfigArn", - "docs-public-apis:@aws-cdk/aws-codedeploy.IEcsDeploymentConfig.deploymentConfigName", "props-physical-name:@aws-cdk/aws-codedeploy.CustomLambdaDeploymentConfigProps" ] }, diff --git a/packages/@aws-cdk/aws-codedeploy/test/ecs/application.test.ts b/packages/@aws-cdk/aws-codedeploy/test/ecs/application.test.ts index a5661c3538f14..c907bb4c38740 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/ecs/application.test.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/ecs/application.test.ts @@ -43,4 +43,13 @@ describe('CodeDeploy ECS Application', () => { expect(() => app.synth()).toThrow('Application name: "my name" can only contain letters (a-z, A-Z), numbers (0-9), periods (.), underscores (_), + (plus signs), = (equals signs), , (commas), @ (at signs), - (minus signs).'); }); + + test('can be imported', () => { + const stack = new cdk.Stack(); + + const application = codedeploy.EcsApplication.fromEcsApplicationName(stack, 'MyApp', 'MyApp'); + + expect(application).not.toEqual(undefined); + expect(application.applicationName).toEqual('MyApp'); + }); }); diff --git a/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/EcsDeploymentConfigTestDefaultTestDeployAssertA6573788.assets.json b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/EcsDeploymentConfigTestDefaultTestDeployAssertA6573788.assets.json new file mode 100644 index 0000000000000..fa8c0147c3faa --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/EcsDeploymentConfigTestDefaultTestDeployAssertA6573788.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "EcsDeploymentConfigTestDefaultTestDeployAssertA6573788.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/EcsDeploymentConfigTestDefaultTestDeployAssertA6573788.template.json b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/EcsDeploymentConfigTestDefaultTestDeployAssertA6573788.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/EcsDeploymentConfigTestDefaultTestDeployAssertA6573788.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/aws-cdk-codedeploy-ecs-config.assets.json b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/aws-cdk-codedeploy-ecs-config.assets.json new file mode 100644 index 0000000000000..1ba080dd4d5d9 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/aws-cdk-codedeploy-ecs-config.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "d38ba84a2fd21dc7efb6bafa53ff6671f4f79544905c0de9f396847a2d782a1d": { + "source": { + "path": "aws-cdk-codedeploy-ecs-config.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "d38ba84a2fd21dc7efb6bafa53ff6671f4f79544905c0de9f396847a2d782a1d.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/aws-cdk-codedeploy-ecs-config.template.json b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/aws-cdk-codedeploy-ecs-config.template.json new file mode 100644 index 0000000000000..5d1696e08036f --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/aws-cdk-codedeploy-ecs-config.template.json @@ -0,0 +1,51 @@ +{ + "Resources": { + "LinearConfig531CF4AA": { + "Type": "AWS::CodeDeploy::DeploymentConfig", + "Properties": { + "ComputePlatform": "ECS", + "TrafficRoutingConfig": { + "TimeBasedLinear": { + "LinearInterval": 1, + "LinearPercentage": 5 + }, + "Type": "TimeBasedLinear" + } + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} diff --git a/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/cdk.out new file mode 100644 index 0000000000000..8ecc185e9dbee --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/integ.json b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/integ.json new file mode 100644 index 0000000000000..8c91633c88ae6 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "21.0.0", + "testCases": { + "EcsDeploymentConfigTest/DefaultTest": { + "stacks": [ + "aws-cdk-codedeploy-ecs-config" + ], + "assertionStack": "EcsDeploymentConfigTest/DefaultTest/DeployAssert", + "assertionStackName": "EcsDeploymentConfigTestDefaultTestDeployAssertA6573788" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/manifest.json new file mode 100644 index 0000000000000..c6dd83b5144dd --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/manifest.json @@ -0,0 +1,111 @@ +{ + "version": "21.0.0", + "artifacts": { + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + }, + "aws-cdk-codedeploy-ecs-config.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-cdk-codedeploy-ecs-config.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-cdk-codedeploy-ecs-config": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-cdk-codedeploy-ecs-config.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/d38ba84a2fd21dc7efb6bafa53ff6671f4f79544905c0de9f396847a2d782a1d.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-cdk-codedeploy-ecs-config.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "aws-cdk-codedeploy-ecs-config.assets" + ], + "metadata": { + "/aws-cdk-codedeploy-ecs-config/LinearConfig/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "LinearConfig531CF4AA" + } + ], + "/aws-cdk-codedeploy-ecs-config/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-cdk-codedeploy-ecs-config/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-cdk-codedeploy-ecs-config" + }, + "EcsDeploymentConfigTestDefaultTestDeployAssertA6573788.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "EcsDeploymentConfigTestDefaultTestDeployAssertA6573788.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "EcsDeploymentConfigTestDefaultTestDeployAssertA6573788": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "EcsDeploymentConfigTestDefaultTestDeployAssertA6573788.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "EcsDeploymentConfigTestDefaultTestDeployAssertA6573788.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "EcsDeploymentConfigTestDefaultTestDeployAssertA6573788.assets" + ], + "metadata": { + "/EcsDeploymentConfigTest/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/EcsDeploymentConfigTest/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "EcsDeploymentConfigTest/DefaultTest/DeployAssert" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/tree.json b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/tree.json new file mode 100644 index 0000000000000..58546bcc2f842 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/tree.json @@ -0,0 +1,99 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.95" + } + }, + "aws-cdk-codedeploy-ecs-config": { + "id": "aws-cdk-codedeploy-ecs-config", + "path": "aws-cdk-codedeploy-ecs-config", + "children": { + "LinearConfig": { + "id": "LinearConfig", + "path": "aws-cdk-codedeploy-ecs-config/LinearConfig", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-codedeploy-ecs-config/LinearConfig/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::CodeDeploy::DeploymentConfig", + "aws:cdk:cloudformation:props": { + "computePlatform": "ECS", + "deploymentConfigName": "awscdkcodedeployecsconfigLinearConfig12E7AC8B.EcsLinear5PercentEvery1Minutes", + "trafficRoutingConfig": { + "type": "TimeBasedLinear", + "timeBasedLinear": { + "linearInterval": 1, + "linearPercentage": 5 + } + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-codedeploy.CfnDeploymentConfig", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-codedeploy.EcsDeploymentConfig", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "EcsDeploymentConfigTest": { + "id": "EcsDeploymentConfigTest", + "path": "EcsDeploymentConfigTest", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "EcsDeploymentConfigTest/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "EcsDeploymentConfigTest/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.95" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "EcsDeploymentConfigTest/DefaultTest/DeployAssert", + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTest", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.test.ts b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.test.ts new file mode 100644 index 0000000000000..3dc660e10bb08 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.test.ts @@ -0,0 +1,142 @@ +import { Template } from '@aws-cdk/assertions'; +import * as cdk from '@aws-cdk/core'; +import * as codedeploy from '../../lib'; +import { TrafficRouting } from '../../lib'; + +/* eslint-disable quote-props */ + +let stack: cdk.Stack; + +beforeEach(() => { + stack = new cdk.Stack(); +}); + +test('can create default config', () => { + // WHEN + new codedeploy.EcsDeploymentConfig(stack, 'MyConfig'); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentConfig', { + 'ComputePlatform': 'ECS', + 'TrafficRoutingConfig': { + 'Type': 'AllAtOnce', + }, + }); +}); + +test('can create all-at-once config', () => { + // WHEN + new codedeploy.EcsDeploymentConfig(stack, 'MyConfig', { + trafficRouting: TrafficRouting.allAtOnce(), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentConfig', { + 'ComputePlatform': 'ECS', + 'TrafficRoutingConfig': { + 'Type': 'AllAtOnce', + }, + }); +}); + +test('can create linear config', () => { + // WHEN + new codedeploy.EcsDeploymentConfig(stack, 'MyConfig', { + trafficRouting: TrafficRouting.timeBasedLinear({ + interval: cdk.Duration.minutes(1), + percentage: 5, + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentConfig', { + 'ComputePlatform': 'ECS', + 'TrafficRoutingConfig': { + 'TimeBasedLinear': { + 'LinearInterval': 1, + 'LinearPercentage': 5, + }, + 'Type': 'TimeBasedLinear', + }, + }); +}); + +test('can create canary config', () => { + // WHEN + new codedeploy.EcsDeploymentConfig(stack, 'MyConfig', { + trafficRouting: TrafficRouting.timeBasedCanary({ + interval: cdk.Duration.minutes(1), + percentage: 5, + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentConfig', { + 'ComputePlatform': 'ECS', + 'TrafficRoutingConfig': { + 'TimeBasedCanary': { + 'CanaryInterval': 1, + 'CanaryPercentage': 5, + }, + 'Type': 'TimeBasedCanary', + }, + }); +}); + +test('can create a config with a specific name', () => { + // WHEN + new codedeploy.EcsDeploymentConfig(stack, 'MyConfig', { + deploymentConfigName: 'MyCanaryConfig', + trafficRouting: TrafficRouting.timeBasedCanary({ + interval: cdk.Duration.minutes(1), + percentage: 5, + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentConfig', { + 'ComputePlatform': 'ECS', + 'DeploymentConfigName': 'MyCanaryConfig', + 'TrafficRoutingConfig': { + 'TimeBasedCanary': { + 'CanaryInterval': 1, + 'CanaryPercentage': 5, + }, + 'Type': 'TimeBasedCanary', + }, + }); +}); + +test('can be imported', () => { + const deploymentConfig = codedeploy.EcsDeploymentConfig.fromEcsDeploymentConfigName(stack, 'MyDC', 'MyDC'); + + expect(deploymentConfig).not.toEqual(undefined); +}); + +test('fail with more than 100 characters in name', () => { + const app = new cdk.App(); + const stackWithApp = new cdk.Stack(app); + new codedeploy.EcsDeploymentConfig(stackWithApp, 'MyConfig', { + trafficRouting: TrafficRouting.timeBasedCanary({ + interval: cdk.Duration.minutes(1), + percentage: 5, + }), + deploymentConfigName: 'a'.repeat(101), + }); + + expect(() => app.synth()).toThrow(`Deployment config name: "${'a'.repeat(101)}" can be a max of 100 characters.`); +}); + +test('fail with unallowed characters in name', () => { + const app = new cdk.App(); + const stackWithApp = new cdk.Stack(app); + new codedeploy.EcsDeploymentConfig(stackWithApp, 'MyConfig', { + trafficRouting: TrafficRouting.timeBasedCanary({ + interval: cdk.Duration.minutes(1), + percentage: 5, + }), + deploymentConfigName: 'my name', + }); + + expect(() => app.synth()).toThrow('Deployment config name: "my name" can only contain letters (a-z, A-Z), numbers (0-9), periods (.), underscores (_), + (plus signs), = (equals signs), , (commas), @ (at signs), - (minus signs).'); +}); diff --git a/packages/@aws-cdk/aws-codedeploy/test/ecs/integ.deployment-config.ts b/packages/@aws-cdk/aws-codedeploy/test/ecs/integ.deployment-config.ts new file mode 100644 index 0000000000000..651ed7efa0962 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/ecs/integ.deployment-config.ts @@ -0,0 +1,19 @@ +import * as cdk from '@aws-cdk/core'; +import * as integ from '@aws-cdk/integ-tests'; +import * as codedeploy from '../../lib'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'aws-cdk-codedeploy-ecs-config'); + +new codedeploy.EcsDeploymentConfig(stack, 'LinearConfig', { + trafficRouting: codedeploy.TrafficRouting.timeBasedLinear({ + interval: cdk.Duration.minutes(1), + percentage: 5, + }), +}); + +new integ.IntegTest(app, 'EcsDeploymentConfigTest', { + testCases: [stack], +}); + +app.synth(); diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/application.test.ts b/packages/@aws-cdk/aws-codedeploy/test/lambda/application.test.ts index 4b870c53c0e1d..7832ef202c949 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/lambda/application.test.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/application.test.ts @@ -41,4 +41,13 @@ describe('CodeDeploy Lambda Application', () => { expect(() => app.synth()).toThrow('Application name: "my name" can only contain letters (a-z, A-Z), numbers (0-9), periods (.), underscores (_), + (plus signs), = (equals signs), , (commas), @ (at signs), - (minus signs).'); }); + + test('can be imported', () => { + const stack = new cdk.Stack(); + + const application = codedeploy.LambdaApplication.fromLambdaApplicationName(stack, 'MyApp', 'MyApp'); + + expect(application).not.toEqual(undefined); + expect(application.applicationName).toEqual('MyApp'); + }); }); diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/custom-deployment-config.test.ts b/packages/@aws-cdk/aws-codedeploy/test/lambda/custom-deployment-config.test.ts index 4498ed8522f73..20af6c7147751 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/lambda/custom-deployment-config.test.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/custom-deployment-config.test.ts @@ -1,5 +1,6 @@ import { Template } from '@aws-cdk/assertions'; import * as lambda from '@aws-cdk/aws-lambda'; +import { testDeprecated } from '@aws-cdk/cdk-build-tools'; import * as cdk from '@aws-cdk/core'; import * as codedeploy from '../../lib'; @@ -30,7 +31,7 @@ beforeEach(() => { }); -test('custom resource created', () => { +testDeprecated('custom resource created', () => { // WHEN const config = new codedeploy.CustomLambdaDeploymentConfig(stack, 'CustomConfig', { type: codedeploy.CustomLambdaDeploymentConfigType.CANARY, @@ -75,7 +76,7 @@ test('custom resource created', () => { }); }); -test('custom resource created with specific name', () => { +testDeprecated('custom resource created with specific name', () => { // WHEN const config = new codedeploy.CustomLambdaDeploymentConfig(stack, 'CustomConfig', { type: codedeploy.CustomLambdaDeploymentConfigType.CANARY, @@ -97,7 +98,7 @@ test('custom resource created with specific name', () => { }); }); -test('fail with more than 100 characters in name', () => { +testDeprecated('fail with more than 100 characters in name', () => { const app = new cdk.App(); const stackWithApp = new cdk.Stack(app); new codedeploy.CustomLambdaDeploymentConfig(stackWithApp, 'CustomConfig', { @@ -110,7 +111,7 @@ test('fail with more than 100 characters in name', () => { expect(() => app.synth()).toThrow(`Deployment config name: "${'a'.repeat(101)}" can be a max of 100 characters.`); }); -test('fail with unallowed characters in name', () => { +testDeprecated('fail with unallowed characters in name', () => { const app = new cdk.App(); const stackWithApp = new cdk.Stack(app); new codedeploy.CustomLambdaDeploymentConfig(stackWithApp, 'CustomConfig', { @@ -123,7 +124,7 @@ test('fail with unallowed characters in name', () => { expect(() => app.synth()).toThrow('Deployment config name: "my name" can only contain letters (a-z, A-Z), numbers (0-9), periods (.), underscores (_), + (plus signs), = (equals signs), , (commas), @ (at signs), - (minus signs).'); }); -test('can create linear custom config', () => { +testDeprecated('can create linear custom config', () => { // WHEN const config = new codedeploy.CustomLambdaDeploymentConfig(stack, 'CustomConfig', { type: codedeploy.CustomLambdaDeploymentConfigType.LINEAR, @@ -142,7 +143,7 @@ test('can create linear custom config', () => { }); }); -test('can create canary custom config', () => { +testDeprecated('can create canary custom config', () => { // WHEN const config = new codedeploy.CustomLambdaDeploymentConfig(stack, 'CustomConfig', { type: codedeploy.CustomLambdaDeploymentConfigType.CANARY, @@ -161,7 +162,7 @@ test('can create canary custom config', () => { }); }); -test('dependency on the config exists to ensure ordering', () => { +testDeprecated('dependency on the config exists to ensure ordering', () => { // WHEN const config = new codedeploy.CustomLambdaDeploymentConfig(stack, 'CustomConfig', { type: codedeploy.CustomLambdaDeploymentConfigType.CANARY, diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6.assets.json b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6.assets.json new file mode 100644 index 0000000000000..bd67c83d14603 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6.template.json b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/aws-cdk-codedeploy-lambda-config.assets.json b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/aws-cdk-codedeploy-lambda-config.assets.json new file mode 100644 index 0000000000000..d861f05e2c9fb --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/aws-cdk-codedeploy-lambda-config.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "681328df6a987c681a4d5cafad61efbf97e5934c41842c8fc92c113ee39a6b28": { + "source": { + "path": "aws-cdk-codedeploy-lambda-config.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "681328df6a987c681a4d5cafad61efbf97e5934c41842c8fc92c113ee39a6b28.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/aws-cdk-codedeploy-lambda-config.template.json b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/aws-cdk-codedeploy-lambda-config.template.json new file mode 100644 index 0000000000000..2c5dddc618a4a --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/aws-cdk-codedeploy-lambda-config.template.json @@ -0,0 +1,51 @@ +{ + "Resources": { + "LinearConfig531CF4AA": { + "Type": "AWS::CodeDeploy::DeploymentConfig", + "Properties": { + "ComputePlatform": "Lambda", + "TrafficRoutingConfig": { + "TimeBasedLinear": { + "LinearInterval": 1, + "LinearPercentage": 5 + }, + "Type": "TimeBasedLinear" + } + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/cdk.out new file mode 100644 index 0000000000000..8ecc185e9dbee --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/integ.json b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/integ.json new file mode 100644 index 0000000000000..094127896a86e --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "21.0.0", + "testCases": { + "LambdaDeploymentConfigTest/DefaultTest": { + "stacks": [ + "aws-cdk-codedeploy-lambda-config" + ], + "assertionStack": "LambdaDeploymentConfigTest/DefaultTest/DeployAssert", + "assertionStackName": "LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/manifest.json new file mode 100644 index 0000000000000..c6fc338e7e046 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/manifest.json @@ -0,0 +1,111 @@ +{ + "version": "21.0.0", + "artifacts": { + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + }, + "aws-cdk-codedeploy-lambda-config.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-cdk-codedeploy-lambda-config.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-cdk-codedeploy-lambda-config": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-cdk-codedeploy-lambda-config.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/681328df6a987c681a4d5cafad61efbf97e5934c41842c8fc92c113ee39a6b28.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-cdk-codedeploy-lambda-config.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "aws-cdk-codedeploy-lambda-config.assets" + ], + "metadata": { + "/aws-cdk-codedeploy-lambda-config/LinearConfig/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "LinearConfig531CF4AA" + } + ], + "/aws-cdk-codedeploy-lambda-config/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-cdk-codedeploy-lambda-config/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-cdk-codedeploy-lambda-config" + }, + "LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6.assets" + ], + "metadata": { + "/LambdaDeploymentConfigTest/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/LambdaDeploymentConfigTest/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "LambdaDeploymentConfigTest/DefaultTest/DeployAssert" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/tree.json b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/tree.json new file mode 100644 index 0000000000000..9f9a449ffe63b --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/tree.json @@ -0,0 +1,99 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.95" + } + }, + "aws-cdk-codedeploy-lambda-config": { + "id": "aws-cdk-codedeploy-lambda-config", + "path": "aws-cdk-codedeploy-lambda-config", + "children": { + "LinearConfig": { + "id": "LinearConfig", + "path": "aws-cdk-codedeploy-lambda-config/LinearConfig", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-codedeploy-lambda-config/LinearConfig/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::CodeDeploy::DeploymentConfig", + "aws:cdk:cloudformation:props": { + "computePlatform": "Lambda", + "deploymentConfigName": "awscdkcodedeploylambdaconfigLinearConfig655064A4.LambdaLinear5PercentEvery1Minutes", + "trafficRoutingConfig": { + "type": "TimeBasedLinear", + "timeBasedLinear": { + "linearInterval": 1, + "linearPercentage": 5 + } + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-codedeploy.CfnDeploymentConfig", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-codedeploy.LambdaDeploymentConfig", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "LambdaDeploymentConfigTest": { + "id": "LambdaDeploymentConfigTest", + "path": "LambdaDeploymentConfigTest", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "LambdaDeploymentConfigTest/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "LambdaDeploymentConfigTest/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.95" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "LambdaDeploymentConfigTest/DefaultTest/DeployAssert", + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTest", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.test.ts b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.test.ts new file mode 100644 index 0000000000000..b50e897bf366d --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.test.ts @@ -0,0 +1,142 @@ +import { Template } from '@aws-cdk/assertions'; +import * as cdk from '@aws-cdk/core'; +import * as codedeploy from '../../lib'; +import { TrafficRouting } from '../../lib'; + +/* eslint-disable quote-props */ + +let stack: cdk.Stack; + +beforeEach(() => { + stack = new cdk.Stack(); +}); + +test('can create default config', () => { + // WHEN + new codedeploy.LambdaDeploymentConfig(stack, 'MyConfig'); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentConfig', { + 'ComputePlatform': 'Lambda', + 'TrafficRoutingConfig': { + 'Type': 'AllAtOnce', + }, + }); +}); + +test('can create all-at-once config', () => { + // WHEN + new codedeploy.LambdaDeploymentConfig(stack, 'MyConfig', { + trafficRouting: TrafficRouting.allAtOnce(), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentConfig', { + 'ComputePlatform': 'Lambda', + 'TrafficRoutingConfig': { + 'Type': 'AllAtOnce', + }, + }); +}); + +test('can create linear config', () => { + // WHEN + new codedeploy.LambdaDeploymentConfig(stack, 'MyConfig', { + trafficRouting: TrafficRouting.timeBasedLinear({ + interval: cdk.Duration.minutes(1), + percentage: 5, + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentConfig', { + 'ComputePlatform': 'Lambda', + 'TrafficRoutingConfig': { + 'TimeBasedLinear': { + 'LinearInterval': 1, + 'LinearPercentage': 5, + }, + 'Type': 'TimeBasedLinear', + }, + }); +}); + +test('can create canary config', () => { + // WHEN + new codedeploy.LambdaDeploymentConfig(stack, 'MyConfig', { + trafficRouting: TrafficRouting.timeBasedCanary({ + interval: cdk.Duration.minutes(1), + percentage: 5, + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentConfig', { + 'ComputePlatform': 'Lambda', + 'TrafficRoutingConfig': { + 'TimeBasedCanary': { + 'CanaryInterval': 1, + 'CanaryPercentage': 5, + }, + 'Type': 'TimeBasedCanary', + }, + }); +}); + +test('can create a config with a specific name', () => { + // WHEN + new codedeploy.LambdaDeploymentConfig(stack, 'MyConfig', { + deploymentConfigName: 'MyCanaryConfig', + trafficRouting: TrafficRouting.timeBasedCanary({ + interval: cdk.Duration.minutes(1), + percentage: 5, + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentConfig', { + 'ComputePlatform': 'Lambda', + 'DeploymentConfigName': 'MyCanaryConfig', + 'TrafficRoutingConfig': { + 'TimeBasedCanary': { + 'CanaryInterval': 1, + 'CanaryPercentage': 5, + }, + 'Type': 'TimeBasedCanary', + }, + }); +}); + +test('can be imported', () => { + const deploymentConfig = codedeploy.LambdaDeploymentConfig.fromLambdaDeploymentConfigName(stack, 'MyDC', 'MyDC'); + + expect(deploymentConfig).not.toEqual(undefined); +}); + +test('fail with more than 100 characters in name', () => { + const app = new cdk.App(); + const stackWithApp = new cdk.Stack(app); + new codedeploy.LambdaDeploymentConfig(stackWithApp, 'MyConfig', { + trafficRouting: TrafficRouting.timeBasedCanary({ + interval: cdk.Duration.minutes(1), + percentage: 5, + }), + deploymentConfigName: 'a'.repeat(101), + }); + + expect(() => app.synth()).toThrow(`Deployment config name: "${'a'.repeat(101)}" can be a max of 100 characters.`); +}); + +test('fail with unallowed characters in name', () => { + const app = new cdk.App(); + const stackWithApp = new cdk.Stack(app); + new codedeploy.LambdaDeploymentConfig(stackWithApp, 'MyConfig', { + trafficRouting: TrafficRouting.timeBasedCanary({ + interval: cdk.Duration.minutes(1), + percentage: 5, + }), + deploymentConfigName: 'my name', + }); + + expect(() => app.synth()).toThrow('Deployment config name: "my name" can only contain letters (a-z, A-Z), numbers (0-9), periods (.), underscores (_), + (plus signs), = (equals signs), , (commas), @ (at signs), - (minus signs).'); +}); diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-group.test.ts b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-group.test.ts index 9fc6049e9a2c5..959c98bbce139 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-group.test.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-group.test.ts @@ -4,6 +4,7 @@ import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; import * as cdk from '@aws-cdk/core'; import * as codedeploy from '../../lib'; +import { TrafficRouting } from '../../lib'; function mockFunction(stack: cdk.Stack, id: string) { return new lambda.Function(stack, id, { @@ -629,3 +630,31 @@ describe('imported with fromLambdaDeploymentGroupAttributes', () => { expect(importedGroup.deploymentConfig).toEqual(codedeploy.LambdaDeploymentConfig.CANARY_10PERCENT_5MINUTES); }); }); + +test('dependency on the config exists to ensure ordering', () => { + // WHEN + const stack = new cdk.Stack(); + const application = new codedeploy.LambdaApplication(stack, 'MyApp'); + const alias = mockAlias(stack); + const config = new codedeploy.LambdaDeploymentConfig(stack, 'MyConfig', { + trafficRouting: TrafficRouting.timeBasedCanary({ + interval: cdk.Duration.minutes(1), + percentage: 5, + }), + }); + new codedeploy.LambdaDeploymentGroup(stack, 'MyDG', { + application, + alias, + deploymentConfig: config, + }); + + // THEN + Template.fromStack(stack).hasResource('AWS::CodeDeploy::DeploymentGroup', { + Properties: { + DeploymentConfigName: stack.resolve(config.deploymentConfigName), + }, + DependsOn: [ + stack.getLogicalId(config.node.defaultChild as codedeploy.CfnDeploymentConfig), + ], + }); +}); diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-config.ts b/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-config.ts new file mode 100644 index 0000000000000..de578cb7eb648 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-config.ts @@ -0,0 +1,19 @@ +import * as cdk from '@aws-cdk/core'; +import * as integ from '@aws-cdk/integ-tests'; +import * as codedeploy from '../../lib'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'aws-cdk-codedeploy-lambda-config'); + +new codedeploy.LambdaDeploymentConfig(stack, 'LinearConfig', { + trafficRouting: codedeploy.TrafficRouting.timeBasedLinear({ + interval: cdk.Duration.minutes(1), + percentage: 5, + }), +}); + +new integ.IntegTest(app, 'LambdaDeploymentConfigTest', { + testCases: [stack], +}); + +app.synth(); From a2b7a4624638a458bfb6e8e09c67a77e48e1d167 Mon Sep 17 00:00:00 2001 From: Rohit Aggarwal Date: Fri, 30 Sep 2022 01:45:06 -0700 Subject: [PATCH 24/24] feat(servicecatalogappregistry): application-associator L2 Construct (#22024) new application-associator L2 Construct : This construct is responsible for following: * Create a new AppRegistry Application * Associate all stacks inside a cdk app scope * share an app registry application upon determining cross account stack. [ This only works for non environment agnostic stack] ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* Co-authored by: Santanu Ghosh --- .../aws-servicecatalogappregistry/README.md | 82 ++++++- .../lib/application-associator.ts | 100 +++++++++ .../lib/application.ts | 81 ++++++- .../lib/aspects/stack-associator.ts | 131 +++++++++++ .../lib/attribute-group.ts | 4 +- .../lib/index.ts | 1 + .../lib/private/utils.ts | 22 ++ .../package.json | 12 +- .../ApplicationAssociatorStack.assets.json | 19 ++ .../ApplicationAssociatorStack.template.json | 60 +++++ .../cdk.out | 1 + ...catalogappregistry-application.assets.json | 19 ++ ...talogappregistry-application.template.json | 48 ++++ .../integ.json | 14 ++ ...licationresourcesStack4399A149.assets.json | 19 ++ ...cationresourcesStack4399A149.template.json | 48 ++++ .../manifest.json | 209 ++++++++++++++++++ .../tree.json | 143 ++++++++++++ .../test/application-associator.test.ts | 196 ++++++++++++++++ .../test/application.test.ts | 131 ++++++++++- ...ation-associator.all-stacks-association.ts | 20 ++ 21 files changed, 1349 insertions(+), 11 deletions(-) create mode 100644 packages/@aws-cdk/aws-servicecatalogappregistry/lib/application-associator.ts create mode 100644 packages/@aws-cdk/aws-servicecatalogappregistry/lib/aspects/stack-associator.ts create mode 100644 packages/@aws-cdk/aws-servicecatalogappregistry/lib/private/utils.ts create mode 100644 packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/ApplicationAssociatorStack.assets.json create mode 100644 packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/ApplicationAssociatorStack.template.json create mode 100644 packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/integ-servicecatalogappregistry-application.assets.json create mode 100644 packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/integ-servicecatalogappregistry-application.template.json create mode 100644 packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/integ.json create mode 100644 packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/integservicecatalogappregistryapplicationresourcesStack4399A149.assets.json create mode 100644 packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/integservicecatalogappregistryapplicationresourcesStack4399A149.template.json create mode 100644 packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/manifest.json create mode 100644 packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/tree.json create mode 100644 packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.test.ts create mode 100644 packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application-associator.all-stacks-association.ts diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/README.md b/packages/@aws-cdk/aws-servicecatalogappregistry/README.md index e96a13bc8b2db..c65fd7a66b2a6 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/README.md +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/README.md @@ -21,12 +21,13 @@ -[AWS Service Catalog App Registry](https://docs.aws.amazon.com/servicecatalog/latest/adminguide/appregistry.html) +[AWS Service Catalog App Registry](https://docs.aws.amazon.com/servicecatalog/latest/adminguide/appregistry.html) enables organizations to create and manage repositores of applications and associated resources. ## Table Of Contents - [Application](#application) +- [Application-Associator](#application-associator) - [Attribute-Group](#attribute-group) - [Associations](#associations) - [Associating application with an attribute group](#attribute-group-association) @@ -44,11 +45,11 @@ import * as appreg from '@aws-cdk/aws-servicecatalogappregistry'; ## Application An AppRegistry application enables you to define your applications and associated resources. -The application name must be unique at the account level, but is mutable. +The application name must be unique at the account level and it's immutable. ```ts const application = new appreg.Application(this, 'MyFirstApplication', { - applicationName: 'MyFirstApplicationName', + applicationName: 'MyFirstApplicationName', description: 'description for my application', // the description is optional }); ``` @@ -64,6 +65,77 @@ const importedApplication = appreg.Application.fromApplicationArn( ); ``` +## Application-Associator + +If you want to create an Application named `MyAssociatedApplication` in account `123456789012` and region `us-east-1` +and want to associate all stacks in the `App` scope to `MyAssociatedApplication`, then use as shown in the example below: + +```ts +const app = new App(); +const associatedApp = new appreg.ApplicationAssociator(app, 'AssociatedApplication', { + applicationName: 'MyAssociatedApplication', + description: 'Testing associated application', + stackProps: { + stackName: 'MyAssociatedApplicationStack', + env: {account: '123456789012', region: 'us-east-1'}, + }, +}); +``` + +If you want to re-use an existing Application with ARN: `arn:aws:servicecatalog:us-east-1:123456789012:/applications/applicationId` +and want to associate all stacks in the `App` scope to your imported application, then use as shown in the example below: + +```ts +const app = new App(); +const associatedApp = new appreg.ApplicationAssociator(app, 'AssociatedApplication', { + applicationArnValue: 'arn:aws:servicecatalog:us-east-1:123456789012:/applications/applicationId', + stackProps: { + stackName: 'MyAssociatedApplicationStack', + }, +}); +``` + +If you are using CDK Pipelines to deploy your application, the application stacks will be inside Stages, and +ApplicationAssociator will not be able to find them. Call `associateStage` on each Stage object before adding it to the +Pipeline, as shown in the example below: + +```ts +import * as cdk from "@aws-cdk/core"; +import * as codepipeline from "@aws-cdk/pipelines"; +import * as codecommit from "@aws-cdk/aws-codecommit"; +declare const repo: codecommit.Repository; +declare const pipeline: codepipeline.CodePipeline; +declare const beta: cdk.Stage; +class ApplicationPipelineStack extends cdk.Stack { + constructor(scope: cdk.App, id: string, props: ApplicationPipelineStackProps) { + super(scope, id, props); + + //associate the stage to application associator. + props.application.associateStage(beta); + pipeline.addStage(beta); + } +}; + +interface ApplicationPipelineStackProps extends cdk.StackProps { + application: appreg.ApplicationAssociator; +}; + +const app = new App(); +const associatedApp = new appreg.ApplicationAssociator(app, 'AssociatedApplication', { + applicationName: 'MyPipelineAssociatedApplication', + description: 'Testing pipeline associated app', + stackProps: { + stackName: 'MyPipelineAssociatedApplicationStack', + env: {account: '123456789012', region: 'us-east-1'}, + }, +}); + +const cdkPipeline = new ApplicationPipelineStack(app, 'CDKApplicationPipelineStack', { + application: associatedApp, + env: {account: '123456789012', region: 'us-east-1'}, +}); +``` + ## Attribute Group An AppRegistry attribute group acts as a container for user-defined attributes for an application. @@ -71,7 +143,7 @@ Metadata is attached in a machine-readble format to integrate with automated wor ```ts const attributeGroup = new appreg.AttributeGroup(this, 'MyFirstAttributeGroup', { - attributeGroupName: 'MyFirstAttributeGroupName', + attributeGroupName: 'MyFirstAttributeGroupName', description: 'description for my attribute group', // the description is optional, attributes: { project: 'foo', @@ -104,7 +176,7 @@ Resources are CloudFormation stacks that you can associate with an application t stacks together to enable metadata rich insights into your applications and resources. A Cloudformation stack can only be associated with one appregistry application. If a stack is associated with multiple applications in your app or is already associated with one, -CDK will fail at deploy time. +CDK will fail at deploy time. ### Associating application with an attribute group diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application-associator.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application-associator.ts new file mode 100644 index 0000000000000..226e9d6c452ec --- /dev/null +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application-associator.ts @@ -0,0 +1,100 @@ +import * as cdk from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import { IApplication, Application } from './application'; +import { CheckedStageStackAssociator } from './aspects/stack-associator'; + +/** + * Properties for a Service Catalog AppRegistry AutoApplication + */ +export interface ApplicationAssociatorProps { + /** + * Enforces a particular physical application name. + * + * @default - No name. + */ + readonly applicationName?: string; + + /** + * Enforces a particular application arn. + * + * @default - No application arn. + */ + readonly applicationArnValue?: string; + + /** + * Application description. + * + * @default - No description. + */ + readonly description?: string; + + /** + * Stack properties. + * + */ + readonly stackProps: cdk.StackProps; +} + +/** + * An AppRegistry construct to automatically create an application with the given name and description. + * + * The application name must be unique at the account level and it's immutable. + * This construct will automatically associate all stacks in the given scope, however + * in case of a `Pipeline` stack, stage underneath the pipeline will not automatically be associated and + * needs to be associated separately. + * + * If cross account stack is detected, then this construct will automatically share the application to consumer accounts. + * Cross account feature will only work for non environment agnostic stacks. + */ +export class ApplicationAssociator extends Construct { + /** + * Created or imported application. + */ + private readonly application: IApplication; + private readonly associatedStages: Set = new Set(); + + constructor(scope: cdk.App, id: string, props: ApplicationAssociatorProps) { + super(scope, id); + + const applicationStack = new cdk.Stack(scope, 'ApplicationAssociatorStack', props.stackProps); + + if (!!props.applicationArnValue) { + this.application = Application.fromApplicationArn(applicationStack, 'ImportedApplication', props.applicationArnValue); + } else if (!!props.applicationName) { + this.application = new Application(applicationStack, 'DefaultCdkApplication', { + applicationName: props.applicationName, + description: props.description, + }); + } else { + throw new Error('Please provide either ARN or application name.'); + } + + cdk.Aspects.of(scope).add(new CheckedStageStackAssociator(this)); + } + + /** + * Associate this application with the given stage. + * + */ + public associateStage(stage: cdk.Stage): cdk.Stage { + this.associatedStages.add(stage); + cdk.Aspects.of(stage).add(new CheckedStageStackAssociator(this)); + return stage; + } + + /** + * Validates if a stage is already associated to the application. + * + */ + public isStageAssociated(stage: cdk.Stage): boolean { + return this.associatedStages.has(stage); + } + + /** + * Get the AppRegistry application. + * + */ + get appRegistryApplication() { + return this.application; + } +} diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts index 256de4099afd0..eb5112e45287e 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts @@ -2,8 +2,10 @@ import { CfnResourceShare } from '@aws-cdk/aws-ram'; import * as cdk from '@aws-cdk/core'; import { Names } from '@aws-cdk/core'; import { Construct } from 'constructs'; +import { StageStackAssociator } from './aspects/stack-associator'; import { IAttributeGroup } from './attribute-group'; import { getPrincipalsforSharing, hashValues, ShareOptions, SharePermission } from './common'; +import { isAccountUnresolved } from './private/utils'; import { InputValidator } from './private/validation'; import { CfnApplication, CfnAttributeGroupAssociation, CfnResourceAssociation } from './servicecatalogappregistry.generated'; @@ -27,7 +29,13 @@ export interface IApplication extends cdk.IResource { readonly applicationId: string; /** - * Associate thisapplication with an attribute group. + * The name of the application. + * @attribute + */ + readonly applicationName?: string; + + /** + * Associate this application with an attribute group. * * @param attributeGroup AppRegistry attribute group */ @@ -36,16 +44,34 @@ export interface IApplication extends cdk.IResource { /** * Associate this application with a CloudFormation stack. * + * @deprecated Use `associateApplicationWithStack` instead. * @param stack a CFN stack */ associateStack(stack: cdk.Stack): void; + /** + * Associate a Cloudformation statck with the application in the given stack. + * + * @param stack a CFN stack + */ + associateApplicationWithStack(stack: cdk.Stack): void; + /** * Share this application with other IAM entities, accounts, or OUs. * * @param shareOptions The options for the share. */ shareApplication(shareOptions: ShareOptions): void; + + /** + * Associate this application with all stacks under the construct node. + * NOTE: This method won't automatically register stacks under pipeline stages, + * and requires association of each pipeline stage by calling this method with stage Construct. + * + * @param construct cdk Construct + */ + associateAllStacksInScope(construct: Construct): void; + } /** @@ -67,6 +93,7 @@ export interface ApplicationProps { abstract class ApplicationBase extends cdk.Resource implements IApplication { public abstract readonly applicationArn: string; public abstract readonly applicationId: string; + public abstract readonly applicationName?: string; private readonly associatedAttributeGroups: Set = new Set(); private readonly associatedResources: Set = new Set(); @@ -89,6 +116,8 @@ abstract class ApplicationBase extends cdk.Resource implements IApplication { * Associate a stack with the application * If the resource is already associated, it will ignore duplicate request. * A stack can only be associated with one application. + * + * @deprecated Use `associateApplicationWithStack` instead. */ public associateStack(stack: cdk.Stack): void { if (!this.associatedResources.has(stack.node.addr)) { @@ -102,6 +131,27 @@ abstract class ApplicationBase extends cdk.Resource implements IApplication { } } + /** + * Associate stack with the application in the stack passed as parameter. + * + * If the stack is already associated, it will ignore duplicate request. + * A stack can only be associated with one application. + */ + public associateApplicationWithStack(stack: cdk.Stack): void { + if (!this.associatedResources.has(stack.node.addr)) { + new CfnResourceAssociation(stack, 'AppRegistryAssociation', { + application: stack === cdk.Stack.of(this) ? this.applicationId : this.applicationName ?? this.applicationId, + resource: stack.stackId, + resourceType: 'CFN_STACK', + }); + + this.associatedResources.add(stack.node.addr); + if (stack !== cdk.Stack.of(this) && this.isSameAccount(stack) && !this.isStageScope(stack)) { + stack.addDependency(cdk.Stack.of(this)); + } + } + } + /** * Share an application with accounts, organizations and OUs, and IAM roles and users. * The application will become available to end users within those principals. @@ -120,6 +170,17 @@ abstract class ApplicationBase extends cdk.Resource implements IApplication { }); } + /** + * Associate all stacks present in construct's aspect with application. + * + * NOTE: This method won't automatically register stacks under pipeline stages, + * and requires association of each pipeline stage by calling this method with stage Construct. + * + */ + public associateAllStacksInScope(scope: Construct): void { + cdk.Aspects.of(scope).add(new StageStackAssociator(this)); + } + /** * Create a unique id */ @@ -139,6 +200,21 @@ abstract class ApplicationBase extends cdk.Resource implements IApplication { return shareOptions.sharePermission ?? APPLICATION_READ_ONLY_RAM_PERMISSION_ARN; } } + + /** + * Checks whether a stack is defined in a Stage or not. + */ + private isStageScope(stack : cdk.Stack): boolean { + return !(stack.node.scope instanceof cdk.App) && (stack.node.scope instanceof cdk.Stage); + } + + /** + * Verifies if application and the visited node is deployed in different account. + */ + private isSameAccount(stack: cdk.Stack): boolean { + return isAccountUnresolved(this.env.account, stack.account) || this.env.account === stack.account; + } + } /** @@ -163,6 +239,7 @@ export class Application extends ApplicationBase { class Import extends ApplicationBase { public readonly applicationArn = applicationArn; public readonly applicationId = applicationId!; + public readonly applicationName = undefined; protected generateUniqueHash(resourceAddress: string): string { return hashValues(this.applicationArn, resourceAddress); @@ -176,6 +253,7 @@ export class Application extends ApplicationBase { public readonly applicationArn: string; public readonly applicationId: string; + public readonly applicationName?: string; private readonly nodeAddress: string; constructor(scope: Construct, id: string, props: ApplicationProps) { @@ -190,6 +268,7 @@ export class Application extends ApplicationBase { this.applicationArn = application.attrArn; this.applicationId = application.attrId; + this.applicationName = props.applicationName; this.nodeAddress = cdk.Names.nodeUniqueId(application.node); } diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/aspects/stack-associator.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/aspects/stack-associator.ts new file mode 100644 index 0000000000000..593bf8a8265d6 --- /dev/null +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/aspects/stack-associator.ts @@ -0,0 +1,131 @@ +import { IAspect, Stack, Stage, Annotations } from '@aws-cdk/core'; +import { IConstruct } from 'constructs'; +import { IApplication } from '../application'; +import { ApplicationAssociator } from '../application-associator'; +import { SharePermission } from '../common'; +import { isRegionUnresolved, isAccountUnresolved } from '../private/utils'; + +/** + * Aspect class, this will visit each node from the provided construct once. + * + * For every stack node visited, this class will be responsible to associate + * the stack to the application. + */ +abstract class StackAssociatorBase implements IAspect { + protected abstract readonly application: IApplication; + protected abstract readonly applicationAssociator?: ApplicationAssociator; + + protected readonly sharedAccounts: Set = new Set(); + + public visit(node: IConstruct): void { + // verify if a stage in a particular stack is associated to Application. + node.node.children.forEach((childNode) => { + if (Stage.isStage(childNode)) { + var stageAssociated = this.applicationAssociator?.isStageAssociated(childNode); + if (stageAssociated === false) { + this.error(childNode, 'Associate Stage: ' + childNode.stageName + ' to ensure all stacks in your cdk app are associated with AppRegistry. ' + + 'You can use ApplicationAssociator.associateStage to associate any stage.'); + } + } + }); + + if (Stack.isStack(node)) { + this.handleCrossRegionStack(node); + this.handleCrossAccountStack(node); + this.associate(node); + } + } + + /** + * Associate a stage stack to the given application. + * + * @param node A Stage stack. + */ + private associate(node: Stack): void { + this.application.associateApplicationWithStack(node); + } + + /** + * Adds an error annotation to a node. + * + * @param node The scope to add the error to. + * @param message The error message. + */ + private error(node: IConstruct, message: string): void { + Annotations.of(node).addError(message); + } + + /** + * Adds a warning annotation to a node. + * + * @param node The scope to add the warning to. + * @param message The error message. + */ + private warning(node: IConstruct, message: string): void { + Annotations.of(node).addWarning(message); + } + + /** + * Handle cross-region association. AppRegistry do not support + * cross region association at this moment, + * If any stack is evaluated as cross-region than that of application, + * we will throw an error. + * + * @param node Cfn stack. + */ + private handleCrossRegionStack(node: Stack): void { + if (isRegionUnresolved(this.application.env.region, node.region)) { + this.warning(node, 'Environment agnostic stack determined, AppRegistry association might not work as expected in case you deploy cross-region or cross-account stack.'); + return; + } + + if (node.region != this.application.env.region) { + this.error(node, 'AppRegistry does not support cross region associations. Application region ' + + this.application.env.region + ', stack region ' + node.region); + } + } + + /** + * Handle cross-account association. + * If any stack is evaluated as cross-account than that of application, + * then we will share the application to the stack owning account. + * + * @param node Cfn stack. + */ + private handleCrossAccountStack(node: Stack): void { + if (isAccountUnresolved(this.application.env.account!, node.account)) { + this.warning(node, 'Environment agnostic stack determined, AppRegistry association might not work as expected in case you deploy cross-region or cross-account stack.'); + return; + } + + if (node.account != this.application.env.account && !this.sharedAccounts.has(node.account)) { + this.application.shareApplication({ + accounts: [node.account], + sharePermission: SharePermission.ALLOW_ACCESS, + }); + + this.sharedAccounts.add(node.account); + } + } +} + +export class CheckedStageStackAssociator extends StackAssociatorBase { + protected readonly application: IApplication; + protected readonly applicationAssociator?: ApplicationAssociator; + + constructor(app: ApplicationAssociator) { + super(); + this.application = app.appRegistryApplication; + this.applicationAssociator = app; + } +} + +export class StageStackAssociator extends StackAssociatorBase { + protected readonly application: IApplication; + protected readonly applicationAssociator?: ApplicationAssociator; + + constructor(app: IApplication) { + super(); + this.application = app; + } +} diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/attribute-group.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/attribute-group.ts index ea5a893422aa5..d6dda21fe797d 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/attribute-group.ts +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/attribute-group.ts @@ -1,10 +1,10 @@ import { CfnResourceShare } from '@aws-cdk/aws-ram'; import * as cdk from '@aws-cdk/core'; -import { getPrincipalsforSharing, hashValues, ShareOptions, SharePermission } from './common'; +import { Names } from '@aws-cdk/core'; import { Construct } from 'constructs'; +import { getPrincipalsforSharing, hashValues, ShareOptions, SharePermission } from './common'; import { InputValidator } from './private/validation'; import { CfnAttributeGroup } from './servicecatalogappregistry.generated'; -import { Names } from '@aws-cdk/core'; const ATTRIBUTE_GROUP_READ_ONLY_RAM_PERMISSION_ARN = 'arn:aws:ram::aws:permission/AWSRAMPermissionServiceCatalogAppRegistryAttributeGroupReadOnly'; const ATTRIBUTE_GROUP_ALLOW_ACCESS_RAM_PERMISSION_ARN = 'arn:aws:ram::aws:permission/AWSRAMPermissionServiceCatalogAppRegistryAttributeGroupAllowAssociation'; diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/index.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/index.ts index adbf2a9febfe6..72239c4a1d110 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/index.ts +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/index.ts @@ -1,5 +1,6 @@ export * from './application'; export * from './attribute-group'; +export * from './application-associator'; export * from './common'; // AWS::ServiceCatalogAppRegistry CloudFormation Resources: diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/private/utils.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/private/utils.ts new file mode 100644 index 0000000000000..cdbf97a0f7337 --- /dev/null +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/private/utils.ts @@ -0,0 +1,22 @@ +import { Token } from '@aws-cdk/core'; + + +/** + * Verifies if application or the visited node is region agnostic. + * + * @param applicationRegion Region of the application. + * @param nodeRegion Region of the visited node. + */ +export function isRegionUnresolved(applicationRegion: string, nodeRegion: string): boolean { + return Token.isUnresolved(applicationRegion) || Token.isUnresolved(nodeRegion); +} + +/** + * Verifies if application or the visited node is account agnostic. + * + * @param applicationAccount Account of the application. + * @param nodeAccount Account of the visited node. + */ +export function isAccountUnresolved(applicationAccount: string, nodeAccount: string): boolean { + return Token.isUnresolved(applicationAccount) || Token.isUnresolved(nodeAccount); +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/package.json b/packages/@aws-cdk/aws-servicecatalogappregistry/package.json index f4d1243ef7542..aa5401b6ac812 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/package.json +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/package.json @@ -88,19 +88,24 @@ "@aws-cdk/integ-runner": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", + "@aws-cdk/aws-codecommit": "0.0.0", "@types/jest": "^27.5.2" }, "dependencies": { "@aws-cdk/core": "0.0.0", "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-ram": "0.0.0", + "@aws-cdk/aws-codepipeline": "0.0.0", + "@aws-cdk/pipelines": "0.0.0", "constructs": "^10.0.0" }, "peerDependencies": { "@aws-cdk/core": "0.0.0", "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-ram": "0.0.0", - "constructs": "^10.0.0" + "@aws-cdk/aws-codepipeline": "0.0.0", + "constructs": "^10.0.0", + "@aws-cdk/pipelines": "0.0.0" }, "engines": { "node": ">= 14.15.0" @@ -113,5 +118,10 @@ "publishConfig": { "tag": "latest" }, + "awslint": { + "exclude": [ + "construct-ctor:@aws-cdk/aws-servicecatalogappregistry.ApplicationAssociator..params[0]" + ] + }, "private": true } diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/ApplicationAssociatorStack.assets.json b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/ApplicationAssociatorStack.assets.json new file mode 100644 index 0000000000000..2f99ef79e2174 --- /dev/null +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/ApplicationAssociatorStack.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "4285054f947789e255e76c75b889b3b216adabb0b3f990c8966c18459cdf7b35": { + "source": { + "path": "ApplicationAssociatorStack.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "4285054f947789e255e76c75b889b3b216adabb0b3f990c8966c18459cdf7b35.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/ApplicationAssociatorStack.template.json b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/ApplicationAssociatorStack.template.json new file mode 100644 index 0000000000000..79c9dec209b9e --- /dev/null +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/ApplicationAssociatorStack.template.json @@ -0,0 +1,60 @@ +{ + "Resources": { + "DefaultCdkApplication4573D5A3": { + "Type": "AWS::ServiceCatalogAppRegistry::Application", + "Properties": { + "Name": "AppRegistryAssociatedApplication", + "Description": "Testing AppRegistry ApplicationAssociator" + } + }, + "AppRegistryAssociation": { + "Type": "AWS::ServiceCatalogAppRegistry::ResourceAssociation", + "Properties": { + "Application": { + "Fn::GetAtt": [ + "DefaultCdkApplication4573D5A3", + "Id" + ] + }, + "Resource": { + "Ref": "AWS::StackId" + }, + "ResourceType": "CFN_STACK" + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/cdk.out new file mode 100644 index 0000000000000..8ecc185e9dbee --- /dev/null +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/integ-servicecatalogappregistry-application.assets.json b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/integ-servicecatalogappregistry-application.assets.json new file mode 100644 index 0000000000000..5dec59ac54517 --- /dev/null +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/integ-servicecatalogappregistry-application.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "19dd33f3c17e59cafd22b9459b0a8d9bedbd42252737fedb06b2bcdbcf7809cc": { + "source": { + "path": "integ-servicecatalogappregistry-application.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "19dd33f3c17e59cafd22b9459b0a8d9bedbd42252737fedb06b2bcdbcf7809cc.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/integ-servicecatalogappregistry-application.template.json b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/integ-servicecatalogappregistry-application.template.json new file mode 100644 index 0000000000000..ecc817b74774a --- /dev/null +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/integ-servicecatalogappregistry-application.template.json @@ -0,0 +1,48 @@ +{ + "Resources": { + "AppRegistryAssociation": { + "Type": "AWS::ServiceCatalogAppRegistry::ResourceAssociation", + "Properties": { + "Application": "AppRegistryAssociatedApplication", + "Resource": { + "Ref": "AWS::StackId" + }, + "ResourceType": "CFN_STACK" + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/integ.json b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/integ.json new file mode 100644 index 0000000000000..1f1a144803dcd --- /dev/null +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/integ.json @@ -0,0 +1,14 @@ +{ + "version": "21.0.0", + "testCases": { + "integ.application-associator.all-stacks-association": { + "stacks": [ + "integ-servicecatalogappregistry-application" + ], + "diffAssets": false, + "stackUpdateWorkflow": true + } + }, + "synthContext": {}, + "enableLookups": false +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/integservicecatalogappregistryapplicationresourcesStack4399A149.assets.json b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/integservicecatalogappregistryapplicationresourcesStack4399A149.assets.json new file mode 100644 index 0000000000000..65c6d2c1ecf64 --- /dev/null +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/integservicecatalogappregistryapplicationresourcesStack4399A149.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "19dd33f3c17e59cafd22b9459b0a8d9bedbd42252737fedb06b2bcdbcf7809cc": { + "source": { + "path": "integservicecatalogappregistryapplicationresourcesStack4399A149.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "19dd33f3c17e59cafd22b9459b0a8d9bedbd42252737fedb06b2bcdbcf7809cc.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/integservicecatalogappregistryapplicationresourcesStack4399A149.template.json b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/integservicecatalogappregistryapplicationresourcesStack4399A149.template.json new file mode 100644 index 0000000000000..ecc817b74774a --- /dev/null +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/integservicecatalogappregistryapplicationresourcesStack4399A149.template.json @@ -0,0 +1,48 @@ +{ + "Resources": { + "AppRegistryAssociation": { + "Type": "AWS::ServiceCatalogAppRegistry::ResourceAssociation", + "Properties": { + "Application": "AppRegistryAssociatedApplication", + "Resource": { + "Ref": "AWS::StackId" + }, + "ResourceType": "CFN_STACK" + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/manifest.json new file mode 100644 index 0000000000000..65db06b047d68 --- /dev/null +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/manifest.json @@ -0,0 +1,209 @@ +{ + "version": "21.0.0", + "artifacts": { + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + }, + "integservicecatalogappregistryapplicationresourcesStack4399A149.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "integservicecatalogappregistryapplicationresourcesStack4399A149.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "integservicecatalogappregistryapplicationresourcesStack4399A149": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "integservicecatalogappregistryapplicationresourcesStack4399A149.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/19dd33f3c17e59cafd22b9459b0a8d9bedbd42252737fedb06b2bcdbcf7809cc.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "integservicecatalogappregistryapplicationresourcesStack4399A149.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "ApplicationAssociatorStack", + "integservicecatalogappregistryapplicationresourcesStack4399A149.assets" + ], + "metadata": { + "/integ-servicecatalogappregistry-application/resourcesStack": [ + { + "type": "aws:cdk:warning", + "data": "Environment agnostic stack determined, AppRegistry association might not work as expected in case you deploy cross-region or cross-account stack." + }, + { + "type": "aws:cdk:warning", + "data": "Environment agnostic stack determined, AppRegistry association might not work as expected in case you deploy cross-region or cross-account stack." + } + ], + "/integ-servicecatalogappregistry-application/resourcesStack/AppRegistryAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "AppRegistryAssociation" + } + ], + "/integ-servicecatalogappregistry-application/resourcesStack/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/integ-servicecatalogappregistry-application/resourcesStack/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "integ-servicecatalogappregistry-application/resourcesStack" + }, + "integ-servicecatalogappregistry-application.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "integ-servicecatalogappregistry-application.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "integ-servicecatalogappregistry-application": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "integ-servicecatalogappregistry-application.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/19dd33f3c17e59cafd22b9459b0a8d9bedbd42252737fedb06b2bcdbcf7809cc.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "integ-servicecatalogappregistry-application.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "ApplicationAssociatorStack", + "integ-servicecatalogappregistry-application.assets" + ], + "metadata": { + "/integ-servicecatalogappregistry-application": [ + { + "type": "aws:cdk:warning", + "data": "Environment agnostic stack determined, AppRegistry association might not work as expected in case you deploy cross-region or cross-account stack." + }, + { + "type": "aws:cdk:warning", + "data": "Environment agnostic stack determined, AppRegistry association might not work as expected in case you deploy cross-region or cross-account stack." + } + ], + "/integ-servicecatalogappregistry-application/AppRegistryAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "AppRegistryAssociation" + } + ], + "/integ-servicecatalogappregistry-application/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/integ-servicecatalogappregistry-application/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "integ-servicecatalogappregistry-application" + }, + "ApplicationAssociatorStack.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "ApplicationAssociatorStack.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "ApplicationAssociatorStack": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "ApplicationAssociatorStack.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/4285054f947789e255e76c75b889b3b216adabb0b3f990c8966c18459cdf7b35.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "ApplicationAssociatorStack.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + }, + "stackName": "AppRegistryApplicationAssociatorStack" + }, + "dependencies": [ + "ApplicationAssociatorStack.assets" + ], + "metadata": { + "/ApplicationAssociatorStack": [ + { + "type": "aws:cdk:warning", + "data": "Environment agnostic stack determined, AppRegistry association might not work as expected in case you deploy cross-region or cross-account stack." + }, + { + "type": "aws:cdk:warning", + "data": "Environment agnostic stack determined, AppRegistry association might not work as expected in case you deploy cross-region or cross-account stack." + } + ], + "/ApplicationAssociatorStack/DefaultCdkApplication/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "DefaultCdkApplication4573D5A3" + } + ], + "/ApplicationAssociatorStack/AppRegistryAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "AppRegistryAssociation" + } + ], + "/ApplicationAssociatorStack/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/ApplicationAssociatorStack/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "ApplicationAssociatorStack" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/tree.json b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/tree.json new file mode 100644 index 0000000000000..078cc92093b86 --- /dev/null +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.all-stacks-association.integ.snapshot/tree.json @@ -0,0 +1,143 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.95" + } + }, + "integ-servicecatalogappregistry-application": { + "id": "integ-servicecatalogappregistry-application", + "path": "integ-servicecatalogappregistry-application", + "children": { + "resourcesStack": { + "id": "resourcesStack", + "path": "integ-servicecatalogappregistry-application/resourcesStack", + "children": { + "AppRegistryAssociation": { + "id": "AppRegistryAssociation", + "path": "integ-servicecatalogappregistry-application/resourcesStack/AppRegistryAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ServiceCatalogAppRegistry::ResourceAssociation", + "aws:cdk:cloudformation:props": { + "application": "AppRegistryAssociatedApplication", + "resource": { + "Ref": "AWS::StackId" + }, + "resourceType": "CFN_STACK" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-servicecatalogappregistry.CfnResourceAssociation", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "AppRegistryAssociation": { + "id": "AppRegistryAssociation", + "path": "integ-servicecatalogappregistry-application/AppRegistryAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ServiceCatalogAppRegistry::ResourceAssociation", + "aws:cdk:cloudformation:props": { + "application": "AppRegistryAssociatedApplication", + "resource": { + "Ref": "AWS::StackId" + }, + "resourceType": "CFN_STACK" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-servicecatalogappregistry.CfnResourceAssociation", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "RegisterCdkApplication": { + "id": "RegisterCdkApplication", + "path": "RegisterCdkApplication", + "constructInfo": { + "fqn": "@aws-cdk/aws-servicecatalogappregistry.ApplicationAssociator", + "version": "0.0.0" + } + }, + "ApplicationAssociatorStack": { + "id": "ApplicationAssociatorStack", + "path": "ApplicationAssociatorStack", + "children": { + "DefaultCdkApplication": { + "id": "DefaultCdkApplication", + "path": "ApplicationAssociatorStack/DefaultCdkApplication", + "children": { + "Resource": { + "id": "Resource", + "path": "ApplicationAssociatorStack/DefaultCdkApplication/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ServiceCatalogAppRegistry::Application", + "aws:cdk:cloudformation:props": { + "name": "AppRegistryAssociatedApplication", + "description": "Testing AppRegistry ApplicationAssociator" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-servicecatalogappregistry.CfnApplication", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-servicecatalogappregistry.Application", + "version": "0.0.0" + } + }, + "AppRegistryAssociation": { + "id": "AppRegistryAssociation", + "path": "ApplicationAssociatorStack/AppRegistryAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ServiceCatalogAppRegistry::ResourceAssociation", + "aws:cdk:cloudformation:props": { + "application": { + "Fn::GetAtt": [ + "DefaultCdkApplication4573D5A3", + "Id" + ] + }, + "resource": { + "Ref": "AWS::StackId" + }, + "resourceType": "CFN_STACK" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-servicecatalogappregistry.CfnResourceAssociation", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.test.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.test.ts new file mode 100644 index 0000000000000..de64c0c35b68b --- /dev/null +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application-associator.test.ts @@ -0,0 +1,196 @@ +import { Annotations, Template } from '@aws-cdk/assertions'; +import * as codecommit from '@aws-cdk/aws-codecommit'; +import * as cdk from '@aws-cdk/core'; +import * as codepipeline from '@aws-cdk/pipelines'; +import { Construct } from 'constructs'; +import * as appreg from '../lib'; + +describe('Scope based Associations with Application within Same Account', () => { + let app: cdk.App; + beforeEach(() => { + app = new cdk.App({ + context: { + '@aws-cdk/core:newStyleStackSynthesis': false, + }, + }); + }); + test('ApplicationAssociator will associate allStacks created inside cdkApp', () => { + new appreg.ApplicationAssociator(app, 'MyApplication', { + applicationName: 'MyAssociatedApplication', + stackProps: { + stackName: 'MyAssociatedApplicationStack', + }, + }); + const anotherStack = new AppRegistrySampleStack(app, 'SampleStack'); + Template.fromStack(anotherStack).resourceCountIs('AWS::ServiceCatalogAppRegistry::ResourceAssociation', 1); + Template.fromStack(anotherStack).hasResourceProperties('AWS::ServiceCatalogAppRegistry::ResourceAssociation', { + Application: 'MyAssociatedApplication', + Resource: { Ref: 'AWS::StackId' }, + }); + }); +}); +describe('Scope based Associations with Application with Cross Region/Account', () => { + let app: cdk.App; + beforeEach(() => { + app = new cdk.App({ + context: { + '@aws-cdk/core:newStyleStackSynthesis': false, + }, + }); + }); + test('ApplicationAssociator in cross-account associates all stacks created inside cdk app', () => { + new appreg.ApplicationAssociator(app, 'MyApplication', { + applicationName: 'MyAssociatedApplication', + stackProps: { + stackName: 'MyAssociatedApplicationStack', + }, + }); + const firstStack = new cdk.Stack(app, 'testStack', { + env: { account: 'account2', region: 'region' }, + }); + const nestedStack = new cdk.Stack(firstStack, 'MyFirstStack', { + env: { account: 'account2', region: 'region' }, + }); + Template.fromStack(firstStack).resourceCountIs('AWS::ServiceCatalogAppRegistry::ResourceAssociation', 1); + Template.fromStack(nestedStack).resourceCountIs('AWS::ServiceCatalogAppRegistry::ResourceAssociation', 1); + }); + + test('ApplicationAssociator creation failed when neither Application name nor ARN is provided', () => { + expect(() => { + new appreg.ApplicationAssociator(app, 'MyApplication', { + stackProps: { + stackName: 'MyAssociatedApplicationStack', + }, + }); + }).toThrow(/Please provide either ARN or application name./); + }); + + test('associate resource on imported application', () => { + const resource = new cdk.Stack(app, 'MyStack'); + + new appreg.ApplicationAssociator(app, 'MyApplication', { + applicationArnValue: 'arn:aws:servicecatalog:us-east-1:482211128593:/applications/0a17wtxeg5vilok0sbxfozwpq9', + stackProps: { + stackName: 'MyAssociatedApplicationStack', + }, + }); + + Template.fromStack(resource).hasResourceProperties('AWS::ServiceCatalogAppRegistry::ResourceAssociation', { + Application: '0a17wtxeg5vilok0sbxfozwpq9', + Resource: { Ref: 'AWS::StackId' }, + }); + }), + + test('ApplicationAssociator with cross region stacks inside cdkApp throws error', () => { + new appreg.ApplicationAssociator(app, 'MyApplication', { + applicationName: 'MyAssociatedApplication', + stackProps: { + stackName: 'MyAssociatedApplicationStack', + env: { account: 'account2', region: 'region2' }, + }, + }); + + const crossRegionStack = new cdk.Stack(app, 'crossRegionStack', { + env: { account: 'account', region: 'region' }, + }); + Annotations.fromStack(crossRegionStack).hasError('*', 'AppRegistry does not support cross region associations. Application region region2, stack region region'); + }); + + test('Environment Agnostic ApplicationAssociator with cross region stacks inside cdkApp gives warning', () => { + new appreg.ApplicationAssociator(app, 'MyApplication', { + applicationName: 'MyAssociatedApplication', + stackProps: { + stackName: 'MyAssociatedApplicationStack', + }, + }); + + const crossRegionStack = new cdk.Stack(app, 'crossRegionStack', { + env: { account: 'account', region: 'region' }, + }); + Annotations.fromStack(crossRegionStack).hasWarning('*', 'Environment agnostic stack determined, AppRegistry association might not work as expected in case you deploy cross-region or cross-account stack.'); + }); + + test('Cdk App Containing Pipeline with stage but stage not associated throws error', () => { + const application = new appreg.ApplicationAssociator(app, 'MyApplication', { + applicationName: 'MyAssociatedApplication', + stackProps: { + stackName: 'MyAssociatedApplicationStack', + }, + }); + const pipelineStack = new AppRegistrySampleCodePipelineStack(app, 'PipelineStackA', { + application: application, + associateStage: false, + }); + app.synth(); + Annotations.fromStack(pipelineStack).hasError('*', + 'Associate Stage: SampleStage to ensure all stacks in your cdk app are associated with AppRegistry. You can use ApplicationAssociator.associateStage to associate any stage.'); + }); + + test('Cdk App Containing Pipeline with stage and stage associated successfully gets synthesized', () => { + const application = new appreg.ApplicationAssociator(app, 'MyApplication', { + applicationName: 'MyAssociatedApplication', + stackProps: { + stackName: 'MyAssociatedApplicationStack', + }, + }); + const pipelineStack = new AppRegistrySampleCodePipelineStack(app, 'PipelineStackA', { + application: application, + associateStage: true, + }); + app.synth(); + Template.fromStack(pipelineStack).resourceCountIs('AWS::ServiceCatalogAppRegistry::ResourceAssociation', 1); + }); +}); + +interface AppRegistrySampleCodePipelineStackProps extends cdk.StackProps { + application: appreg.ApplicationAssociator; + associateStage: boolean; +} + +class AppRegistrySampleCodePipelineStack extends cdk.Stack { + public constructor(scope: Construct, id: string, props: AppRegistrySampleCodePipelineStackProps ) { + super(scope, id, props); + const repo = new codecommit.Repository(this, 'Repo', { + repositoryName: 'MyRepo', + }); + + const pipeline = new codepipeline.CodePipeline(this, 'Pipeline', { + pipelineName: 'MyPipeline', + synth: new codepipeline.CodeBuildStep('SynthStep', { + input: codepipeline.CodePipelineSource.codeCommit(repo, 'main'), + installCommands: [ + 'npm install -g aws-cdk', + ], + commands: [ + 'npm ci', + 'npm run build', + 'npx cdk synth', + ], + }, + ), + }); + + const stage = new AppRegistrySampleStage( + this, + 'SampleStage', + ); + + if (props.associateStage) { + props.application.associateStage(stage); + } + pipeline.addStage(stage); + } +} + +class AppRegistrySampleStage extends cdk.Stage { + public constructor(scope: Construct, id: string, props?: cdk.StageProps) { + super(scope, id, props); + new AppRegistrySampleStack(this, 'SampleStack', {}); + } +} + +class AppRegistrySampleStack extends cdk.Stack { + public constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + } +} diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/application.test.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application.test.ts index 33f1ca2628cdb..dce0d1147ec44 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/test/application.test.ts +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application.test.ts @@ -1,11 +1,11 @@ -import { Template } from '@aws-cdk/assertions'; +import { Annotations, Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; +import { Construct } from 'constructs'; import * as appreg from '../lib'; describe('Application', () => { let stack: cdk.Stack; - beforeEach(() => { const app = new cdk.App({ context: { @@ -207,6 +207,20 @@ describe('Application', () => { }); }), + test('associate resource on imported application', () => { + const resource = new cdk.Stack(stack, 'MyStack'); + + const importedApplication = appreg.Application.fromApplicationArn(stack, 'ImportedApplication', + 'arn:aws:servicecatalog:us-east-1:123456789012:/applications/0bqmvxvgmry0ecc4mjhwypun6i'); + + importedApplication.associateStack(resource); + + Template.fromStack(stack).hasResourceProperties('AWS::ServiceCatalogAppRegistry::ResourceAssociation', { + Application: '0bqmvxvgmry0ecc4mjhwypun6i', + Resource: { 'Fn::ImportValue': 'MyStack:ExportsOutputRefAWSStackIdB2DD5BAA' }, + }); + }), + test('duplicate resource assocations are idempotent', () => { const resource = new cdk.Stack(stack, 'MyStack'); @@ -324,3 +338,116 @@ describe('Application', () => { }); }); }); + +describe('Scope based Associations with Application within Same Account', () => { + let stack: cdk.Stack; + let app: cdk.App; + beforeEach(() => { + app = new cdk.App({ + context: { + '@aws-cdk/core:newStyleStackSynthesis': false, + }, + }); + stack = new cdk.Stack(app, 'cdkApplication'); + }); + + test('Associate Stage in same account will associate allStacks Inside it', () => { + const application = new appreg.Application(stack, 'MyApplication', { + applicationName: 'MyApplication', + }); + const stage = new cdk.Stage(stack, 'MyStage'); + const stageStack = new cdk.Stack(stage, 'MyStack'); + application.associateAllStacksInScope(stage); + expect(stageStack.stackName).toEqual('MyStage-MyStack'); + Template.fromStack(stageStack).hasResourceProperties('AWS::ServiceCatalogAppRegistry::ResourceAssociation', { + Application: 'MyApplication', + Resource: { Ref: 'AWS::StackId' }, + }); + }); + + + test('Associate Stack in same account will associate allStacks Inside it', () => { + const application = new appreg.Application(stack, 'MyApplication', { + applicationName: 'MyApplication', + }); + + const anotherStack = new AppRegistrySampleStack(app, 'SampleStack'); + application.associateAllStacksInScope(app); + Template.fromStack(stack).resourceCountIs('AWS::ServiceCatalogAppRegistry::ResourceAssociation', 1); + Template.fromStack(anotherStack).resourceCountIs('AWS::ServiceCatalogAppRegistry::ResourceAssociation', 1); + Template.fromStack(stack).hasResourceProperties('AWS::ServiceCatalogAppRegistry::ResourceAssociation', { + Application: { 'Fn::GetAtt': ['MyApplication5C63EC1D', 'Id'] }, + Resource: { Ref: 'AWS::StackId' }, + }); + Template.fromStack(anotherStack).hasResourceProperties('AWS::ServiceCatalogAppRegistry::ResourceAssociation', { + Application: 'MyApplication', + Resource: { Ref: 'AWS::StackId' }, + }); + }); +}); + +describe('Scope based Associations with Application with Cross Region/Account', () => { + let stack: cdk.Stack; + let app: cdk.App; + beforeEach(() => { + app = new cdk.App({ + context: { + '@aws-cdk/core:newStyleStackSynthesis': false, + }, + }); + stack = new cdk.Stack(app, 'CdkApplication', { + env: { account: 'account', region: 'region' }, + }); + }); + + test('associateAllStacksInScope in cross-account associates all stacks from the context passed', () => { + const application = new appreg.Application(stack, 'MyApplication', { + applicationName: 'MyApplication', + }); + const firstStack = new cdk.Stack(app, 'testStack', { + env: { account: 'account2', region: 'region' }, + }); + const nestedStack = new cdk.Stack(firstStack, 'MyFirstStack', { + env: { account: 'account2', region: 'region' }, + }); + application.associateAllStacksInScope(app); + Template.fromStack(stack).resourceCountIs('AWS::ServiceCatalogAppRegistry::ResourceAssociation', 1); + Template.fromStack(firstStack).resourceCountIs('AWS::ServiceCatalogAppRegistry::ResourceAssociation', 1); + Template.fromStack(nestedStack).resourceCountIs('AWS::ServiceCatalogAppRegistry::ResourceAssociation', 1); + }); + + test('Associate Stage in cross account association will associate allStacks Inside it', () => { + const application = new appreg.Application(stack, 'MyApplication', { + applicationName: 'MyApplication', + }); + const stage = new cdk.Stage(app, 'MyStage', { + env: { account: 'account2', region: 'region' }, + }); + const stageStack = new cdk.Stack(stage, 'MyStack'); + application.associateAllStacksInScope(stage); + Template.fromStack(stageStack).resourceCountIs('AWS::ServiceCatalogAppRegistry::ResourceAssociation', 1); + Template.fromStack(stageStack).hasResourceProperties('AWS::ServiceCatalogAppRegistry::ResourceAssociation', { + Application: 'MyApplication', + Resource: { Ref: 'AWS::StackId' }, + }); + }); + + test('Associate Stage in cross region throw error', () => { + const application = new appreg.Application(stack, 'MyApplication', { + applicationName: 'MyApplication', + }); + const stage = new cdk.Stage(stack, 'MyStage', { + env: { account: 'account1', region: 'region1' }, + }); + const stageStack = new cdk.Stack(stage, 'MyStack'); + application.associateAllStacksInScope(stage); + Annotations.fromStack(stageStack).hasError('*', + 'AppRegistry does not support cross region associations. Application region region, stack region region1'); + }); +}); + +class AppRegistrySampleStack extends cdk.Stack { + public constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + } +} diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application-associator.all-stacks-association.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application-associator.all-stacks-association.ts new file mode 100644 index 0000000000000..afd822dc6dc16 --- /dev/null +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application-associator.all-stacks-association.ts @@ -0,0 +1,20 @@ +/// !cdk-integ integ-servicecatalogappregistry-application +import * as cdk from '@aws-cdk/core'; +import * as appreg from '../lib'; + + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'integ-servicecatalogappregistry-application'); + + +new appreg.ApplicationAssociator(app, 'RegisterCdkApplication', { + applicationName: 'AppRegistryAssociatedApplication', + description: 'Testing AppRegistry ApplicationAssociator', + stackProps: { + stackName: 'AppRegistryApplicationAssociatorStack', + }, +}); + +new cdk.Stack(stack, 'resourcesStack'); + +app.synth();