From ae44fc486e7d3ef6e0e049ecf31884e6a4691887 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 2 Mar 2023 15:20:37 -0500 Subject: [PATCH 001/120] initial project structure --- .../@aws-cdk/core-synthesizer/.eslintrc.js | 4 + packages/@aws-cdk/core-synthesizer/.gitignore | 8 + packages/@aws-cdk/core-synthesizer/.jsii | 208 ++++++++++++++++++ packages/@aws-cdk/core-synthesizer/.npmignore | 14 ++ .../core-synthesizer/.warnings.jsii.js | 37 ++++ packages/@aws-cdk/core-synthesizer/LICENSE | 201 +++++++++++++++++ packages/@aws-cdk/core-synthesizer/NOTICE | 2 + packages/@aws-cdk/core-synthesizer/README.md | 1 + .../@aws-cdk/core-synthesizer/lib/index.d.ts | 1 + .../@aws-cdk/core-synthesizer/lib/index.js | 11 + .../@aws-cdk/core-synthesizer/lib/index.ts | 7 + .../@aws-cdk/core-synthesizer/package.json | 99 +++++++++ .../@aws-cdk/core-synthesizer/tsconfig.json | 51 +++++ 13 files changed, 644 insertions(+) create mode 100644 packages/@aws-cdk/core-synthesizer/.eslintrc.js create mode 100644 packages/@aws-cdk/core-synthesizer/.gitignore create mode 100644 packages/@aws-cdk/core-synthesizer/.jsii create mode 100644 packages/@aws-cdk/core-synthesizer/.npmignore create mode 100644 packages/@aws-cdk/core-synthesizer/.warnings.jsii.js create mode 100644 packages/@aws-cdk/core-synthesizer/LICENSE create mode 100644 packages/@aws-cdk/core-synthesizer/NOTICE create mode 100644 packages/@aws-cdk/core-synthesizer/README.md create mode 100644 packages/@aws-cdk/core-synthesizer/lib/index.d.ts create mode 100644 packages/@aws-cdk/core-synthesizer/lib/index.js create mode 100644 packages/@aws-cdk/core-synthesizer/lib/index.ts create mode 100644 packages/@aws-cdk/core-synthesizer/package.json create mode 100644 packages/@aws-cdk/core-synthesizer/tsconfig.json diff --git a/packages/@aws-cdk/core-synthesizer/.eslintrc.js b/packages/@aws-cdk/core-synthesizer/.eslintrc.js new file mode 100644 index 0000000000000..c6b0adb2216b1 --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/.eslintrc.js @@ -0,0 +1,4 @@ +const baseConfig = require('@aws-cdk/cdk-build-tools/config/eslintrc'); +baseConfig.ignorePatterns.push('resources/**/*'); +baseConfig.parserOptions.project = __dirname + '/tsconfig.json'; +module.exports = baseConfig; \ No newline at end of file diff --git a/packages/@aws-cdk/core-synthesizer/.gitignore b/packages/@aws-cdk/core-synthesizer/.gitignore new file mode 100644 index 0000000000000..dfd5365951031 --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/.gitignore @@ -0,0 +1,8 @@ + +.LAST_BUILD +*.snk +junit.xml +.nyc_output +coverage +nyc.config.js +!.eslintrc.js \ No newline at end of file diff --git a/packages/@aws-cdk/core-synthesizer/.jsii b/packages/@aws-cdk/core-synthesizer/.jsii new file mode 100644 index 0000000000000..0a04cb73a64d7 --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/.jsii @@ -0,0 +1,208 @@ +{ + "author": { + "name": "Amazon Web Services", + "organization": true, + "roles": [ + "author" + ], + "url": "https://aws.amazon.com" + }, + "dependencies": { + "@aws-cdk/core": "0.0.0", + "constructs": "^10.0.0" + }, + "dependencyClosure": { + "@aws-cdk/cloud-assembly-schema": { + "targets": { + "dotnet": { + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/main/logo/default-256-dark.png", + "namespace": "Amazon.CDK.CloudAssembly.Schema", + "packageId": "Amazon.CDK.CloudAssembly.Schema" + }, + "java": { + "maven": { + "artifactId": "cdk-cloud-assembly-schema", + "groupId": "software.amazon.awscdk" + }, + "package": "software.amazon.awscdk.cloudassembly.schema" + }, + "js": { + "npm": "@aws-cdk/cloud-assembly-schema" + }, + "python": { + "classifiers": [ + "Framework :: AWS CDK", + "Framework :: AWS CDK :: 2" + ], + "distName": "aws-cdk.cloud-assembly-schema", + "module": "aws_cdk.cloud_assembly_schema" + } + } + }, + "@aws-cdk/core": { + "targets": { + "dotnet": { + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/main/logo/default-256-dark.png", + "namespace": "Amazon.CDK", + "packageId": "Amazon.CDK" + }, + "java": { + "maven": { + "artifactId": "core", + "groupId": "software.amazon.awscdk" + }, + "package": "software.amazon.awscdk.core" + }, + "js": { + "npm": "@aws-cdk/core" + }, + "python": { + "classifiers": [ + "Framework :: AWS CDK", + "Framework :: AWS CDK :: 2" + ], + "distName": "aws-cdk.core", + "module": "aws_cdk.core" + } + } + }, + "@aws-cdk/cx-api": { + "targets": { + "dotnet": { + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/main/logo/default-256-dark.png", + "namespace": "Amazon.CDK.CXAPI", + "packageId": "Amazon.CDK.CXAPI" + }, + "java": { + "maven": { + "artifactId": "cdk-cx-api", + "groupId": "software.amazon.awscdk" + }, + "package": "software.amazon.awscdk.cxapi" + }, + "js": { + "npm": "@aws-cdk/cx-api" + }, + "python": { + "classifiers": [ + "Framework :: AWS CDK", + "Framework :: AWS CDK :: 2" + ], + "distName": "aws-cdk.cx-api", + "module": "aws_cdk.cx_api" + } + } + }, + "@aws-cdk/region-info": { + "targets": { + "dotnet": { + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/main/logo/default-256-dark.png", + "namespace": "Amazon.CDK.RegionInfo", + "packageId": "Amazon.CDK.RegionInfo" + }, + "java": { + "maven": { + "artifactId": "cdk-region-info", + "groupId": "software.amazon.awscdk" + }, + "package": "software.amazon.awscdk.regioninfo" + }, + "js": { + "npm": "@aws-cdk/region-info" + }, + "python": { + "classifiers": [ + "Framework :: AWS CDK", + "Framework :: AWS CDK :: 2" + ], + "distName": "aws-cdk.region-info", + "module": "aws_cdk.region_info" + } + } + }, + "constructs": { + "targets": { + "dotnet": { + "namespace": "Constructs", + "packageId": "Constructs" + }, + "go": { + "moduleName": "github.com/aws/constructs-go" + }, + "java": { + "maven": { + "artifactId": "constructs", + "groupId": "software.constructs" + }, + "package": "software.constructs" + }, + "js": { + "npm": "constructs" + }, + "python": { + "distName": "constructs", + "module": "constructs" + } + } + } + }, + "description": "Cdk synthesizer for Bootstrap v3", + "docs": { + "stability": "experimental" + }, + "homepage": "https://github.com/aws/aws-cdk", + "jsiiVersion": "1.74.0 (build 6d08790)", + "keywords": [ + "aws", + "cdk" + ], + "license": "Apache-2.0", + "metadata": { + "jsii": { + "compiledWithDeprecationWarnings": true, + "pacmak": { + "hasDefaultInterfaces": true + }, + "rosetta": { + "strict": true + } + } + }, + "name": "@aws-cdk/core-synthesizer", + "readme": { + "markdown": "# Synthesizer\n" + }, + "repository": { + "directory": "packages/@aws-cdk/core-synthesizer", + "type": "git", + "url": "https://github.com/aws/aws-cdk.git" + }, + "schema": "jsii/0.10.0", + "targets": { + "dotnet": { + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/main/logo/default-256-dark.png", + "namespace": "Amazon.CDK.Core.Synthesizer" + }, + "java": { + "maven": { + "artifactId": "cdk-core-synthesizer", + "groupId": "software.amazon.awscdk" + }, + "package": "software.amazon.awscdk.core.synthesizer" + }, + "js": { + "npm": "@aws-cdk/core-synthesizer" + }, + "python": { + "classifiers": [ + "Framework :: AWS CDK", + "Framework :: AWS CDK :: 2" + ], + "distName": "aws-cdk.core-synthesizer", + "module": "aws_cdk.core_synthesizer" + } + }, + "types": {}, + "version": "0.0.0", + "fingerprint": "W+VjVFm5LaJ+VNOiYH1Fpha37M8IeMCXUwCda6OxWmU=" +} \ No newline at end of file diff --git a/packages/@aws-cdk/core-synthesizer/.npmignore b/packages/@aws-cdk/core-synthesizer/.npmignore new file mode 100644 index 0000000000000..773d1bc0f120e --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/.npmignore @@ -0,0 +1,14 @@ + +.LAST_BUILD +*.snk +junit.xml +*.ts +!*.d.ts +!*.js +!*.lit.ts +coverage +.nyc_output +*.tgz +.eslintrc.js +# exclude cdk artifacts +**/cdk.out \ No newline at end of file diff --git a/packages/@aws-cdk/core-synthesizer/.warnings.jsii.js b/packages/@aws-cdk/core-synthesizer/.warnings.jsii.js new file mode 100644 index 0000000000000..f1bdca28019e4 --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/.warnings.jsii.js @@ -0,0 +1,37 @@ +function print(name, deprecationMessage) { + const deprecated = process.env.JSII_DEPRECATED; + const deprecationMode = ["warn", "fail", "quiet"].includes(deprecated) ? deprecated : "warn"; + const message = `${name} is deprecated.\n ${deprecationMessage.trim()}\n This API will be removed in the next major release.`; + switch (deprecationMode) { + case "fail": + throw new DeprecationError(message); + case "warn": + console.warn("[WARNING]", message); + break; + } +} +function getPropertyDescriptor(obj, prop) { + const descriptor = Object.getOwnPropertyDescriptor(obj, prop); + if (descriptor) { + return descriptor; + } + const proto = Object.getPrototypeOf(obj); + const prototypeDescriptor = proto && getPropertyDescriptor(proto, prop); + if (prototypeDescriptor) { + return prototypeDescriptor; + } + return {}; +} +const visitedObjects = new Set(); +class DeprecationError extends Error { + constructor(...args) { + super(...args); + Object.defineProperty(this, "name", { + configurable: false, + enumerable: true, + value: "DeprecationError", + writable: false, + }); + } +} +module.exports = { print, getPropertyDescriptor, DeprecationError }; diff --git a/packages/@aws-cdk/core-synthesizer/LICENSE b/packages/@aws-cdk/core-synthesizer/LICENSE new file mode 100644 index 0000000000000..9b722c65c5481 --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/@aws-cdk/core-synthesizer/NOTICE b/packages/@aws-cdk/core-synthesizer/NOTICE new file mode 100644 index 0000000000000..a27b7dd317649 --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/NOTICE @@ -0,0 +1,2 @@ +AWS Cloud Development Kit (AWS CDK) +Copyright 2018-2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/@aws-cdk/core-synthesizer/README.md b/packages/@aws-cdk/core-synthesizer/README.md new file mode 100644 index 0000000000000..abb7b38766db4 --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/README.md @@ -0,0 +1 @@ +# Synthesizer diff --git a/packages/@aws-cdk/core-synthesizer/lib/index.d.ts b/packages/@aws-cdk/core-synthesizer/lib/index.d.ts new file mode 100644 index 0000000000000..7df27ddf55ea0 --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/lib/index.d.ts @@ -0,0 +1 @@ +export declare function main(): void; diff --git a/packages/@aws-cdk/core-synthesizer/lib/index.js b/packages/@aws-cdk/core-synthesizer/lib/index.js new file mode 100644 index 0000000000000..0f8c34ce8a27b --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/lib/index.js @@ -0,0 +1,11 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.main = void 0; +const core = require("@aws-cdk/core"); +function main() { + const a = new core.App(); + // eslint-disable-next-line no-console + console.log(a.toString()); +} +exports.main = main; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxzQ0FBc0M7QUFFdEMsU0FBZ0IsSUFBSTtJQUNsQixNQUFNLENBQUMsR0FBRyxJQUFJLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUN6QixzQ0FBc0M7SUFDdEMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQztBQUM1QixDQUFDO0FBSkQsb0JBSUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBjb3JlIGZyb20gJ0Bhd3MtY2RrL2NvcmUnO1xuXG5leHBvcnQgZnVuY3Rpb24gbWFpbigpIHtcbiAgY29uc3QgYSA9IG5ldyBjb3JlLkFwcCgpO1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZVxuICBjb25zb2xlLmxvZyhhLnRvU3RyaW5nKCkpO1xufSJdfQ== \ No newline at end of file diff --git a/packages/@aws-cdk/core-synthesizer/lib/index.ts b/packages/@aws-cdk/core-synthesizer/lib/index.ts new file mode 100644 index 0000000000000..dbb6031683650 --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/lib/index.ts @@ -0,0 +1,7 @@ +import * as core from '@aws-cdk/core'; + +export function main() { + const a = new core.App(); + // eslint-disable-next-line no-console + console.log(a.toString()); +} \ No newline at end of file diff --git a/packages/@aws-cdk/core-synthesizer/package.json b/packages/@aws-cdk/core-synthesizer/package.json new file mode 100644 index 0000000000000..02135aa4a0d9b --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/package.json @@ -0,0 +1,99 @@ +{ + "name": "@aws-cdk/core-synthesizer", + "private": true, + "version": "0.0.0", + "description": "Cdk synthesizer for Bootstrap v3", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "jsii": { + "outdir": "dist", + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + }, + "targets": { + "java": { + "maven": { + "groupId": "software.amazon.awscdk", + "artifactId": "cdk-core-synthesizer" + }, + "package": "software.amazon.awscdk.core.synthesizer" + }, + "python": { + "distName": "aws-cdk.core-synthesizer", + "module": "aws_cdk.core_synthesizer", + "classifiers": [ + "Framework :: AWS CDK", + "Framework :: AWS CDK :: 2" + ] + }, + "dotnet": { + "namespace": "Amazon.CDK.Core.Synthesizer", + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/main/logo/default-256-dark.png" + } + } + }, + "repository": { + "type": "git", + "url": "https://github.com/aws/aws-cdk.git", + "directory": "packages/@aws-cdk/core-synthesizer" + }, + "scripts": { + "build": "cdk-build", + "watch": "cdk-watch", + "lint": "cdk-lint", + "test": "cdk-test", + "integ": "integ-runner", + "pkglint": "pkglint -f", + "package": "cdk-package", + "awslint": "cdk-awslint", + "build+test": "yarn build && yarn test", + "build+test+package": "yarn build+test && yarn package", + "compat": "cdk-compat", + "rosetta:extract": "yarn --silent jsii-rosetta extract", + "build+extract": "yarn build && yarn rosetta:extract", + "build+test+extract": "yarn build+test && yarn rosetta:extract" + }, + "keywords": [ + "aws", + "cdk" + ], + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com", + "organization": true + }, + "license": "Apache-2.0", + "homepage": "https://github.com/aws/aws-cdk", + "engines": { + "node": ">= 14.15.0" + }, + "stability": "experimental", + "maturity": "experimental", + "awscdkio": { + "announce": false + }, + "cdk-build": { + "env": { + "AWSLINT_BASE_CONSTRUCT": true + } + }, + "dependencies": { + "@aws-cdk/core": "0.0.0", + "constructs": "^10.0.0" + }, + "devDependencies": { + "@aws-cdk/core": "0.0.0", + "constructs": "^10.0.0", + "@aws-cdk/cdk-build-tools": "0.0.0", + "@aws-cdk/pkglint": "0.0.0" + }, + "peerDependencies": { + "@aws-cdk/core": "0.0.0", + "constructs": "^10.0.0" + } +} diff --git a/packages/@aws-cdk/core-synthesizer/tsconfig.json b/packages/@aws-cdk/core-synthesizer/tsconfig.json new file mode 100644 index 0000000000000..e4f19ec08ec72 --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/tsconfig.json @@ -0,0 +1,51 @@ +{ + "compilerOptions": { + "declarationMap": false, + "inlineSourceMap": true, + "inlineSources": true, + "alwaysStrict": true, + "charset": "utf8", + "declaration": true, + "experimentalDecorators": true, + "incremental": true, + "lib": [ + "es2020" + ], + "module": "CommonJS", + "newLine": "lf", + "noEmitOnError": true, + "noFallthroughCasesInSwitch": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true, + "strictNullChecks": true, + "strictPropertyInitialization": true, + "stripInternal": false, + "target": "ES2020", + "composite": true, + "tsBuildInfoFile": "tsconfig.tsbuildinfo" + }, + "include": [ + "**/*.ts" + ], + "exclude": [ + "node_modules" + ], + "references": [ + { + "path": "../core" + }, + { + "path": "../../../tools/@aws-cdk/cdk-build-tools" + }, + { + "path": "../../../tools/@aws-cdk/pkglint" + } + ], + "_generated_by_jsii_": "Generated by jsii - safe to delete, and ideally should be in .gitignore" +} From b6fd1eb9fc7333f25a49074aa0cbafe0adb5efc4 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 2 Mar 2023 18:15:54 -0500 Subject: [PATCH 002/120] skeleton implementation --- packages/@aws-cdk/core-synthesizer/.gitignore | 22 ++- packages/@aws-cdk/core-synthesizer/.jsii | 139 +++++++++++++++++- .../core-synthesizer/lib/staging-stack.ts | 34 +++++ .../core-synthesizer/lib/synthesizer.ts | 56 +++++++ .../@aws-cdk/core-synthesizer/package.json | 10 +- .../@aws-cdk/core-synthesizer/tsconfig.json | 6 + 6 files changed, 260 insertions(+), 7 deletions(-) create mode 100644 packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts create mode 100644 packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts diff --git a/packages/@aws-cdk/core-synthesizer/.gitignore b/packages/@aws-cdk/core-synthesizer/.gitignore index dfd5365951031..1272e8254630e 100644 --- a/packages/@aws-cdk/core-synthesizer/.gitignore +++ b/packages/@aws-cdk/core-synthesizer/.gitignore @@ -1,8 +1,22 @@ +*.js +*.d.ts +tsconfig.json +*.generated.ts +*.js.map +dist +coverage +.nyc_output +.jsii .LAST_BUILD +nyc.config.js +.LAST_PACKAGE *.snk +!.eslintrc.js + junit.xml -.nyc_output -coverage -nyc.config.js -!.eslintrc.js \ No newline at end of file +!jest.config.js +!**/*.snapshot/**/asset.*/*.js +!**/*.snapshot/**/asset.*/*.d.ts + +!**/*.snapshot/**/asset.*/** diff --git a/packages/@aws-cdk/core-synthesizer/.jsii b/packages/@aws-cdk/core-synthesizer/.jsii index 0a04cb73a64d7..aeab90bd39be8 100644 --- a/packages/@aws-cdk/core-synthesizer/.jsii +++ b/packages/@aws-cdk/core-synthesizer/.jsii @@ -8,10 +8,147 @@ "url": "https://aws.amazon.com" }, "dependencies": { + "@aws-cdk/aws-ecr": "0.0.0", + "@aws-cdk/aws-s3": "0.0.0", "@aws-cdk/core": "0.0.0", "constructs": "^10.0.0" }, "dependencyClosure": { + "@aws-cdk/aws-ecr": { + "targets": { + "dotnet": { + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/main/logo/default-256-dark.png", + "namespace": "Amazon.CDK.AWS.ECR", + "packageId": "Amazon.CDK.AWS.ECR" + }, + "java": { + "maven": { + "artifactId": "ecr", + "groupId": "software.amazon.awscdk" + }, + "package": "software.amazon.awscdk.services.ecr" + }, + "js": { + "npm": "@aws-cdk/aws-ecr" + }, + "python": { + "classifiers": [ + "Framework :: AWS CDK", + "Framework :: AWS CDK :: 2" + ], + "distName": "aws-cdk.aws-ecr", + "module": "aws_cdk.aws_ecr" + } + } + }, + "@aws-cdk/aws-events": { + "targets": { + "dotnet": { + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/main/logo/default-256-dark.png", + "namespace": "Amazon.CDK.AWS.Events", + "packageId": "Amazon.CDK.AWS.Events" + }, + "java": { + "maven": { + "artifactId": "events", + "groupId": "software.amazon.awscdk" + }, + "package": "software.amazon.awscdk.services.events" + }, + "js": { + "npm": "@aws-cdk/aws-events" + }, + "python": { + "classifiers": [ + "Framework :: AWS CDK", + "Framework :: AWS CDK :: 2" + ], + "distName": "aws-cdk.aws-events", + "module": "aws_cdk.aws_events" + } + } + }, + "@aws-cdk/aws-iam": { + "targets": { + "dotnet": { + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/main/logo/default-256-dark.png", + "namespace": "Amazon.CDK.AWS.IAM", + "packageId": "Amazon.CDK.AWS.IAM" + }, + "java": { + "maven": { + "artifactId": "iam", + "groupId": "software.amazon.awscdk" + }, + "package": "software.amazon.awscdk.services.iam" + }, + "js": { + "npm": "@aws-cdk/aws-iam" + }, + "python": { + "classifiers": [ + "Framework :: AWS CDK", + "Framework :: AWS CDK :: 2" + ], + "distName": "aws-cdk.aws-iam", + "module": "aws_cdk.aws_iam" + } + } + }, + "@aws-cdk/aws-kms": { + "targets": { + "dotnet": { + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/main/logo/default-256-dark.png", + "namespace": "Amazon.CDK.AWS.KMS", + "packageId": "Amazon.CDK.AWS.KMS" + }, + "java": { + "maven": { + "artifactId": "kms", + "groupId": "software.amazon.awscdk" + }, + "package": "software.amazon.awscdk.services.kms" + }, + "js": { + "npm": "@aws-cdk/aws-kms" + }, + "python": { + "classifiers": [ + "Framework :: AWS CDK", + "Framework :: AWS CDK :: 2" + ], + "distName": "aws-cdk.aws-kms", + "module": "aws_cdk.aws_kms" + } + } + }, + "@aws-cdk/aws-s3": { + "targets": { + "dotnet": { + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/main/logo/default-256-dark.png", + "namespace": "Amazon.CDK.AWS.S3", + "packageId": "Amazon.CDK.AWS.S3" + }, + "java": { + "maven": { + "artifactId": "s3", + "groupId": "software.amazon.awscdk" + }, + "package": "software.amazon.awscdk.services.s3" + }, + "js": { + "npm": "@aws-cdk/aws-s3" + }, + "python": { + "classifiers": [ + "Framework :: AWS CDK", + "Framework :: AWS CDK :: 2" + ], + "distName": "aws-cdk.aws-s3", + "module": "aws_cdk.aws_s3" + } + } + }, "@aws-cdk/cloud-assembly-schema": { "targets": { "dotnet": { @@ -204,5 +341,5 @@ }, "types": {}, "version": "0.0.0", - "fingerprint": "W+VjVFm5LaJ+VNOiYH1Fpha37M8IeMCXUwCda6OxWmU=" + "fingerprint": "a0Z5vBS/P+7FmRo3kYrn9919CBBimwoFrAbS7yZ6+Pw=" } \ No newline at end of file diff --git a/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts b/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts new file mode 100644 index 0000000000000..87d25fb6b9a9b --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts @@ -0,0 +1,34 @@ +import * as ecr from '@aws-cdk/aws-ecr'; +import * as s3 from '@aws-cdk/aws-s3'; +import { App, DockerImageAssetSource, RemovalPolicy, Stack, StackProps } from '@aws-cdk/core'; + +export interface IStagingStack { + readonly stagingBucket: s3.Bucket; + readonly stagingRepos: Record; + + locateRepo(asset: DockerImageAssetSource): ecr.Repository; +} + +export interface StagingStackProps extends StackProps { +} + +export class StagingStack extends Stack implements IStagingStack { + public readonly stagingBucket: s3.Bucket; + public readonly stagingRepos: Record; + + constructor(scope: App, id: string, props: StagingStackProps) { + super(scope, id, props); + + this.stagingBucket = new s3.Bucket(this, 'StagingBucket', { + autoDeleteObjects: true, + removalPolicy: RemovalPolicy.DESTROY, + }); + + this.stagingRepos = {}; + } + + public locateRepo(asset: DockerImageAssetSource): ecr.Repository { + // TODO: not the source hash! construct path + return this.stagingRepos[asset.sourceHash]; + } +} diff --git a/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts new file mode 100644 index 0000000000000..fb9c90953b4fc --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts @@ -0,0 +1,56 @@ +import { App, AssetManifestBuilder, DockerImageAssetLocation, DockerImageAssetSource, FileAssetLocation, FileAssetSource, IBoundStackSynthesizer, IReusableStackSynthesizer, ISynthesisSession, Stack, StackSynthesizer } from '@aws-cdk/core'; +import { assertBound } from '@aws-cdk/core/lib/stack-synthesizers/_shared'; +import { IStagingStack, StagingStack } from './staging-stack'; + +export interface NewStackSynthesizerProps { + stagingStack?: IStagingStack; +} + +export class NewStackSynthesizer extends StackSynthesizer implements IReusableStackSynthesizer, IBoundStackSynthesizer { + private qualifier?: string; + private stagingStack: IStagingStack; + private assetManifest = new AssetManifestBuilder(); + + constructor(props: NewStackSynthesizerProps = {}) { + super(); + + // TODO: ensure no tokens + + // TODO: revisit scope + this.stagingStack = props.stagingStack ?? new StagingStack(new App(), 'StagingStack', {}); + } + + public reusableBind(stack: Stack): IBoundStackSynthesizer { + // Create a copy of the current object and bind that + const copy = Object.create(this); + copy.bind(stack); + return copy; + } + + public synthesize(_session: ISynthesisSession): void { + assertBound(this.qualifier); + + // TODO: finish implementing + } + + public addFileAsset(asset: FileAssetSource): FileAssetLocation { + assertBound(this.stagingStack.stagingBucket.bucketName); + + const location = this.assetManifest.defaultAddFileAsset(this.boundStack, asset, { + bucketName: this.stagingStack.stagingBucket.bucketName, + // TODO: more props + }); + return this.cloudFormationLocationFromFileAsset(location); + } + + public addDockerImageAsset(asset: DockerImageAssetSource): DockerImageAssetLocation { + const repo = this.stagingStack.locateRepo(asset); + assertBound(repo.repositoryName); + + const location = this.assetManifest.defaultAddDockerImageAsset(this.boundStack, asset, { + repositoryName: repo.repositoryName, + // TODO: more props + }); + return this.cloudFormationLocationFromDockerImageAsset(location); + } +} diff --git a/packages/@aws-cdk/core-synthesizer/package.json b/packages/@aws-cdk/core-synthesizer/package.json index 02135aa4a0d9b..9e8a1f4355d6e 100644 --- a/packages/@aws-cdk/core-synthesizer/package.json +++ b/packages/@aws-cdk/core-synthesizer/package.json @@ -84,16 +84,22 @@ }, "dependencies": { "@aws-cdk/core": "0.0.0", - "constructs": "^10.0.0" + "constructs": "^10.0.0", + "@aws-cdk/aws-s3": "0.0.0", + "@aws-cdk/aws-ecr": "0.0.0" }, "devDependencies": { "@aws-cdk/core": "0.0.0", "constructs": "^10.0.0", + "@aws-cdk/aws-s3": "0.0.0", + "@aws-cdk/aws-ecr": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/pkglint": "0.0.0" }, "peerDependencies": { "@aws-cdk/core": "0.0.0", - "constructs": "^10.0.0" + "constructs": "^10.0.0", + "@aws-cdk/aws-s3": "0.0.0", + "@aws-cdk/aws-ecr": "0.0.0" } } diff --git a/packages/@aws-cdk/core-synthesizer/tsconfig.json b/packages/@aws-cdk/core-synthesizer/tsconfig.json index e4f19ec08ec72..ba8491cbabd52 100644 --- a/packages/@aws-cdk/core-synthesizer/tsconfig.json +++ b/packages/@aws-cdk/core-synthesizer/tsconfig.json @@ -40,6 +40,12 @@ { "path": "../core" }, + { + "path": "../aws-s3" + }, + { + "path": "../aws-ecr" + }, { "path": "../../../tools/@aws-cdk/cdk-build-tools" }, From 6b59b29bde0b091024ec1c3482789c4cff1195a7 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 2 Mar 2023 18:39:31 -0500 Subject: [PATCH 003/120] get everything to compile --- packages/@aws-cdk/core-synthesizer/.jsii | 396 +++++++++++++++++- .../core-synthesizer/.warnings.jsii.js | 22 +- .../@aws-cdk/core-synthesizer/lib/index.d.ts | 3 +- .../@aws-cdk/core-synthesizer/lib/index.js | 22 +- .../@aws-cdk/core-synthesizer/lib/index.ts | 9 +- .../core-synthesizer/lib/staging-stack.ts | 35 +- .../core-synthesizer/lib/synthesizer.ts | 25 +- .../@aws-cdk/core-synthesizer/package.json | 3 +- 8 files changed, 488 insertions(+), 27 deletions(-) diff --git a/packages/@aws-cdk/core-synthesizer/.jsii b/packages/@aws-cdk/core-synthesizer/.jsii index aeab90bd39be8..1e773e3004854 100644 --- a/packages/@aws-cdk/core-synthesizer/.jsii +++ b/packages/@aws-cdk/core-synthesizer/.jsii @@ -288,7 +288,7 @@ "stability": "experimental" }, "homepage": "https://github.com/aws/aws-cdk", - "jsiiVersion": "1.74.0 (build 6d08790)", + "jsiiVersion": "1.76.0 (build a2651be)", "keywords": [ "aws", "cdk" @@ -339,7 +339,397 @@ "module": "aws_cdk.core_synthesizer" } }, - "types": {}, + "types": { + "@aws-cdk/core-synthesizer.IStagingStack": { + "assembly": "@aws-cdk/core-synthesizer", + "docs": { + "stability": "experimental", + "summary": "Information on how a Staging Stack should look." + }, + "fqn": "@aws-cdk/core-synthesizer.IStagingStack", + "interfaces": [ + "constructs.IConstruct" + ], + "kind": "interface", + "locationInModule": { + "filename": "lib/staging-stack.ts", + "line": 9 + }, + "methods": [ + { + "abstract": true, + "docs": { + "stability": "experimental", + "summary": "// TODO." + }, + "locationInModule": { + "filename": "lib/staging-stack.ts", + "line": 23 + }, + "name": "locateRepo", + "parameters": [ + { + "name": "asset", + "type": { + "fqn": "@aws-cdk/core.DockerImageAssetSource" + } + } + ], + "returns": { + "type": { + "fqn": "@aws-cdk/aws-ecr.Repository" + } + } + } + ], + "name": "IStagingStack", + "properties": [ + { + "abstract": true, + "docs": { + "stability": "experimental", + "summary": "// TODO." + }, + "immutable": true, + "locationInModule": { + "filename": "lib/staging-stack.ts", + "line": 13 + }, + "name": "stagingBucket", + "type": { + "fqn": "@aws-cdk/aws-s3.Bucket" + } + }, + { + "abstract": true, + "docs": { + "stability": "experimental", + "summary": "// TODO." + }, + "immutable": true, + "locationInModule": { + "filename": "lib/staging-stack.ts", + "line": 18 + }, + "name": "stagingRepos", + "type": { + "collection": { + "elementtype": { + "fqn": "@aws-cdk/aws-ecr.Repository" + }, + "kind": "map" + } + } + } + ], + "symbolId": "lib/staging-stack:IStagingStack" + }, + "@aws-cdk/core-synthesizer.NewStackSynthesizer": { + "assembly": "@aws-cdk/core-synthesizer", + "base": "@aws-cdk/core.StackSynthesizer", + "docs": { + "stability": "experimental", + "summary": "New Stack Synthesizer." + }, + "fqn": "@aws-cdk/core-synthesizer.NewStackSynthesizer", + "initializer": { + "docs": { + "stability": "experimental" + }, + "locationInModule": { + "filename": "lib/synthesizer.ts", + "line": 25 + }, + "parameters": [ + { + "name": "props", + "optional": true, + "type": { + "fqn": "@aws-cdk/core-synthesizer.NewStackSynthesizerProps" + } + } + ] + }, + "interfaces": [ + "@aws-cdk/core.IReusableStackSynthesizer", + "@aws-cdk/core.IBoundStackSynthesizer" + ], + "kind": "class", + "locationInModule": { + "filename": "lib/synthesizer.ts", + "line": 20 + }, + "methods": [ + { + "docs": { + "stability": "experimental", + "summary": "// TODO." + }, + "locationInModule": { + "filename": "lib/synthesizer.ts", + "line": 69 + }, + "name": "addDockerImageAsset", + "overrides": "@aws-cdk/core.StackSynthesizer", + "parameters": [ + { + "name": "asset", + "type": { + "fqn": "@aws-cdk/core.DockerImageAssetSource" + } + } + ], + "returns": { + "type": { + "fqn": "@aws-cdk/core.DockerImageAssetLocation" + } + } + }, + { + "docs": { + "stability": "experimental", + "summary": "// TODO." + }, + "locationInModule": { + "filename": "lib/synthesizer.ts", + "line": 56 + }, + "name": "addFileAsset", + "overrides": "@aws-cdk/core.StackSynthesizer", + "parameters": [ + { + "name": "asset", + "type": { + "fqn": "@aws-cdk/core.FileAssetSource" + } + } + ], + "returns": { + "type": { + "fqn": "@aws-cdk/core.FileAssetLocation" + } + } + }, + { + "docs": { + "stability": "experimental", + "summary": "// TODO." + }, + "locationInModule": { + "filename": "lib/synthesizer.ts", + "line": 37 + }, + "name": "reusableBind", + "overrides": "@aws-cdk/core.IReusableStackSynthesizer", + "parameters": [ + { + "name": "stack", + "type": { + "fqn": "@aws-cdk/core.Stack" + } + } + ], + "returns": { + "type": { + "fqn": "@aws-cdk/core.IBoundStackSynthesizer" + } + } + }, + { + "docs": { + "stability": "experimental", + "summary": "// TODO." + }, + "locationInModule": { + "filename": "lib/synthesizer.ts", + "line": 47 + }, + "name": "synthesize", + "overrides": "@aws-cdk/core.StackSynthesizer", + "parameters": [ + { + "name": "_session", + "type": { + "fqn": "@aws-cdk/core.ISynthesisSession" + } + } + ] + } + ], + "name": "NewStackSynthesizer", + "symbolId": "lib/synthesizer:NewStackSynthesizer" + }, + "@aws-cdk/core-synthesizer.NewStackSynthesizerProps": { + "assembly": "@aws-cdk/core-synthesizer", + "datatype": true, + "docs": { + "stability": "experimental", + "summary": "New stack synthesizer properties." + }, + "fqn": "@aws-cdk/core-synthesizer.NewStackSynthesizerProps", + "kind": "interface", + "locationInModule": { + "filename": "lib/synthesizer.ts", + "line": 8 + }, + "name": "NewStackSynthesizerProps", + "properties": [ + { + "abstract": true, + "docs": { + "default": "- default staging stack", + "stability": "experimental", + "summary": "Bring a custom staging stack into the app." + }, + "immutable": true, + "locationInModule": { + "filename": "lib/synthesizer.ts", + "line": 14 + }, + "name": "stagingStack", + "optional": true, + "type": { + "fqn": "@aws-cdk/core-synthesizer.IStagingStack" + } + } + ], + "symbolId": "lib/synthesizer:NewStackSynthesizerProps" + }, + "@aws-cdk/core-synthesizer.StagingStack": { + "assembly": "@aws-cdk/core-synthesizer", + "base": "@aws-cdk/core.Stack", + "docs": { + "stability": "experimental", + "summary": "A default Staging Stack." + }, + "fqn": "@aws-cdk/core-synthesizer.StagingStack", + "initializer": { + "docs": { + "stability": "experimental" + }, + "locationInModule": { + "filename": "lib/staging-stack.ts", + "line": 46 + }, + "parameters": [ + { + "name": "scope", + "type": { + "fqn": "constructs.Construct" + } + }, + { + "name": "id", + "type": { + "primitive": "string" + } + }, + { + "name": "props", + "optional": true, + "type": { + "fqn": "@aws-cdk/core-synthesizer.StagingStackProps" + } + } + ] + }, + "interfaces": [ + "@aws-cdk/core-synthesizer.IStagingStack" + ], + "kind": "class", + "locationInModule": { + "filename": "lib/staging-stack.ts", + "line": 35 + }, + "methods": [ + { + "docs": { + "stability": "experimental", + "summary": "// TODO." + }, + "locationInModule": { + "filename": "lib/staging-stack.ts", + "line": 57 + }, + "name": "locateRepo", + "overrides": "@aws-cdk/core-synthesizer.IStagingStack", + "parameters": [ + { + "name": "asset", + "type": { + "fqn": "@aws-cdk/core.DockerImageAssetSource" + } + } + ], + "returns": { + "type": { + "fqn": "@aws-cdk/aws-ecr.Repository" + } + } + } + ], + "name": "StagingStack", + "properties": [ + { + "docs": { + "stability": "experimental", + "summary": "// TODO." + }, + "immutable": true, + "locationInModule": { + "filename": "lib/staging-stack.ts", + "line": 39 + }, + "name": "stagingBucket", + "overrides": "@aws-cdk/core-synthesizer.IStagingStack", + "type": { + "fqn": "@aws-cdk/aws-s3.Bucket" + } + }, + { + "docs": { + "stability": "experimental", + "summary": "// TODO." + }, + "immutable": true, + "locationInModule": { + "filename": "lib/staging-stack.ts", + "line": 44 + }, + "name": "stagingRepos", + "overrides": "@aws-cdk/core-synthesizer.IStagingStack", + "type": { + "collection": { + "elementtype": { + "fqn": "@aws-cdk/aws-ecr.Repository" + }, + "kind": "map" + } + } + } + ], + "symbolId": "lib/staging-stack:StagingStack" + }, + "@aws-cdk/core-synthesizer.StagingStackProps": { + "assembly": "@aws-cdk/core-synthesizer", + "datatype": true, + "docs": { + "stability": "experimental", + "summary": "Staging Stack Properties." + }, + "fqn": "@aws-cdk/core-synthesizer.StagingStackProps", + "interfaces": [ + "@aws-cdk/core.StackProps" + ], + "kind": "interface", + "locationInModule": { + "filename": "lib/staging-stack.ts", + "line": 29 + }, + "name": "StagingStackProps", + "symbolId": "lib/staging-stack:StagingStackProps" + } + }, "version": "0.0.0", - "fingerprint": "a0Z5vBS/P+7FmRo3kYrn9919CBBimwoFrAbS7yZ6+Pw=" + "fingerprint": "nuiVlxDyaL7rerGX5Nz/mvwg03eZ85f9ecMCIhK/d2U=" } \ No newline at end of file diff --git a/packages/@aws-cdk/core-synthesizer/.warnings.jsii.js b/packages/@aws-cdk/core-synthesizer/.warnings.jsii.js index f1bdca28019e4..86aec8ac31693 100644 --- a/packages/@aws-cdk/core-synthesizer/.warnings.jsii.js +++ b/packages/@aws-cdk/core-synthesizer/.warnings.jsii.js @@ -1,3 +1,23 @@ +function _aws_cdk_core_synthesizer_IStagingStack(p) { +} +function _aws_cdk_core_synthesizer_StagingStackProps(p) { +} +function _aws_cdk_core_synthesizer_StagingStack(p) { +} +function _aws_cdk_core_synthesizer_NewStackSynthesizerProps(p) { + if (p == null) + return; + visitedObjects.add(p); + try { + if (!visitedObjects.has(p.stagingStack)) + _aws_cdk_core_synthesizer_IStagingStack(p.stagingStack); + } + finally { + visitedObjects.delete(p); + } +} +function _aws_cdk_core_synthesizer_NewStackSynthesizer(p) { +} function print(name, deprecationMessage) { const deprecated = process.env.JSII_DEPRECATED; const deprecationMode = ["warn", "fail", "quiet"].includes(deprecated) ? deprecated : "warn"; @@ -34,4 +54,4 @@ class DeprecationError extends Error { }); } } -module.exports = { print, getPropertyDescriptor, DeprecationError }; +module.exports = { print, getPropertyDescriptor, DeprecationError, _aws_cdk_core_synthesizer_IStagingStack, _aws_cdk_core_synthesizer_StagingStackProps, _aws_cdk_core_synthesizer_StagingStack, _aws_cdk_core_synthesizer_NewStackSynthesizerProps, _aws_cdk_core_synthesizer_NewStackSynthesizer }; diff --git a/packages/@aws-cdk/core-synthesizer/lib/index.d.ts b/packages/@aws-cdk/core-synthesizer/lib/index.d.ts index 7df27ddf55ea0..df4d2cb14226a 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/index.d.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/index.d.ts @@ -1 +1,2 @@ -export declare function main(): void; +export * from './staging-stack'; +export * from './synthesizer'; diff --git a/packages/@aws-cdk/core-synthesizer/lib/index.js b/packages/@aws-cdk/core-synthesizer/lib/index.js index 0f8c34ce8a27b..9318510927a90 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/index.js +++ b/packages/@aws-cdk/core-synthesizer/lib/index.js @@ -1,11 +1,15 @@ "use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; Object.defineProperty(exports, "__esModule", { value: true }); -exports.main = void 0; -const core = require("@aws-cdk/core"); -function main() { - const a = new core.App(); - // eslint-disable-next-line no-console - console.log(a.toString()); -} -exports.main = main; -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxzQ0FBc0M7QUFFdEMsU0FBZ0IsSUFBSTtJQUNsQixNQUFNLENBQUMsR0FBRyxJQUFJLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUN6QixzQ0FBc0M7SUFDdEMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQztBQUM1QixDQUFDO0FBSkQsb0JBSUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBjb3JlIGZyb20gJ0Bhd3MtY2RrL2NvcmUnO1xuXG5leHBvcnQgZnVuY3Rpb24gbWFpbigpIHtcbiAgY29uc3QgYSA9IG5ldyBjb3JlLkFwcCgpO1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZVxuICBjb25zb2xlLmxvZyhhLnRvU3RyaW5nKCkpO1xufSJdfQ== \ No newline at end of file +__exportStar(require("./staging-stack"), exports); +__exportStar(require("./synthesizer"), exports); +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7QUFBQSxrREFBZ0M7QUFDaEMsZ0RBQThCIiwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0ICogZnJvbSAnLi9zdGFnaW5nLXN0YWNrJztcbmV4cG9ydCAqIGZyb20gJy4vc3ludGhlc2l6ZXInO1xuIl19 \ No newline at end of file diff --git a/packages/@aws-cdk/core-synthesizer/lib/index.ts b/packages/@aws-cdk/core-synthesizer/lib/index.ts index dbb6031683650..df4d2cb14226a 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/index.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/index.ts @@ -1,7 +1,2 @@ -import * as core from '@aws-cdk/core'; - -export function main() { - const a = new core.App(); - // eslint-disable-next-line no-console - console.log(a.toString()); -} \ No newline at end of file +export * from './staging-stack'; +export * from './synthesizer'; diff --git a/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts b/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts index 87d25fb6b9a9b..501121bd5b24f 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts @@ -1,22 +1,49 @@ import * as ecr from '@aws-cdk/aws-ecr'; import * as s3 from '@aws-cdk/aws-s3'; -import { App, DockerImageAssetSource, RemovalPolicy, Stack, StackProps } from '@aws-cdk/core'; - -export interface IStagingStack { +import { DockerImageAssetSource, RemovalPolicy, Stack, StackProps } from '@aws-cdk/core'; +import { Construct, IConstruct } from 'constructs'; + +/** + * Information on how a Staging Stack should look. + */ +export interface IStagingStack extends IConstruct { + /** + * // TODO + */ readonly stagingBucket: s3.Bucket; + + /** + * // TODO + */ readonly stagingRepos: Record; + /** + * // TODO + */ locateRepo(asset: DockerImageAssetSource): ecr.Repository; } +/** + * Staging Stack Properties + */ export interface StagingStackProps extends StackProps { } +/** + * A default Staging Stack + */ export class StagingStack extends Stack implements IStagingStack { + /** + * // TODO + */ public readonly stagingBucket: s3.Bucket; + + /** + * // TODO + */ public readonly stagingRepos: Record; - constructor(scope: App, id: string, props: StagingStackProps) { + constructor(scope: Construct, id: string, props: StagingStackProps = {}) { super(scope, id, props); this.stagingBucket = new s3.Bucket(this, 'StagingBucket', { diff --git a/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts index fb9c90953b4fc..96030f18bd4d5 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts @@ -2,10 +2,21 @@ import { App, AssetManifestBuilder, DockerImageAssetLocation, DockerImageAssetSo import { assertBound } from '@aws-cdk/core/lib/stack-synthesizers/_shared'; import { IStagingStack, StagingStack } from './staging-stack'; +/** + * New stack synthesizer properties + */ export interface NewStackSynthesizerProps { - stagingStack?: IStagingStack; + /** + * Bring a custom staging stack into the app. + * + * @default - default staging stack + */ + readonly stagingStack?: IStagingStack; } +/** + * New Stack Synthesizer + */ export class NewStackSynthesizer extends StackSynthesizer implements IReusableStackSynthesizer, IBoundStackSynthesizer { private qualifier?: string; private stagingStack: IStagingStack; @@ -20,6 +31,9 @@ export class NewStackSynthesizer extends StackSynthesizer implements IReusableSt this.stagingStack = props.stagingStack ?? new StagingStack(new App(), 'StagingStack', {}); } + /** + * // TODO + */ public reusableBind(stack: Stack): IBoundStackSynthesizer { // Create a copy of the current object and bind that const copy = Object.create(this); @@ -27,12 +41,18 @@ export class NewStackSynthesizer extends StackSynthesizer implements IReusableSt return copy; } + /** + * // TODO + */ public synthesize(_session: ISynthesisSession): void { assertBound(this.qualifier); // TODO: finish implementing } + /** + * // TODO + */ public addFileAsset(asset: FileAssetSource): FileAssetLocation { assertBound(this.stagingStack.stagingBucket.bucketName); @@ -43,6 +63,9 @@ export class NewStackSynthesizer extends StackSynthesizer implements IReusableSt return this.cloudFormationLocationFromFileAsset(location); } + /** + * // TODO + */ public addDockerImageAsset(asset: DockerImageAssetSource): DockerImageAssetLocation { const repo = this.stagingStack.locateRepo(asset); assertBound(repo.repositoryName); diff --git a/packages/@aws-cdk/core-synthesizer/package.json b/packages/@aws-cdk/core-synthesizer/package.json index 9e8a1f4355d6e..692f1beb3982a 100644 --- a/packages/@aws-cdk/core-synthesizer/package.json +++ b/packages/@aws-cdk/core-synthesizer/package.json @@ -94,7 +94,8 @@ "@aws-cdk/aws-s3": "0.0.0", "@aws-cdk/aws-ecr": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", - "@aws-cdk/pkglint": "0.0.0" + "@aws-cdk/pkglint": "0.0.0", + "jsii": "v4.9-next" }, "peerDependencies": { "@aws-cdk/core": "0.0.0", From 4ca12fecc5b97ac484d5905e655b7f0385cbe544 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 2 Mar 2023 18:41:56 -0500 Subject: [PATCH 004/120] delete extraneous files --- packages/@aws-cdk/core-synthesizer/.jsii | 735 ------------------ .../core-synthesizer/.warnings.jsii.js | 57 -- .../@aws-cdk/core-synthesizer/lib/index.d.ts | 2 - .../@aws-cdk/core-synthesizer/lib/index.js | 15 - 4 files changed, 809 deletions(-) delete mode 100644 packages/@aws-cdk/core-synthesizer/.jsii delete mode 100644 packages/@aws-cdk/core-synthesizer/.warnings.jsii.js delete mode 100644 packages/@aws-cdk/core-synthesizer/lib/index.d.ts delete mode 100644 packages/@aws-cdk/core-synthesizer/lib/index.js diff --git a/packages/@aws-cdk/core-synthesizer/.jsii b/packages/@aws-cdk/core-synthesizer/.jsii deleted file mode 100644 index 1e773e3004854..0000000000000 --- a/packages/@aws-cdk/core-synthesizer/.jsii +++ /dev/null @@ -1,735 +0,0 @@ -{ - "author": { - "name": "Amazon Web Services", - "organization": true, - "roles": [ - "author" - ], - "url": "https://aws.amazon.com" - }, - "dependencies": { - "@aws-cdk/aws-ecr": "0.0.0", - "@aws-cdk/aws-s3": "0.0.0", - "@aws-cdk/core": "0.0.0", - "constructs": "^10.0.0" - }, - "dependencyClosure": { - "@aws-cdk/aws-ecr": { - "targets": { - "dotnet": { - "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/main/logo/default-256-dark.png", - "namespace": "Amazon.CDK.AWS.ECR", - "packageId": "Amazon.CDK.AWS.ECR" - }, - "java": { - "maven": { - "artifactId": "ecr", - "groupId": "software.amazon.awscdk" - }, - "package": "software.amazon.awscdk.services.ecr" - }, - "js": { - "npm": "@aws-cdk/aws-ecr" - }, - "python": { - "classifiers": [ - "Framework :: AWS CDK", - "Framework :: AWS CDK :: 2" - ], - "distName": "aws-cdk.aws-ecr", - "module": "aws_cdk.aws_ecr" - } - } - }, - "@aws-cdk/aws-events": { - "targets": { - "dotnet": { - "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/main/logo/default-256-dark.png", - "namespace": "Amazon.CDK.AWS.Events", - "packageId": "Amazon.CDK.AWS.Events" - }, - "java": { - "maven": { - "artifactId": "events", - "groupId": "software.amazon.awscdk" - }, - "package": "software.amazon.awscdk.services.events" - }, - "js": { - "npm": "@aws-cdk/aws-events" - }, - "python": { - "classifiers": [ - "Framework :: AWS CDK", - "Framework :: AWS CDK :: 2" - ], - "distName": "aws-cdk.aws-events", - "module": "aws_cdk.aws_events" - } - } - }, - "@aws-cdk/aws-iam": { - "targets": { - "dotnet": { - "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/main/logo/default-256-dark.png", - "namespace": "Amazon.CDK.AWS.IAM", - "packageId": "Amazon.CDK.AWS.IAM" - }, - "java": { - "maven": { - "artifactId": "iam", - "groupId": "software.amazon.awscdk" - }, - "package": "software.amazon.awscdk.services.iam" - }, - "js": { - "npm": "@aws-cdk/aws-iam" - }, - "python": { - "classifiers": [ - "Framework :: AWS CDK", - "Framework :: AWS CDK :: 2" - ], - "distName": "aws-cdk.aws-iam", - "module": "aws_cdk.aws_iam" - } - } - }, - "@aws-cdk/aws-kms": { - "targets": { - "dotnet": { - "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/main/logo/default-256-dark.png", - "namespace": "Amazon.CDK.AWS.KMS", - "packageId": "Amazon.CDK.AWS.KMS" - }, - "java": { - "maven": { - "artifactId": "kms", - "groupId": "software.amazon.awscdk" - }, - "package": "software.amazon.awscdk.services.kms" - }, - "js": { - "npm": "@aws-cdk/aws-kms" - }, - "python": { - "classifiers": [ - "Framework :: AWS CDK", - "Framework :: AWS CDK :: 2" - ], - "distName": "aws-cdk.aws-kms", - "module": "aws_cdk.aws_kms" - } - } - }, - "@aws-cdk/aws-s3": { - "targets": { - "dotnet": { - "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/main/logo/default-256-dark.png", - "namespace": "Amazon.CDK.AWS.S3", - "packageId": "Amazon.CDK.AWS.S3" - }, - "java": { - "maven": { - "artifactId": "s3", - "groupId": "software.amazon.awscdk" - }, - "package": "software.amazon.awscdk.services.s3" - }, - "js": { - "npm": "@aws-cdk/aws-s3" - }, - "python": { - "classifiers": [ - "Framework :: AWS CDK", - "Framework :: AWS CDK :: 2" - ], - "distName": "aws-cdk.aws-s3", - "module": "aws_cdk.aws_s3" - } - } - }, - "@aws-cdk/cloud-assembly-schema": { - "targets": { - "dotnet": { - "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/main/logo/default-256-dark.png", - "namespace": "Amazon.CDK.CloudAssembly.Schema", - "packageId": "Amazon.CDK.CloudAssembly.Schema" - }, - "java": { - "maven": { - "artifactId": "cdk-cloud-assembly-schema", - "groupId": "software.amazon.awscdk" - }, - "package": "software.amazon.awscdk.cloudassembly.schema" - }, - "js": { - "npm": "@aws-cdk/cloud-assembly-schema" - }, - "python": { - "classifiers": [ - "Framework :: AWS CDK", - "Framework :: AWS CDK :: 2" - ], - "distName": "aws-cdk.cloud-assembly-schema", - "module": "aws_cdk.cloud_assembly_schema" - } - } - }, - "@aws-cdk/core": { - "targets": { - "dotnet": { - "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/main/logo/default-256-dark.png", - "namespace": "Amazon.CDK", - "packageId": "Amazon.CDK" - }, - "java": { - "maven": { - "artifactId": "core", - "groupId": "software.amazon.awscdk" - }, - "package": "software.amazon.awscdk.core" - }, - "js": { - "npm": "@aws-cdk/core" - }, - "python": { - "classifiers": [ - "Framework :: AWS CDK", - "Framework :: AWS CDK :: 2" - ], - "distName": "aws-cdk.core", - "module": "aws_cdk.core" - } - } - }, - "@aws-cdk/cx-api": { - "targets": { - "dotnet": { - "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/main/logo/default-256-dark.png", - "namespace": "Amazon.CDK.CXAPI", - "packageId": "Amazon.CDK.CXAPI" - }, - "java": { - "maven": { - "artifactId": "cdk-cx-api", - "groupId": "software.amazon.awscdk" - }, - "package": "software.amazon.awscdk.cxapi" - }, - "js": { - "npm": "@aws-cdk/cx-api" - }, - "python": { - "classifiers": [ - "Framework :: AWS CDK", - "Framework :: AWS CDK :: 2" - ], - "distName": "aws-cdk.cx-api", - "module": "aws_cdk.cx_api" - } - } - }, - "@aws-cdk/region-info": { - "targets": { - "dotnet": { - "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/main/logo/default-256-dark.png", - "namespace": "Amazon.CDK.RegionInfo", - "packageId": "Amazon.CDK.RegionInfo" - }, - "java": { - "maven": { - "artifactId": "cdk-region-info", - "groupId": "software.amazon.awscdk" - }, - "package": "software.amazon.awscdk.regioninfo" - }, - "js": { - "npm": "@aws-cdk/region-info" - }, - "python": { - "classifiers": [ - "Framework :: AWS CDK", - "Framework :: AWS CDK :: 2" - ], - "distName": "aws-cdk.region-info", - "module": "aws_cdk.region_info" - } - } - }, - "constructs": { - "targets": { - "dotnet": { - "namespace": "Constructs", - "packageId": "Constructs" - }, - "go": { - "moduleName": "github.com/aws/constructs-go" - }, - "java": { - "maven": { - "artifactId": "constructs", - "groupId": "software.constructs" - }, - "package": "software.constructs" - }, - "js": { - "npm": "constructs" - }, - "python": { - "distName": "constructs", - "module": "constructs" - } - } - } - }, - "description": "Cdk synthesizer for Bootstrap v3", - "docs": { - "stability": "experimental" - }, - "homepage": "https://github.com/aws/aws-cdk", - "jsiiVersion": "1.76.0 (build a2651be)", - "keywords": [ - "aws", - "cdk" - ], - "license": "Apache-2.0", - "metadata": { - "jsii": { - "compiledWithDeprecationWarnings": true, - "pacmak": { - "hasDefaultInterfaces": true - }, - "rosetta": { - "strict": true - } - } - }, - "name": "@aws-cdk/core-synthesizer", - "readme": { - "markdown": "# Synthesizer\n" - }, - "repository": { - "directory": "packages/@aws-cdk/core-synthesizer", - "type": "git", - "url": "https://github.com/aws/aws-cdk.git" - }, - "schema": "jsii/0.10.0", - "targets": { - "dotnet": { - "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/main/logo/default-256-dark.png", - "namespace": "Amazon.CDK.Core.Synthesizer" - }, - "java": { - "maven": { - "artifactId": "cdk-core-synthesizer", - "groupId": "software.amazon.awscdk" - }, - "package": "software.amazon.awscdk.core.synthesizer" - }, - "js": { - "npm": "@aws-cdk/core-synthesizer" - }, - "python": { - "classifiers": [ - "Framework :: AWS CDK", - "Framework :: AWS CDK :: 2" - ], - "distName": "aws-cdk.core-synthesizer", - "module": "aws_cdk.core_synthesizer" - } - }, - "types": { - "@aws-cdk/core-synthesizer.IStagingStack": { - "assembly": "@aws-cdk/core-synthesizer", - "docs": { - "stability": "experimental", - "summary": "Information on how a Staging Stack should look." - }, - "fqn": "@aws-cdk/core-synthesizer.IStagingStack", - "interfaces": [ - "constructs.IConstruct" - ], - "kind": "interface", - "locationInModule": { - "filename": "lib/staging-stack.ts", - "line": 9 - }, - "methods": [ - { - "abstract": true, - "docs": { - "stability": "experimental", - "summary": "// TODO." - }, - "locationInModule": { - "filename": "lib/staging-stack.ts", - "line": 23 - }, - "name": "locateRepo", - "parameters": [ - { - "name": "asset", - "type": { - "fqn": "@aws-cdk/core.DockerImageAssetSource" - } - } - ], - "returns": { - "type": { - "fqn": "@aws-cdk/aws-ecr.Repository" - } - } - } - ], - "name": "IStagingStack", - "properties": [ - { - "abstract": true, - "docs": { - "stability": "experimental", - "summary": "// TODO." - }, - "immutable": true, - "locationInModule": { - "filename": "lib/staging-stack.ts", - "line": 13 - }, - "name": "stagingBucket", - "type": { - "fqn": "@aws-cdk/aws-s3.Bucket" - } - }, - { - "abstract": true, - "docs": { - "stability": "experimental", - "summary": "// TODO." - }, - "immutable": true, - "locationInModule": { - "filename": "lib/staging-stack.ts", - "line": 18 - }, - "name": "stagingRepos", - "type": { - "collection": { - "elementtype": { - "fqn": "@aws-cdk/aws-ecr.Repository" - }, - "kind": "map" - } - } - } - ], - "symbolId": "lib/staging-stack:IStagingStack" - }, - "@aws-cdk/core-synthesizer.NewStackSynthesizer": { - "assembly": "@aws-cdk/core-synthesizer", - "base": "@aws-cdk/core.StackSynthesizer", - "docs": { - "stability": "experimental", - "summary": "New Stack Synthesizer." - }, - "fqn": "@aws-cdk/core-synthesizer.NewStackSynthesizer", - "initializer": { - "docs": { - "stability": "experimental" - }, - "locationInModule": { - "filename": "lib/synthesizer.ts", - "line": 25 - }, - "parameters": [ - { - "name": "props", - "optional": true, - "type": { - "fqn": "@aws-cdk/core-synthesizer.NewStackSynthesizerProps" - } - } - ] - }, - "interfaces": [ - "@aws-cdk/core.IReusableStackSynthesizer", - "@aws-cdk/core.IBoundStackSynthesizer" - ], - "kind": "class", - "locationInModule": { - "filename": "lib/synthesizer.ts", - "line": 20 - }, - "methods": [ - { - "docs": { - "stability": "experimental", - "summary": "// TODO." - }, - "locationInModule": { - "filename": "lib/synthesizer.ts", - "line": 69 - }, - "name": "addDockerImageAsset", - "overrides": "@aws-cdk/core.StackSynthesizer", - "parameters": [ - { - "name": "asset", - "type": { - "fqn": "@aws-cdk/core.DockerImageAssetSource" - } - } - ], - "returns": { - "type": { - "fqn": "@aws-cdk/core.DockerImageAssetLocation" - } - } - }, - { - "docs": { - "stability": "experimental", - "summary": "// TODO." - }, - "locationInModule": { - "filename": "lib/synthesizer.ts", - "line": 56 - }, - "name": "addFileAsset", - "overrides": "@aws-cdk/core.StackSynthesizer", - "parameters": [ - { - "name": "asset", - "type": { - "fqn": "@aws-cdk/core.FileAssetSource" - } - } - ], - "returns": { - "type": { - "fqn": "@aws-cdk/core.FileAssetLocation" - } - } - }, - { - "docs": { - "stability": "experimental", - "summary": "// TODO." - }, - "locationInModule": { - "filename": "lib/synthesizer.ts", - "line": 37 - }, - "name": "reusableBind", - "overrides": "@aws-cdk/core.IReusableStackSynthesizer", - "parameters": [ - { - "name": "stack", - "type": { - "fqn": "@aws-cdk/core.Stack" - } - } - ], - "returns": { - "type": { - "fqn": "@aws-cdk/core.IBoundStackSynthesizer" - } - } - }, - { - "docs": { - "stability": "experimental", - "summary": "// TODO." - }, - "locationInModule": { - "filename": "lib/synthesizer.ts", - "line": 47 - }, - "name": "synthesize", - "overrides": "@aws-cdk/core.StackSynthesizer", - "parameters": [ - { - "name": "_session", - "type": { - "fqn": "@aws-cdk/core.ISynthesisSession" - } - } - ] - } - ], - "name": "NewStackSynthesizer", - "symbolId": "lib/synthesizer:NewStackSynthesizer" - }, - "@aws-cdk/core-synthesizer.NewStackSynthesizerProps": { - "assembly": "@aws-cdk/core-synthesizer", - "datatype": true, - "docs": { - "stability": "experimental", - "summary": "New stack synthesizer properties." - }, - "fqn": "@aws-cdk/core-synthesizer.NewStackSynthesizerProps", - "kind": "interface", - "locationInModule": { - "filename": "lib/synthesizer.ts", - "line": 8 - }, - "name": "NewStackSynthesizerProps", - "properties": [ - { - "abstract": true, - "docs": { - "default": "- default staging stack", - "stability": "experimental", - "summary": "Bring a custom staging stack into the app." - }, - "immutable": true, - "locationInModule": { - "filename": "lib/synthesizer.ts", - "line": 14 - }, - "name": "stagingStack", - "optional": true, - "type": { - "fqn": "@aws-cdk/core-synthesizer.IStagingStack" - } - } - ], - "symbolId": "lib/synthesizer:NewStackSynthesizerProps" - }, - "@aws-cdk/core-synthesizer.StagingStack": { - "assembly": "@aws-cdk/core-synthesizer", - "base": "@aws-cdk/core.Stack", - "docs": { - "stability": "experimental", - "summary": "A default Staging Stack." - }, - "fqn": "@aws-cdk/core-synthesizer.StagingStack", - "initializer": { - "docs": { - "stability": "experimental" - }, - "locationInModule": { - "filename": "lib/staging-stack.ts", - "line": 46 - }, - "parameters": [ - { - "name": "scope", - "type": { - "fqn": "constructs.Construct" - } - }, - { - "name": "id", - "type": { - "primitive": "string" - } - }, - { - "name": "props", - "optional": true, - "type": { - "fqn": "@aws-cdk/core-synthesizer.StagingStackProps" - } - } - ] - }, - "interfaces": [ - "@aws-cdk/core-synthesizer.IStagingStack" - ], - "kind": "class", - "locationInModule": { - "filename": "lib/staging-stack.ts", - "line": 35 - }, - "methods": [ - { - "docs": { - "stability": "experimental", - "summary": "// TODO." - }, - "locationInModule": { - "filename": "lib/staging-stack.ts", - "line": 57 - }, - "name": "locateRepo", - "overrides": "@aws-cdk/core-synthesizer.IStagingStack", - "parameters": [ - { - "name": "asset", - "type": { - "fqn": "@aws-cdk/core.DockerImageAssetSource" - } - } - ], - "returns": { - "type": { - "fqn": "@aws-cdk/aws-ecr.Repository" - } - } - } - ], - "name": "StagingStack", - "properties": [ - { - "docs": { - "stability": "experimental", - "summary": "// TODO." - }, - "immutable": true, - "locationInModule": { - "filename": "lib/staging-stack.ts", - "line": 39 - }, - "name": "stagingBucket", - "overrides": "@aws-cdk/core-synthesizer.IStagingStack", - "type": { - "fqn": "@aws-cdk/aws-s3.Bucket" - } - }, - { - "docs": { - "stability": "experimental", - "summary": "// TODO." - }, - "immutable": true, - "locationInModule": { - "filename": "lib/staging-stack.ts", - "line": 44 - }, - "name": "stagingRepos", - "overrides": "@aws-cdk/core-synthesizer.IStagingStack", - "type": { - "collection": { - "elementtype": { - "fqn": "@aws-cdk/aws-ecr.Repository" - }, - "kind": "map" - } - } - } - ], - "symbolId": "lib/staging-stack:StagingStack" - }, - "@aws-cdk/core-synthesizer.StagingStackProps": { - "assembly": "@aws-cdk/core-synthesizer", - "datatype": true, - "docs": { - "stability": "experimental", - "summary": "Staging Stack Properties." - }, - "fqn": "@aws-cdk/core-synthesizer.StagingStackProps", - "interfaces": [ - "@aws-cdk/core.StackProps" - ], - "kind": "interface", - "locationInModule": { - "filename": "lib/staging-stack.ts", - "line": 29 - }, - "name": "StagingStackProps", - "symbolId": "lib/staging-stack:StagingStackProps" - } - }, - "version": "0.0.0", - "fingerprint": "nuiVlxDyaL7rerGX5Nz/mvwg03eZ85f9ecMCIhK/d2U=" -} \ No newline at end of file diff --git a/packages/@aws-cdk/core-synthesizer/.warnings.jsii.js b/packages/@aws-cdk/core-synthesizer/.warnings.jsii.js deleted file mode 100644 index 86aec8ac31693..0000000000000 --- a/packages/@aws-cdk/core-synthesizer/.warnings.jsii.js +++ /dev/null @@ -1,57 +0,0 @@ -function _aws_cdk_core_synthesizer_IStagingStack(p) { -} -function _aws_cdk_core_synthesizer_StagingStackProps(p) { -} -function _aws_cdk_core_synthesizer_StagingStack(p) { -} -function _aws_cdk_core_synthesizer_NewStackSynthesizerProps(p) { - if (p == null) - return; - visitedObjects.add(p); - try { - if (!visitedObjects.has(p.stagingStack)) - _aws_cdk_core_synthesizer_IStagingStack(p.stagingStack); - } - finally { - visitedObjects.delete(p); - } -} -function _aws_cdk_core_synthesizer_NewStackSynthesizer(p) { -} -function print(name, deprecationMessage) { - const deprecated = process.env.JSII_DEPRECATED; - const deprecationMode = ["warn", "fail", "quiet"].includes(deprecated) ? deprecated : "warn"; - const message = `${name} is deprecated.\n ${deprecationMessage.trim()}\n This API will be removed in the next major release.`; - switch (deprecationMode) { - case "fail": - throw new DeprecationError(message); - case "warn": - console.warn("[WARNING]", message); - break; - } -} -function getPropertyDescriptor(obj, prop) { - const descriptor = Object.getOwnPropertyDescriptor(obj, prop); - if (descriptor) { - return descriptor; - } - const proto = Object.getPrototypeOf(obj); - const prototypeDescriptor = proto && getPropertyDescriptor(proto, prop); - if (prototypeDescriptor) { - return prototypeDescriptor; - } - return {}; -} -const visitedObjects = new Set(); -class DeprecationError extends Error { - constructor(...args) { - super(...args); - Object.defineProperty(this, "name", { - configurable: false, - enumerable: true, - value: "DeprecationError", - writable: false, - }); - } -} -module.exports = { print, getPropertyDescriptor, DeprecationError, _aws_cdk_core_synthesizer_IStagingStack, _aws_cdk_core_synthesizer_StagingStackProps, _aws_cdk_core_synthesizer_StagingStack, _aws_cdk_core_synthesizer_NewStackSynthesizerProps, _aws_cdk_core_synthesizer_NewStackSynthesizer }; diff --git a/packages/@aws-cdk/core-synthesizer/lib/index.d.ts b/packages/@aws-cdk/core-synthesizer/lib/index.d.ts deleted file mode 100644 index df4d2cb14226a..0000000000000 --- a/packages/@aws-cdk/core-synthesizer/lib/index.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './staging-stack'; -export * from './synthesizer'; diff --git a/packages/@aws-cdk/core-synthesizer/lib/index.js b/packages/@aws-cdk/core-synthesizer/lib/index.js deleted file mode 100644 index 9318510927a90..0000000000000 --- a/packages/@aws-cdk/core-synthesizer/lib/index.js +++ /dev/null @@ -1,15 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __exportStar = (this && this.__exportStar) || function(m, exports) { - for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -__exportStar(require("./staging-stack"), exports); -__exportStar(require("./synthesizer"), exports); -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7QUFBQSxrREFBZ0M7QUFDaEMsZ0RBQThCIiwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0ICogZnJvbSAnLi9zdGFnaW5nLXN0YWNrJztcbmV4cG9ydCAqIGZyb20gJy4vc3ludGhlc2l6ZXInO1xuIl19 \ No newline at end of file From 6ee9d78496acb0291d9681cefc7fbf053975c782 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Fri, 3 Mar 2023 20:47:24 -0500 Subject: [PATCH 005/120] more untested progress --- .../core-synthesizer/lib/staging-stack.ts | 10 +- .../core-synthesizer/lib/synthesizer.ts | 104 ++++++++++++++---- .../@aws-cdk/core/lib/private/synthesis.ts | 1 + 3 files changed, 89 insertions(+), 26 deletions(-) diff --git a/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts b/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts index 501121bd5b24f..f573044f6673a 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts @@ -20,7 +20,7 @@ export interface IStagingStack extends IConstruct { /** * // TODO */ - locateRepo(asset: DockerImageAssetSource): ecr.Repository; + getCreateRepository(asset: DockerImageAssetSource): ecr.Repository; } /** @@ -54,8 +54,14 @@ export class StagingStack extends Stack implements IStagingStack { this.stagingRepos = {}; } - public locateRepo(asset: DockerImageAssetSource): ecr.Repository { + public getCreateRepository(asset: DockerImageAssetSource): ecr.Repository { // TODO: not the source hash! construct path + if (this.stagingRepos[asset.sourceHash] === undefined) { + this.stagingRepos[asset.sourceHash] = new ecr.Repository(this, `${asset.sourceHash}-repo`, { + // TODO: lifecycle rules + }); + } + return this.stagingRepos[asset.sourceHash]; } } diff --git a/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts index 96030f18bd4d5..a35ead7212bb9 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts @@ -1,61 +1,118 @@ -import { App, AssetManifestBuilder, DockerImageAssetLocation, DockerImageAssetSource, FileAssetLocation, FileAssetSource, IBoundStackSynthesizer, IReusableStackSynthesizer, ISynthesisSession, Stack, StackSynthesizer } from '@aws-cdk/core'; -import { assertBound } from '@aws-cdk/core/lib/stack-synthesizers/_shared'; +import { AssetManifestBuilder, DockerImageAssetLocation, DockerImageAssetSource, FileAssetLocation, FileAssetSource, IBoundStackSynthesizer, IReusableStackSynthesizer, ISynthesisSession, Stack, StackSynthesizer } from '@aws-cdk/core'; import { IStagingStack, StagingStack } from './staging-stack'; /** * New stack synthesizer properties */ -export interface NewStackSynthesizerProps { +export interface StagingStackSynthesizerProps { /** * Bring a custom staging stack into the app. * * @default - default staging stack */ readonly stagingStack?: IStagingStack; + readonly lookupRoleArn?: string; } /** * New Stack Synthesizer */ -export class NewStackSynthesizer extends StackSynthesizer implements IReusableStackSynthesizer, IBoundStackSynthesizer { - private qualifier?: string; - private stagingStack: IStagingStack; - private assetManifest = new AssetManifestBuilder(); - - constructor(props: NewStackSynthesizerProps = {}) { +export class UnboundStagingStackSynthesizer extends StackSynthesizer implements IReusableStackSynthesizer, IBoundStackSynthesizer { + constructor(private readonly props: StagingStackSynthesizerProps = {}) { super(); - // TODO: ensure no tokens - - // TODO: revisit scope - this.stagingStack = props.stagingStack ?? new StagingStack(new App(), 'StagingStack', {}); + // TODO: no tokens } /** - * // TODO + * Returns a version of the synthesizer bound to a stack. */ public reusableBind(stack: Stack): IBoundStackSynthesizer { - // Create a copy of the current object and bind that - const copy = Object.create(this); - copy.bind(stack); - return copy; + return new BoundStagingStackSynthesizer(stack, this.props); } /** - * // TODO + * Implemented for legacy purposes; this will never be called. + */ + public bind(_stack: Stack) { + throw new Error('This is a legacy API, call reusableBind instead'); + } + + /** + * Implemented for legacy purposes; this will never be called. */ public synthesize(_session: ISynthesisSession): void { - assertBound(this.qualifier); + throw new Error('This is a legacy API, call reusableBind instead'); + } + + /** + * Implemented for legacy purposes; this will never be called. + */ + public addFileAsset(_asset: FileAssetSource): FileAssetLocation { + throw new Error('This is a legacy API, call reusableBind instead'); + } + + /** + * Implemented for legacy purposes; this will never be called. + */ + public addDockerImageAsset(_asset: DockerImageAssetSource): DockerImageAssetLocation { + throw new Error('This is a legacy API, call reusableBind instead'); + } +} + +class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundStackSynthesizer { + private stagingStack: IStagingStack; + private assetManifest = new AssetManifestBuilder(); + private lookupRoleArn: string; + + constructor(stack: Stack, props: StagingStackSynthesizerProps) { + super(); + super.bind(stack); + + this.lookupRoleArn = props.lookupRoleArn ?? 'INSERT_DEFAULT'; + + // TODO: logic should be get or synthesize staging stack here, not create each time + this.stagingStack = props.stagingStack ?? new StagingStack(stack, 'StagingStack', { + env: { + account: stack.account, + region: stack.region, + }, + }); + } + + public synthesize(session: ISynthesisSession): void { + const templateAssetSource = this.synthesizeTemplate(session, this.lookupRoleArn); + const templateAsset = this.addFileAsset(templateAssetSource); + + const assetManifestId = this.assetManifest.emitManifest(this.boundStack, session, { + // TODO: handle verison props here + }); + + // eslint-disable-next-line no-console + console.log(templateAsset, assetManifestId); // TODO: finish implementing + // this.emitArtifact(session, { + // assumeRoleExternalId: this.props.deployRoleExternalId, + // assumeRoleArn: this._deployRoleArn, + // cloudFormationExecutionRoleArn: this._cloudFormationExecutionRoleArn, + // stackTemplateAssetObjectUrl: templateAsset.s3ObjectUrlWithPlaceholders, + // requiresBootstrapStackVersion: MIN_BOOTSTRAP_STACK_VERSION, + // bootstrapStackVersionSsmParameter: this.bootstrapStackVersionSsmParameter, + // additionalDependencies: [assetManifestId], + // lookupRole: this.useLookupRoleForStackOperations && this.lookupRoleArn ? { + // arn: this.lookupRoleArn, + // assumeRoleExternalId: this.props.lookupRoleExternalId, + // requiresBootstrapStackVersion: MIN_LOOKUP_ROLE_BOOTSTRAP_STACK_VERSION, + // bootstrapStackVersionSsmParameter: this.bootstrapStackVersionSsmParameter, + // } : undefined, + // }); } /** * // TODO */ public addFileAsset(asset: FileAssetSource): FileAssetLocation { - assertBound(this.stagingStack.stagingBucket.bucketName); - const location = this.assetManifest.defaultAddFileAsset(this.boundStack, asset, { bucketName: this.stagingStack.stagingBucket.bucketName, // TODO: more props @@ -67,8 +124,7 @@ export class NewStackSynthesizer extends StackSynthesizer implements IReusableSt * // TODO */ public addDockerImageAsset(asset: DockerImageAssetSource): DockerImageAssetLocation { - const repo = this.stagingStack.locateRepo(asset); - assertBound(repo.repositoryName); + const repo = this.stagingStack.getCreateRepository(asset); const location = this.assetManifest.defaultAddDockerImageAsset(this.boundStack, asset, { repositoryName: repo.repositoryName, diff --git a/packages/@aws-cdk/core/lib/private/synthesis.ts b/packages/@aws-cdk/core/lib/private/synthesis.ts index 8889281f52606..4b921df35f31f 100644 --- a/packages/@aws-cdk/core/lib/private/synthesis.ts +++ b/packages/@aws-cdk/core/lib/private/synthesis.ts @@ -199,6 +199,7 @@ function synthesizeTree(root: IConstruct, builder: cxapi.CloudAssemblyBuilder, v }; if (Stack.isStack(construct)) { + // TODO: I think I need to do something here to keep track of envs -> staging stacks, but not sure construct.synthesizer.synthesize(session); } else if (construct instanceof TreeMetadata) { construct._synthesizeTree(session); From 696d2759bcec5fdf2482beca01ba07f6599f6aea Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Fri, 3 Mar 2023 20:59:48 -0500 Subject: [PATCH 006/120] add bedrock for testing --- .../@aws-cdk/core-synthesizer/package.json | 3 +- .../core-synthesizer/test/synthesizer.test.ts | 34 +++++++++++++++++++ .../@aws-cdk/core-synthesizer/tsconfig.json | 3 ++ packages/@aws-cdk/core/lib/stack.ts | 10 +++++- packages/@aws-cdk/cx-api/lib/features.ts | 1 + 5 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts diff --git a/packages/@aws-cdk/core-synthesizer/package.json b/packages/@aws-cdk/core-synthesizer/package.json index 692f1beb3982a..bd0e19836ed28 100644 --- a/packages/@aws-cdk/core-synthesizer/package.json +++ b/packages/@aws-cdk/core-synthesizer/package.json @@ -86,7 +86,8 @@ "@aws-cdk/core": "0.0.0", "constructs": "^10.0.0", "@aws-cdk/aws-s3": "0.0.0", - "@aws-cdk/aws-ecr": "0.0.0" + "@aws-cdk/aws-ecr": "0.0.0", + "@aws-cdk/cx-api": "0.0.0" }, "devDependencies": { "@aws-cdk/core": "0.0.0", diff --git a/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts b/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts new file mode 100644 index 0000000000000..8a924ecf37af4 --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts @@ -0,0 +1,34 @@ +import { App, Stack, CfnResource } from '@aws-cdk/core'; +import * as cxapi from '@aws-cdk/cx-api'; + +describe('new style synthesis', () => { + let app: App; + let stack: Stack; + + beforeEach(() => { + app = new App({ + context: { + [cxapi.V3_STACK_SYNTHESIS_CONTEXT]: 'true', + }, + }); + stack = new Stack(app, 'Stack'); + + }); + + test('stack template is in asset manifest', () => { + // GIVEN + new CfnResource(stack, 'Resource', { + type: 'Some::Resource', + }); + + // WHEN + const asm = app.synth(); + + // THEN -- the S3 url is advertised on the stack artifact + const stackArtifact = asm.getStackArtifact('Stack'); + + // TODO: finish test. + // eslint-disable-next-line no-console + console.log(stackArtifact.stackName); + }); +}); diff --git a/packages/@aws-cdk/core-synthesizer/tsconfig.json b/packages/@aws-cdk/core-synthesizer/tsconfig.json index ba8491cbabd52..631ad1c94d240 100644 --- a/packages/@aws-cdk/core-synthesizer/tsconfig.json +++ b/packages/@aws-cdk/core-synthesizer/tsconfig.json @@ -46,6 +46,9 @@ { "path": "../aws-ecr" }, + { + "path": "../cx-api" + }, { "path": "../../../tools/@aws-cdk/cdk-build-tools" }, diff --git a/packages/@aws-cdk/core/lib/stack.ts b/packages/@aws-cdk/core/lib/stack.ts index 3b235c231fcfa..2197875b83cdd 100644 --- a/packages/@aws-cdk/core/lib/stack.ts +++ b/packages/@aws-cdk/core/lib/stack.ts @@ -426,7 +426,14 @@ export class Stack extends Construct implements ITaggable { const featureFlags = FeatureFlags.of(this); const stackNameDupeContext = featureFlags.isEnabled(cxapi.ENABLE_STACK_NAME_DUPLICATES_CONTEXT); const newStyleSynthesisContext = featureFlags.isEnabled(cxapi.NEW_STYLE_STACK_SYNTHESIS_CONTEXT); - this.artifactId = (stackNameDupeContext || newStyleSynthesisContext) + const v3StackSynthesis = featureFlags.isEnabled(cxapi.V3_STACK_SYNTHESIS_CONTEXT); + + // can only have one synthesis style set + if (newStyleSynthesisContext && v3StackSynthesis) { + throw new Error('can only set on kind of stack synthesis'); + } + + this.artifactId = (stackNameDupeContext || newStyleSynthesisContext || v3StackSynthesis) ? this.generateStackArtifactId() : this.stackName; @@ -436,6 +443,7 @@ export class Stack extends Construct implements ITaggable { this._versionReportingEnabled = (props.analyticsReporting ?? this.node.tryGetContext(cxapi.ANALYTICS_REPORTING_ENABLED_CONTEXT)) && !this.nestedStackParent; + // TODO: want to add my new synthesizer here but can't, because that's a dependency cycle, right? const synthesizer = (props.synthesizer ?? this.node.tryGetContext(PRIVATE_CONTEXT_DEFAULT_STACK_SYNTHESIZER) ?? (newStyleSynthesisContext ? new DefaultStackSynthesizer() : new LegacyStackSynthesizer())); diff --git a/packages/@aws-cdk/cx-api/lib/features.ts b/packages/@aws-cdk/cx-api/lib/features.ts index 9b63ca0f9c86c..8be36a7426b23 100644 --- a/packages/@aws-cdk/cx-api/lib/features.ts +++ b/packages/@aws-cdk/cx-api/lib/features.ts @@ -47,6 +47,7 @@ export const ENABLE_DIFF_NO_FAIL_CONTEXT = 'aws-cdk:enableDiffNoFail'; /** @deprecated use `ENABLE_DIFF_NO_FAIL_CONTEXT` */ export const ENABLE_DIFF_NO_FAIL = ENABLE_DIFF_NO_FAIL_CONTEXT; export const NEW_STYLE_STACK_SYNTHESIS_CONTEXT = '@aws-cdk/core:newStyleStackSynthesis'; +export const V3_STACK_SYNTHESIS_CONTEXT = '@aws-cdk/core:v3StackSynthesis'; export const STACK_RELATIVE_EXPORTS_CONTEXT = '@aws-cdk/core:stackRelativeExports'; export const DOCKER_IGNORE_SUPPORT = '@aws-cdk/aws-ecr-assets:dockerIgnoreSupport'; export const SECRETS_MANAGER_PARSE_OWNED_SECRET_NAME = '@aws-cdk/aws-secretsmanager:parseOwnedSecretName'; From d73f27f4f39fb0241f1089327207db69479e0093 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Mon, 6 Mar 2023 16:52:08 -0500 Subject: [PATCH 007/120] implement staging stack in synth --- .../core-synthesizer/lib/synthesizer.ts | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts index a35ead7212bb9..e159743049ae5 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts @@ -1,4 +1,4 @@ -import { AssetManifestBuilder, DockerImageAssetLocation, DockerImageAssetSource, FileAssetLocation, FileAssetSource, IBoundStackSynthesizer, IReusableStackSynthesizer, ISynthesisSession, Stack, StackSynthesizer } from '@aws-cdk/core'; +import { App, AssetManifestBuilder, DockerImageAssetLocation, DockerImageAssetSource, Environment, FileAssetLocation, FileAssetSource, IBoundStackSynthesizer, IReusableStackSynthesizer, ISynthesisSession, Stack, StackSynthesizer, Stage } from '@aws-cdk/core'; import { IStagingStack, StagingStack } from './staging-stack'; /** @@ -72,11 +72,21 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta this.lookupRoleArn = props.lookupRoleArn ?? 'INSERT_DEFAULT'; // TODO: logic should be get or synthesize staging stack here, not create each time - this.stagingStack = props.stagingStack ?? new StagingStack(stack, 'StagingStack', { - env: { - account: stack.account, - region: stack.region, - }, + const app = App.of(stack); + if (!app) { + throw new Error(`stack ${stack.stackName} must be part of an App`); + } + this.stagingStack = props.stagingStack ?? this.getCreateStagingStack(app, { + account: stack.account, + region: stack.region, + }); + } + + private getCreateStagingStack(app: Stage, env: Environment) { + // TODO: env could be tokens + const stackName = `StagingStack-${env.account}-${env.region}`; + return app.node.tryFindChild(stackName) as IStagingStack ?? new StagingStack(app, stackName, { + env, }); } From 5153ef5258393f28d8bf45744ead988cd7dfe572 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Mon, 6 Mar 2023 17:33:11 -0500 Subject: [PATCH 008/120] get basic test to succeed --- packages/@aws-cdk/core-synthesizer/jest.config.js | 2 ++ packages/@aws-cdk/core-synthesizer/package.json | 3 +-- .../@aws-cdk/core-synthesizer/test/synthesizer.test.ts | 9 +++------ packages/@aws-cdk/core-synthesizer/tsconfig.json | 3 --- packages/@aws-cdk/core/lib/stack.ts | 9 +-------- packages/@aws-cdk/cx-api/lib/features.ts | 1 - 6 files changed, 7 insertions(+), 20 deletions(-) create mode 100644 packages/@aws-cdk/core-synthesizer/jest.config.js diff --git a/packages/@aws-cdk/core-synthesizer/jest.config.js b/packages/@aws-cdk/core-synthesizer/jest.config.js new file mode 100644 index 0000000000000..3a2fd93a1228a --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/jest.config.js @@ -0,0 +1,2 @@ +const baseConfig = require('@aws-cdk/cdk-build-tools/config/jest.config'); +module.exports = baseConfig; diff --git a/packages/@aws-cdk/core-synthesizer/package.json b/packages/@aws-cdk/core-synthesizer/package.json index bd0e19836ed28..692f1beb3982a 100644 --- a/packages/@aws-cdk/core-synthesizer/package.json +++ b/packages/@aws-cdk/core-synthesizer/package.json @@ -86,8 +86,7 @@ "@aws-cdk/core": "0.0.0", "constructs": "^10.0.0", "@aws-cdk/aws-s3": "0.0.0", - "@aws-cdk/aws-ecr": "0.0.0", - "@aws-cdk/cx-api": "0.0.0" + "@aws-cdk/aws-ecr": "0.0.0" }, "devDependencies": { "@aws-cdk/core": "0.0.0", diff --git a/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts b/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts index 8a924ecf37af4..7d607f350c263 100644 --- a/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts +++ b/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts @@ -1,18 +1,15 @@ import { App, Stack, CfnResource } from '@aws-cdk/core'; -import * as cxapi from '@aws-cdk/cx-api'; +import { UnboundStagingStackSynthesizer } from '../lib'; -describe('new style synthesis', () => { +describe('bootstrap v3', () => { let app: App; let stack: Stack; beforeEach(() => { app = new App({ - context: { - [cxapi.V3_STACK_SYNTHESIS_CONTEXT]: 'true', - }, + defaultStackSynthesizer: new UnboundStagingStackSynthesizer(), }); stack = new Stack(app, 'Stack'); - }); test('stack template is in asset manifest', () => { diff --git a/packages/@aws-cdk/core-synthesizer/tsconfig.json b/packages/@aws-cdk/core-synthesizer/tsconfig.json index 631ad1c94d240..ba8491cbabd52 100644 --- a/packages/@aws-cdk/core-synthesizer/tsconfig.json +++ b/packages/@aws-cdk/core-synthesizer/tsconfig.json @@ -46,9 +46,6 @@ { "path": "../aws-ecr" }, - { - "path": "../cx-api" - }, { "path": "../../../tools/@aws-cdk/cdk-build-tools" }, diff --git a/packages/@aws-cdk/core/lib/stack.ts b/packages/@aws-cdk/core/lib/stack.ts index 2197875b83cdd..072d0b0994a73 100644 --- a/packages/@aws-cdk/core/lib/stack.ts +++ b/packages/@aws-cdk/core/lib/stack.ts @@ -426,14 +426,8 @@ export class Stack extends Construct implements ITaggable { const featureFlags = FeatureFlags.of(this); const stackNameDupeContext = featureFlags.isEnabled(cxapi.ENABLE_STACK_NAME_DUPLICATES_CONTEXT); const newStyleSynthesisContext = featureFlags.isEnabled(cxapi.NEW_STYLE_STACK_SYNTHESIS_CONTEXT); - const v3StackSynthesis = featureFlags.isEnabled(cxapi.V3_STACK_SYNTHESIS_CONTEXT); - // can only have one synthesis style set - if (newStyleSynthesisContext && v3StackSynthesis) { - throw new Error('can only set on kind of stack synthesis'); - } - - this.artifactId = (stackNameDupeContext || newStyleSynthesisContext || v3StackSynthesis) + this.artifactId = (stackNameDupeContext || newStyleSynthesisContext) ? this.generateStackArtifactId() : this.stackName; @@ -443,7 +437,6 @@ export class Stack extends Construct implements ITaggable { this._versionReportingEnabled = (props.analyticsReporting ?? this.node.tryGetContext(cxapi.ANALYTICS_REPORTING_ENABLED_CONTEXT)) && !this.nestedStackParent; - // TODO: want to add my new synthesizer here but can't, because that's a dependency cycle, right? const synthesizer = (props.synthesizer ?? this.node.tryGetContext(PRIVATE_CONTEXT_DEFAULT_STACK_SYNTHESIZER) ?? (newStyleSynthesisContext ? new DefaultStackSynthesizer() : new LegacyStackSynthesizer())); diff --git a/packages/@aws-cdk/cx-api/lib/features.ts b/packages/@aws-cdk/cx-api/lib/features.ts index 8be36a7426b23..9b63ca0f9c86c 100644 --- a/packages/@aws-cdk/cx-api/lib/features.ts +++ b/packages/@aws-cdk/cx-api/lib/features.ts @@ -47,7 +47,6 @@ export const ENABLE_DIFF_NO_FAIL_CONTEXT = 'aws-cdk:enableDiffNoFail'; /** @deprecated use `ENABLE_DIFF_NO_FAIL_CONTEXT` */ export const ENABLE_DIFF_NO_FAIL = ENABLE_DIFF_NO_FAIL_CONTEXT; export const NEW_STYLE_STACK_SYNTHESIS_CONTEXT = '@aws-cdk/core:newStyleStackSynthesis'; -export const V3_STACK_SYNTHESIS_CONTEXT = '@aws-cdk/core:v3StackSynthesis'; export const STACK_RELATIVE_EXPORTS_CONTEXT = '@aws-cdk/core:stackRelativeExports'; export const DOCKER_IGNORE_SUPPORT = '@aws-cdk/aws-ecr-assets:dockerIgnoreSupport'; export const SECRETS_MANAGER_PARSE_OWNED_SECRET_NAME = '@aws-cdk/aws-secretsmanager:parseOwnedSecretName'; From c3628f4ee91d6c6a3708e12e84323b49f2b16dc3 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Tue, 7 Mar 2023 09:08:12 -0500 Subject: [PATCH 009/120] remove extra lines --- packages/@aws-cdk/core/lib/private/synthesis.ts | 1 - packages/@aws-cdk/core/lib/stack.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/@aws-cdk/core/lib/private/synthesis.ts b/packages/@aws-cdk/core/lib/private/synthesis.ts index 4b921df35f31f..8889281f52606 100644 --- a/packages/@aws-cdk/core/lib/private/synthesis.ts +++ b/packages/@aws-cdk/core/lib/private/synthesis.ts @@ -199,7 +199,6 @@ function synthesizeTree(root: IConstruct, builder: cxapi.CloudAssemblyBuilder, v }; if (Stack.isStack(construct)) { - // TODO: I think I need to do something here to keep track of envs -> staging stacks, but not sure construct.synthesizer.synthesize(session); } else if (construct instanceof TreeMetadata) { construct._synthesizeTree(session); diff --git a/packages/@aws-cdk/core/lib/stack.ts b/packages/@aws-cdk/core/lib/stack.ts index 072d0b0994a73..3b235c231fcfa 100644 --- a/packages/@aws-cdk/core/lib/stack.ts +++ b/packages/@aws-cdk/core/lib/stack.ts @@ -426,7 +426,6 @@ export class Stack extends Construct implements ITaggable { const featureFlags = FeatureFlags.of(this); const stackNameDupeContext = featureFlags.isEnabled(cxapi.ENABLE_STACK_NAME_DUPLICATES_CONTEXT); const newStyleSynthesisContext = featureFlags.isEnabled(cxapi.NEW_STYLE_STACK_SYNTHESIS_CONTEXT); - this.artifactId = (stackNameDupeContext || newStyleSynthesisContext) ? this.generateStackArtifactId() : this.stackName; From a3a4c79d8efc70ac35e38e96fe772b139b9d7e25 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Tue, 7 Mar 2023 09:08:29 -0500 Subject: [PATCH 010/120] forgot to commit --- .../core-synthesizer/lib/staging-stack.ts | 18 ++++++++ .../core-synthesizer/lib/synthesizer.ts | 41 +++++++++---------- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts b/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts index f573044f6673a..1f35823705653 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts @@ -12,6 +12,11 @@ export interface IStagingStack extends IConstruct { */ readonly stagingBucket: s3.Bucket; + /** + * // TODO + */ + readonly stagingBucketName: string; + /** * // TODO */ @@ -27,6 +32,12 @@ export interface IStagingStack extends IConstruct { * Staging Stack Properties */ export interface StagingStackProps extends StackProps { + /** + * Explicit name for the staging bucket + * + * @default - DEFAULT + */ + readonly stagingBucketName?: string; } /** @@ -43,10 +54,17 @@ export class StagingStack extends Stack implements IStagingStack { */ public readonly stagingRepos: Record; + /** + * // TODO + */ + public readonly stagingBucketName: string; + constructor(scope: Construct, id: string, props: StagingStackProps = {}) { super(scope, id, props); + this.stagingBucketName = props.stagingBucketName ?? 'default-bucket'; this.stagingBucket = new s3.Bucket(this, 'StagingBucket', { + bucketName: this.stagingBucketName, autoDeleteObjects: true, removalPolicy: RemovalPolicy.DESTROY, }); diff --git a/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts index e159743049ae5..1807c83deda47 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts @@ -11,6 +11,12 @@ export interface StagingStackSynthesizerProps { * @default - default staging stack */ readonly stagingStack?: IStagingStack; + + /** + * Custom lookup role arn + * + * @default - default role + */ readonly lookupRoleArn?: string; } @@ -61,17 +67,21 @@ export class UnboundStagingStackSynthesizer extends StackSynthesizer implements } class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundStackSynthesizer { + /** + * Default lookup role ARN for missing values. + */ + public static readonly DEFAULT_LOOKUP_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-lookup-role-${AWS::AccountId}-${AWS::Region}'; + private stagingStack: IStagingStack; private assetManifest = new AssetManifestBuilder(); private lookupRoleArn: string; - constructor(stack: Stack, props: StagingStackSynthesizerProps) { + constructor(stack: Stack, props: StagingStackSynthesizerProps = {}) { super(); super.bind(stack); - this.lookupRoleArn = props.lookupRoleArn ?? 'INSERT_DEFAULT'; + this.lookupRoleArn = props.lookupRoleArn ?? BoundStagingStackSynthesizer.DEFAULT_LOOKUP_ROLE_ARN; - // TODO: logic should be get or synthesize staging stack here, not create each time const app = App.of(stack); if (!app) { throw new Error(`stack ${stack.stackName} must be part of an App`); @@ -82,9 +92,9 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta }); } - private getCreateStagingStack(app: Stage, env: Environment) { + private getCreateStagingStack(app: Stage, env: Environment): IStagingStack { // TODO: env could be tokens - const stackName = `StagingStack-${env.account}-${env.region}`; + const stackName = `StagingStack${app.stageName}`; return app.node.tryFindChild(stackName) as IStagingStack ?? new StagingStack(app, stackName, { env, }); @@ -101,22 +111,9 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta // eslint-disable-next-line no-console console.log(templateAsset, assetManifestId); - // TODO: finish implementing - // this.emitArtifact(session, { - // assumeRoleExternalId: this.props.deployRoleExternalId, - // assumeRoleArn: this._deployRoleArn, - // cloudFormationExecutionRoleArn: this._cloudFormationExecutionRoleArn, - // stackTemplateAssetObjectUrl: templateAsset.s3ObjectUrlWithPlaceholders, - // requiresBootstrapStackVersion: MIN_BOOTSTRAP_STACK_VERSION, - // bootstrapStackVersionSsmParameter: this.bootstrapStackVersionSsmParameter, - // additionalDependencies: [assetManifestId], - // lookupRole: this.useLookupRoleForStackOperations && this.lookupRoleArn ? { - // arn: this.lookupRoleArn, - // assumeRoleExternalId: this.props.lookupRoleExternalId, - // requiresBootstrapStackVersion: MIN_LOOKUP_ROLE_BOOTSTRAP_STACK_VERSION, - // bootstrapStackVersionSsmParameter: this.bootstrapStackVersionSsmParameter, - // } : undefined, - // }); + this.emitArtifact(session, { + additionalDependencies: [assetManifestId], + }); } /** @@ -124,7 +121,7 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta */ public addFileAsset(asset: FileAssetSource): FileAssetLocation { const location = this.assetManifest.defaultAddFileAsset(this.boundStack, asset, { - bucketName: this.stagingStack.stagingBucket.bucketName, + bucketName: this.stagingStack.stagingBucketName, // TODO: more props }); return this.cloudFormationLocationFromFileAsset(location); From fc52f219b84df3d8c730f417b8ab27ff94f61383 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Tue, 7 Mar 2023 17:19:21 -0500 Subject: [PATCH 011/120] tests for docker asset and file asset --- .../core-synthesizer/lib/staging-stack.ts | 38 +++++++-- .../core-synthesizer/lib/synthesizer.ts | 7 +- .../@aws-cdk/core-synthesizer/package.json | 2 + .../core-synthesizer/test/synthesizer.test.ts | 82 ++++++++++++++++++- 4 files changed, 115 insertions(+), 14 deletions(-) diff --git a/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts b/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts index 1f35823705653..8471324a71d10 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts @@ -25,7 +25,17 @@ export interface IStagingStack extends IConstruct { /** * // TODO */ - getCreateRepository(asset: DockerImageAssetSource): ecr.Repository; + readonly fileAssetPublishingRoleArn: string; + + /** + * // TODO + */ + readonly dockerAssetPublishingRoleArn: string; + + /** + * // TODO + */ + getRepoName(asset: DockerImageAssetSource): string; } /** @@ -44,6 +54,11 @@ export interface StagingStackProps extends StackProps { * A default Staging Stack */ export class StagingStack extends Stack implements IStagingStack { + /** + * Default asset publishing role ARN for file (S3) assets. + */ + public static readonly DEFAULT_FILE_ASSET_PUBLISHING_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}'; + /** * // TODO */ @@ -54,6 +69,9 @@ export class StagingStack extends Stack implements IStagingStack { */ public readonly stagingRepos: Record; + public readonly fileAssetPublishingRoleArn: string; + public readonly dockerAssetPublishingRoleArn: string; + /** * // TODO */ @@ -62,6 +80,9 @@ export class StagingStack extends Stack implements IStagingStack { constructor(scope: Construct, id: string, props: StagingStackProps = {}) { super(scope, id, props); + this.fileAssetPublishingRoleArn = 'file-asset-placeholder-arn'; + this.dockerAssetPublishingRoleArn = 'docker-asset-placeholder-arn'; + this.stagingBucketName = props.stagingBucketName ?? 'default-bucket'; this.stagingBucket = new s3.Bucket(this, 'StagingBucket', { bucketName: this.stagingBucketName, @@ -72,14 +93,17 @@ export class StagingStack extends Stack implements IStagingStack { this.stagingRepos = {}; } - public getCreateRepository(asset: DockerImageAssetSource): ecr.Repository { - // TODO: not the source hash! construct path - if (this.stagingRepos[asset.sourceHash] === undefined) { - this.stagingRepos[asset.sourceHash] = new ecr.Repository(this, `${asset.sourceHash}-repo`, { + public getRepoName(asset: DockerImageAssetSource): string { + // This may work for now. At least one of directoryName or executable is required + const uniqueId = asset.directoryName ?? asset.executable!.toString(); + const repoName = `a${uniqueId}repo`.replace('.', '-'); // TODO: actually sanitize + console.log(repoName); + if (this.stagingRepos[uniqueId] === undefined) { + this.stagingRepos[uniqueId] = new ecr.Repository(this, repoName, { + repositoryName: repoName, // TODO: lifecycle rules }); } - - return this.stagingRepos[asset.sourceHash]; + return repoName; } } diff --git a/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts index 1807c83deda47..fbd32a7b4dc0c 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts @@ -113,6 +113,7 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta this.emitArtifact(session, { additionalDependencies: [assetManifestId], + stackTemplateAssetObjectUrl: templateAsset.s3ObjectUrlWithPlaceholders, }); } @@ -122,7 +123,7 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta public addFileAsset(asset: FileAssetSource): FileAssetLocation { const location = this.assetManifest.defaultAddFileAsset(this.boundStack, asset, { bucketName: this.stagingStack.stagingBucketName, - // TODO: more props + role: { assumeRoleArn: this.stagingStack.fileAssetPublishingRoleArn }, }); return this.cloudFormationLocationFromFileAsset(location); } @@ -131,10 +132,10 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta * // TODO */ public addDockerImageAsset(asset: DockerImageAssetSource): DockerImageAssetLocation { - const repo = this.stagingStack.getCreateRepository(asset); + const repoName = this.stagingStack.getRepoName(asset); const location = this.assetManifest.defaultAddDockerImageAsset(this.boundStack, asset, { - repositoryName: repo.repositoryName, + repositoryName: repoName, // TODO: more props }); return this.cloudFormationLocationFromDockerImageAsset(location); diff --git a/packages/@aws-cdk/core-synthesizer/package.json b/packages/@aws-cdk/core-synthesizer/package.json index 692f1beb3982a..f7cd8045bc519 100644 --- a/packages/@aws-cdk/core-synthesizer/package.json +++ b/packages/@aws-cdk/core-synthesizer/package.json @@ -90,6 +90,8 @@ }, "devDependencies": { "@aws-cdk/core": "0.0.0", + "@aws-cdk/cx-api": "0.0.0", + "@aws-cdk/cloud-assembly-schema": "0.0.0", "constructs": "^10.0.0", "@aws-cdk/aws-s3": "0.0.0", "@aws-cdk/aws-ecr": "0.0.0", diff --git a/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts b/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts index 7d607f350c263..22f38257ffcfb 100644 --- a/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts +++ b/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts @@ -1,6 +1,16 @@ -import { App, Stack, CfnResource } from '@aws-cdk/core'; +import * as fs from 'fs'; +import * as cxschema from '@aws-cdk/cloud-assembly-schema'; +import { App, Stack, CfnResource, FileAssetPackaging } from '@aws-cdk/core'; +import { evaluateCFN } from '@aws-cdk/core/test/evaluate-cfn'; +import * as cxapi from '@aws-cdk/cx-api'; import { UnboundStagingStackSynthesizer } from '../lib'; +const CFN_CONTEXT = { + 'AWS::Region': 'the_region', + 'AWS::AccountId': 'the_account', + 'AWS::URLSuffix': 'domain.aws', +}; + describe('bootstrap v3', () => { let app: App; let stack: Stack; @@ -24,8 +34,72 @@ describe('bootstrap v3', () => { // THEN -- the S3 url is advertised on the stack artifact const stackArtifact = asm.getStackArtifact('Stack'); - // TODO: finish test. - // eslint-disable-next-line no-console - console.log(stackArtifact.stackName); + const templateObjectKey = last(stackArtifact.stackTemplateAssetObjectUrl?.split('/')); + expect(stackArtifact.stackTemplateAssetObjectUrl).toEqual(`s3://default-bucket/${templateObjectKey}`); + + // THEN - the template is in the asset manifest + const manifestArtifact = asm.artifacts.filter(isAssetManifest)[0]; + expect(manifestArtifact).toBeDefined(); + const manifest: cxschema.AssetManifest = JSON.parse(fs.readFileSync(manifestArtifact.file, { encoding: 'utf-8' })); + + const firstFile = (manifest.files ? manifest.files[Object.keys(manifest.files)[0]] : undefined) ?? {}; + + expect(firstFile).toEqual({ + source: { path: 'Stack.template.json', packaging: 'file' }, + destinations: { + 'current_account-current_region': { + bucketName: 'default-bucket', + objectKey: templateObjectKey, + assumeRoleArn: 'file-asset-placeholder-arn', + }, + }, + }); + }); + + test('add file asset', () => { + // WHEN + const location = stack.synthesizer.addFileAsset({ + fileName: __filename, + packaging: FileAssetPackaging.FILE, + sourceHash: 'abcdef', + }); + + // THEN - we have a fixed asset location + expect(evalCFN(location.bucketName)).toEqual('default-bucket'); + expect(evalCFN(location.httpUrl)).toEqual('https://s3.the_region.domain.aws/default-bucket/abcdef.js'); + + // THEN - object key contains source hash somewhere + expect(location.objectKey.indexOf('abcdef')).toBeGreaterThan(-1); + }); + + test('add docker image asset', () => { + // WHEN + const location = stack.synthesizer.addDockerImageAsset({ + directoryName: '.', + sourceHash: 'abcdef', + }); + + // THEN - we have a fixed asset location + const repo = 'a-repo'; + expect(evalCFN(location.repositoryName)).toEqual(repo); + expect(evalCFN(location.imageUri)).toEqual(`the_account.dkr.ecr.the_region.domain.aws/${repo}:abcdef`); }); + + /** + * Evaluate a possibly string-containing value the same way CFN would do + * + * (Be invariant to the specific Fn::Sub or Fn::Join we would output) + */ + function evalCFN(value: any) { + return evaluateCFN(stack.resolve(value), CFN_CONTEXT); + } + }); + +function isAssetManifest(x: cxapi.CloudArtifact): x is cxapi.AssetManifestArtifact { + return x instanceof cxapi.AssetManifestArtifact; +} + +function last(xs?: A[]): A | undefined { + return xs ? xs[xs.length - 1] : undefined; +} From e2928106041c2ddcb5d00bc56d6ba1fee2d4ce51 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 8 Mar 2023 14:17:43 -0500 Subject: [PATCH 012/120] add in iam roles --- .../core-synthesizer/lib/staging-stack.ts | 65 +++++++++++++++---- .../@aws-cdk/core-synthesizer/package.json | 3 +- .../core-synthesizer/test/synthesizer.test.ts | 14 +++- 3 files changed, 68 insertions(+), 14 deletions(-) diff --git a/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts b/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts index 8471324a71d10..d6fbcdf5097e0 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts @@ -2,38 +2,41 @@ import * as ecr from '@aws-cdk/aws-ecr'; import * as s3 from '@aws-cdk/aws-s3'; import { DockerImageAssetSource, RemovalPolicy, Stack, StackProps } from '@aws-cdk/core'; import { Construct, IConstruct } from 'constructs'; +import * as iam from '@aws-cdk/aws-iam'; /** * Information on how a Staging Stack should look. */ export interface IStagingStack extends IConstruct { /** - * // TODO + * The app-scoped, environment-keyed bucket created in this staging stack. */ readonly stagingBucket: s3.Bucket; /** - * // TODO + * The well-known name of the app-scpoed, environment-keyed bucket created in this staging stack. */ readonly stagingBucketName: string; /** - * // TODO + * The app-scoped, environment-keyed repositories created in this staging stack. + * A repository is created per image asset family. */ readonly stagingRepos: Record; /** - * // TODO + * The well-known name of the IAM role assumed for publishing to s3. */ readonly fileAssetPublishingRoleArn: string; /** - * // TODO + * The well-known name of the IAM role assumed for publishing to ecr. */ readonly dockerAssetPublishingRoleArn: string; /** - * // TODO + * Returns the well-known name of the app-scoped, environment-keyed repository associated + * with the given asset. */ getRepoName(asset: DockerImageAssetSource): string; } @@ -45,9 +48,23 @@ export interface StagingStackProps extends StackProps { /** * Explicit name for the staging bucket * - * @default - DEFAULT + * @default - a well-known name unique to this app/env. */ readonly stagingBucketName?: string; + + /** + * Pass in an existing role to be used as the file publishing role. + * + * @default - a well-known name unique to this app/env. + */ + readonly fileAssetPublishingRoleArn?: string; + + /** + * Pass in an existing role to be used as the image publishing role. + * + * @default - a well-known name unique to this app/env. + */ + readonly dockerAssetPublishingRoleArn?: string; } /** @@ -60,28 +77,52 @@ export class StagingStack extends Stack implements IStagingStack { public static readonly DEFAULT_FILE_ASSET_PUBLISHING_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}'; /** - * // TODO + * Default asset publishing role ARN for docker (ECR) assets. + */ + public static readonly DEFAULT_DOCKER_ASSET_PUBISHING_ROLE_ARN = 'cdk-${Qualifier}-asset-publishing-role-${AWS::AccountId}-${AWS::Region}'; + + /** + * The app-scoped, evironment-keyed staging bucket. */ public readonly stagingBucket: s3.Bucket; /** - * // TODO + * The app-scoped, environment-keyed ecr repositories associated with this app. */ public readonly stagingRepos: Record; + /** + * The well-known arn of the file asset publishing role. + */ public readonly fileAssetPublishingRoleArn: string; + + /** + * The well-known arn of the docker asset publishing role. + */ public readonly dockerAssetPublishingRoleArn: string; /** - * // TODO + * The well known name of the staging bucket */ public readonly stagingBucketName: string; constructor(scope: Construct, id: string, props: StagingStackProps = {}) { super(scope, id, props); - this.fileAssetPublishingRoleArn = 'file-asset-placeholder-arn'; - this.dockerAssetPublishingRoleArn = 'docker-asset-placeholder-arn'; + this.fileAssetPublishingRoleArn = props.fileAssetPublishingRoleArn ?? StagingStack.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_ARN; + if (!props.fileAssetPublishingRoleArn) { + new iam.Role(this, 'File-Asset-Publishing-Role', { + roleName: StagingStack.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_ARN.split('/')[1], + assumedBy: new iam.ServicePrincipal('sts.amazonaws.com'), + }); + } + this.dockerAssetPublishingRoleArn = props.dockerAssetPublishingRoleArn ?? StagingStack.DEFAULT_DOCKER_ASSET_PUBISHING_ROLE_ARN; + if (!props.dockerAssetPublishingRoleArn) { + new iam.Role(this, 'Docker-Asset-Publishing-Role', { + roleName: StagingStack.DEFAULT_DOCKER_ASSET_PUBISHING_ROLE_ARN.split('/')[1], + assumedBy: new iam.ServicePrincipal('sts.amazonaws.com'), + }); + } this.stagingBucketName = props.stagingBucketName ?? 'default-bucket'; this.stagingBucket = new s3.Bucket(this, 'StagingBucket', { diff --git a/packages/@aws-cdk/core-synthesizer/package.json b/packages/@aws-cdk/core-synthesizer/package.json index f7cd8045bc519..03b8e2cd874f6 100644 --- a/packages/@aws-cdk/core-synthesizer/package.json +++ b/packages/@aws-cdk/core-synthesizer/package.json @@ -86,7 +86,8 @@ "@aws-cdk/core": "0.0.0", "constructs": "^10.0.0", "@aws-cdk/aws-s3": "0.0.0", - "@aws-cdk/aws-ecr": "0.0.0" + "@aws-cdk/aws-ecr": "0.0.0", + "@aws-cdk/aws-iam": "0.0.0" }, "devDependencies": { "@aws-cdk/core": "0.0.0", diff --git a/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts b/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts index 22f38257ffcfb..ac2224a6726fc 100644 --- a/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts +++ b/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts @@ -50,7 +50,7 @@ describe('bootstrap v3', () => { 'current_account-current_region': { bucketName: 'default-bucket', objectKey: templateObjectKey, - assumeRoleArn: 'file-asset-placeholder-arn', + assumeRoleArn: 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}', }, }, }); @@ -85,6 +85,18 @@ describe('bootstrap v3', () => { expect(evalCFN(location.imageUri)).toEqual(`the_account.dkr.ecr.the_region.domain.aws/${repo}:abcdef`); }); + test('file asset depends on staging stack', () => { + // WHEN + const location = stack.synthesizer.addFileAsset({ + fileName: __filename, + packaging: FileAssetPackaging.FILE, + sourceHash: 'abcdef', + }); + + // THEN - we have a fixed asset location + console.log(stack.dependencies); + }); + /** * Evaluate a possibly string-containing value the same way CFN would do * From 3fd54e1d14f613cb049ccbea5387bcd2ba14d9dd Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 8 Mar 2023 14:30:52 -0500 Subject: [PATCH 013/120] handle assets --- .../core-synthesizer/lib/staging-stack.ts | 16 +++++++++------- .../@aws-cdk/core-synthesizer/lib/synthesizer.ts | 3 ++- .../core-synthesizer/test/synthesizer.test.ts | 3 ++- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts b/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts index d6fbcdf5097e0..8e741825001ac 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts @@ -1,6 +1,6 @@ import * as ecr from '@aws-cdk/aws-ecr'; import * as s3 from '@aws-cdk/aws-s3'; -import { DockerImageAssetSource, RemovalPolicy, Stack, StackProps } from '@aws-cdk/core'; +import { App, DockerImageAssetSource, RemovalPolicy, Stack, StackProps } from '@aws-cdk/core'; import { Construct, IConstruct } from 'constructs'; import * as iam from '@aws-cdk/aws-iam'; @@ -124,7 +124,7 @@ export class StagingStack extends Stack implements IStagingStack { }); } - this.stagingBucketName = props.stagingBucketName ?? 'default-bucket'; + this.stagingBucketName = props.stagingBucketName ?? 'default-bucket'; // 'cdk-${AWS::AccountId}-${AWS::Region}'+`${App.of(this)?.stageName}`; this.stagingBucket = new s3.Bucket(this, 'StagingBucket', { bucketName: this.stagingBucketName, autoDeleteObjects: true, @@ -135,12 +135,14 @@ export class StagingStack extends Stack implements IStagingStack { } public getRepoName(asset: DockerImageAssetSource): string { - // This may work for now. At least one of directoryName or executable is required - const uniqueId = asset.directoryName ?? asset.executable!.toString(); - const repoName = `a${uniqueId}repo`.replace('.', '-'); // TODO: actually sanitize + if (!asset.uniqueId) { + throw new Error('uniqueId is required when using bootstrap v3'); + } + + const repoName = `${asset.uniqueId}repo`.replace('.', '-'); // TODO: actually sanitize console.log(repoName); - if (this.stagingRepos[uniqueId] === undefined) { - this.stagingRepos[uniqueId] = new ecr.Repository(this, repoName, { + if (this.stagingRepos[asset.uniqueId] === undefined) { + this.stagingRepos[asset.uniqueId] = new ecr.Repository(this, repoName, { repositoryName: repoName, // TODO: lifecycle rules }); diff --git a/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts index fbd32a7b4dc0c..ff918464c0884 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts @@ -112,7 +112,8 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta console.log(templateAsset, assetManifestId); this.emitArtifact(session, { - additionalDependencies: [assetManifestId], + // TODO: uhh is this right? + additionalDependencies: [assetManifestId/*, this.stagingStack.node.id*/], stackTemplateAssetObjectUrl: templateAsset.s3ObjectUrlWithPlaceholders, }); } diff --git a/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts b/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts index ac2224a6726fc..0a11c30ec8f7f 100644 --- a/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts +++ b/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts @@ -77,10 +77,11 @@ describe('bootstrap v3', () => { const location = stack.synthesizer.addDockerImageAsset({ directoryName: '.', sourceHash: 'abcdef', + uniqueId: 'abcdef', }); // THEN - we have a fixed asset location - const repo = 'a-repo'; + const repo = 'abcdefrepo'; expect(evalCFN(location.repositoryName)).toEqual(repo); expect(evalCFN(location.imageUri)).toEqual(`the_account.dkr.ecr.the_region.domain.aws/${repo}:abcdef`); }); From 12283bdc32a23b80a25539e2c8593ff4df9f14d9 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 8 Mar 2023 15:28:17 -0500 Subject: [PATCH 014/120] assets --- packages/@aws-cdk/core/lib/assets.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/@aws-cdk/core/lib/assets.ts b/packages/@aws-cdk/core/lib/assets.ts index 10e1a61e7e5d0..e81db54052b5f 100644 --- a/packages/@aws-cdk/core/lib/assets.ts +++ b/packages/@aws-cdk/core/lib/assets.ts @@ -242,6 +242,13 @@ export interface DockerImageAssetSource { */ readonly dockerOutputs?: string[]; + /** + * Unique identifier of the docker image asset and its potential revisions. + * Required if using Bootstrap v3. + * + * @default - no unique id + */ + readonly uniqueId?: string; } /** From 86814787a30a02127b068ef13d10f63c85882310 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 8 Mar 2023 15:29:08 -0500 Subject: [PATCH 015/120] small things --- packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts | 4 ++-- packages/@aws-cdk/core-synthesizer/package.json | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts b/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts index 8e741825001ac..5e400ac5bbcb5 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts @@ -1,8 +1,8 @@ import * as ecr from '@aws-cdk/aws-ecr'; +import * as iam from '@aws-cdk/aws-iam'; import * as s3 from '@aws-cdk/aws-s3'; -import { App, DockerImageAssetSource, RemovalPolicy, Stack, StackProps } from '@aws-cdk/core'; +import { DockerImageAssetSource, RemovalPolicy, Stack, StackProps } from '@aws-cdk/core'; import { Construct, IConstruct } from 'constructs'; -import * as iam from '@aws-cdk/aws-iam'; /** * Information on how a Staging Stack should look. diff --git a/packages/@aws-cdk/core-synthesizer/package.json b/packages/@aws-cdk/core-synthesizer/package.json index 03b8e2cd874f6..a63955c9d622f 100644 --- a/packages/@aws-cdk/core-synthesizer/package.json +++ b/packages/@aws-cdk/core-synthesizer/package.json @@ -96,6 +96,7 @@ "constructs": "^10.0.0", "@aws-cdk/aws-s3": "0.0.0", "@aws-cdk/aws-ecr": "0.0.0", + "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/pkglint": "0.0.0", "jsii": "v4.9-next" From ae2781ebf7a2bd03a099e4927a4244ed06e592e3 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 8 Mar 2023 15:37:49 -0500 Subject: [PATCH 016/120] 2 tests --- .../core-synthesizer/test/synthesizer.test.ts | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts b/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts index 0a11c30ec8f7f..f22fb16e93104 100644 --- a/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts +++ b/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts @@ -86,6 +86,44 @@ describe('bootstrap v3', () => { expect(evalCFN(location.imageUri)).toEqual(`the_account.dkr.ecr.the_region.domain.aws/${repo}:abcdef`); }); + test('separate docker image assets have separate repos', () => { + // WHEN + const location1 = stack.synthesizer.addDockerImageAsset({ + directoryName: '.', + sourceHash: 'abcdef', + uniqueId: 'abcdef', + }); + + const location2 = stack.synthesizer.addDockerImageAsset({ + directoryName: './hello', + sourceHash: 'abcdefg', + uniqueId: 'abcdefg', + }); + + // THEN - images have different asset locations + expect(evalCFN(location1.repositoryName)).not.toEqual(evalCFN(location2.repositoryName)); + }); + + test('docker image assets with same unique id have same repos', () => { + // WHEN + const location1 = stack.synthesizer.addDockerImageAsset({ + directoryName: '.', + sourceHash: 'abcdef', + uniqueId: 'abcdef', + }); + + const location2 = stack.synthesizer.addDockerImageAsset({ + directoryName: './hello', + sourceHash: 'abcdefg', + uniqueId: 'abcdef', + }); + + // THEN - images share same ecr repo + const repo = 'abcdefrepo'; + expect(evalCFN(location1.repositoryName)).toEqual(repo); + expect(evalCFN(location1.repositoryName)).toEqual(evalCFN(location2.repositoryName)); + }); + test('file asset depends on staging stack', () => { // WHEN const location = stack.synthesizer.addFileAsset({ From 143df73dd7c5cb56afa42cf3eaa6497756f6e36e Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 8 Mar 2023 15:44:35 -0500 Subject: [PATCH 017/120] add no tokens --- .../core-synthesizer/lib/synthesizer.ts | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts index ff918464c0884..ad6adc820081f 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts @@ -1,4 +1,5 @@ -import { App, AssetManifestBuilder, DockerImageAssetLocation, DockerImageAssetSource, Environment, FileAssetLocation, FileAssetSource, IBoundStackSynthesizer, IReusableStackSynthesizer, ISynthesisSession, Stack, StackSynthesizer, Stage } from '@aws-cdk/core'; +import { App, AssetManifestBuilder, DockerImageAssetLocation, DockerImageAssetSource, Environment, FileAssetLocation, FileAssetSource, IBoundStackSynthesizer, IReusableStackSynthesizer, ISynthesisSession, Stack, StackSynthesizer, Stage, Token } from '@aws-cdk/core'; +import * as cxapi from '@aws-cdk/cx-api'; import { IStagingStack, StagingStack } from './staging-stack'; /** @@ -27,7 +28,23 @@ export class UnboundStagingStackSynthesizer extends StackSynthesizer implements constructor(private readonly props: StagingStackSynthesizerProps = {}) { super(); - // TODO: no tokens + for (const key in props) { + if (props.hasOwnProperty(key)) { + validateNoToken(key as keyof StagingStackSynthesizerProps); + } + } + + function validateNoToken(key: A) { + const prop = props[key]; + if (typeof prop === 'string' && Token.isUnresolved(prop)) { + throw new Error(`DefaultStackSynthesizer property '${key}' cannot contain tokens; only the following placeholder strings are allowed: ` + [ + '${Qualifier}', + cxapi.EnvironmentPlaceholders.CURRENT_REGION, + cxapi.EnvironmentPlaceholders.CURRENT_ACCOUNT, + cxapi.EnvironmentPlaceholders.CURRENT_PARTITION, + ].join(', ')); + } + } } /** From d5a50c3cf7e55e4b8aa29524279e4af99050304a Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 9 Mar 2023 08:31:17 -0500 Subject: [PATCH 018/120] better error messaging --- .../core-synthesizer/lib/staging-stack.ts | 14 +++++++------- .../@aws-cdk/core-synthesizer/lib/synthesizer.ts | 9 +++++---- .../core-synthesizer/test/synthesizer.test.ts | 15 +++++++++++---- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts b/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts index 5e400ac5bbcb5..2357ba5ceee69 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts @@ -70,7 +70,7 @@ export interface StagingStackProps extends StackProps { /** * A default Staging Stack */ -export class StagingStack extends Stack implements IStagingStack { +export class DefaultStagingStack extends Stack implements IStagingStack { /** * Default asset publishing role ARN for file (S3) assets. */ @@ -109,22 +109,22 @@ export class StagingStack extends Stack implements IStagingStack { constructor(scope: Construct, id: string, props: StagingStackProps = {}) { super(scope, id, props); - this.fileAssetPublishingRoleArn = props.fileAssetPublishingRoleArn ?? StagingStack.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_ARN; + this.fileAssetPublishingRoleArn = props.fileAssetPublishingRoleArn ?? DefaultStagingStack.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_ARN; if (!props.fileAssetPublishingRoleArn) { new iam.Role(this, 'File-Asset-Publishing-Role', { - roleName: StagingStack.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_ARN.split('/')[1], + roleName: DefaultStagingStack.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_ARN.split('/')[1], assumedBy: new iam.ServicePrincipal('sts.amazonaws.com'), }); } - this.dockerAssetPublishingRoleArn = props.dockerAssetPublishingRoleArn ?? StagingStack.DEFAULT_DOCKER_ASSET_PUBISHING_ROLE_ARN; + this.dockerAssetPublishingRoleArn = props.dockerAssetPublishingRoleArn ?? DefaultStagingStack.DEFAULT_DOCKER_ASSET_PUBISHING_ROLE_ARN; if (!props.dockerAssetPublishingRoleArn) { new iam.Role(this, 'Docker-Asset-Publishing-Role', { - roleName: StagingStack.DEFAULT_DOCKER_ASSET_PUBISHING_ROLE_ARN.split('/')[1], + roleName: DefaultStagingStack.DEFAULT_DOCKER_ASSET_PUBISHING_ROLE_ARN.split('/')[1], assumedBy: new iam.ServicePrincipal('sts.amazonaws.com'), }); } - this.stagingBucketName = props.stagingBucketName ?? 'default-bucket'; // 'cdk-${AWS::AccountId}-${AWS::Region}'+`${App.of(this)?.stageName}`; + this.stagingBucketName = props.stagingBucketName ?? 'default-bucket'; //`cdk-${this.account}-${this.region}`; this.stagingBucket = new s3.Bucket(this, 'StagingBucket', { bucketName: this.stagingBucketName, autoDeleteObjects: true, @@ -136,7 +136,7 @@ export class StagingStack extends Stack implements IStagingStack { public getRepoName(asset: DockerImageAssetSource): string { if (!asset.uniqueId) { - throw new Error('uniqueId is required when using bootstrap v3'); + throw new Error('Assets synthesized with AppScopedStagingSynthesizer must include a \'uniqueId\' in the asset source definition.'); } const repoName = `${asset.uniqueId}repo`.replace('.', '-'); // TODO: actually sanitize diff --git a/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts index ad6adc820081f..72dd4c7741a48 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts @@ -1,6 +1,6 @@ import { App, AssetManifestBuilder, DockerImageAssetLocation, DockerImageAssetSource, Environment, FileAssetLocation, FileAssetSource, IBoundStackSynthesizer, IReusableStackSynthesizer, ISynthesisSession, Stack, StackSynthesizer, Stage, Token } from '@aws-cdk/core'; import * as cxapi from '@aws-cdk/cx-api'; -import { IStagingStack, StagingStack } from './staging-stack'; +import { IStagingStack, DefaultStagingStack } from './staging-stack'; /** * New stack synthesizer properties @@ -24,7 +24,7 @@ export interface StagingStackSynthesizerProps { /** * New Stack Synthesizer */ -export class UnboundStagingStackSynthesizer extends StackSynthesizer implements IReusableStackSynthesizer, IBoundStackSynthesizer { +export class AppScopedStagingSynthesizer extends StackSynthesizer implements IReusableStackSynthesizer { constructor(private readonly props: StagingStackSynthesizerProps = {}) { super(); @@ -37,7 +37,7 @@ export class UnboundStagingStackSynthesizer extends StackSynthesizer implements function validateNoToken(key: A) { const prop = props[key]; if (typeof prop === 'string' && Token.isUnresolved(prop)) { - throw new Error(`DefaultStackSynthesizer property '${key}' cannot contain tokens; only the following placeholder strings are allowed: ` + [ + throw new Error(`AppScopedStagingSynthesizer property '${key}' cannot contain tokens; only the following placeholder strings are allowed: ` + [ '${Qualifier}', cxapi.EnvironmentPlaceholders.CURRENT_REGION, cxapi.EnvironmentPlaceholders.CURRENT_ACCOUNT, @@ -112,8 +112,9 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta private getCreateStagingStack(app: Stage, env: Environment): IStagingStack { // TODO: env could be tokens const stackName = `StagingStack${app.stageName}`; - return app.node.tryFindChild(stackName) as IStagingStack ?? new StagingStack(app, stackName, { + return app.node.tryFindChild(stackName) as IStagingStack ?? new DefaultStagingStack(app, stackName, { env, + stackName, }); } diff --git a/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts b/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts index f22fb16e93104..092759999a7cf 100644 --- a/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts +++ b/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts @@ -3,7 +3,7 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import { App, Stack, CfnResource, FileAssetPackaging } from '@aws-cdk/core'; import { evaluateCFN } from '@aws-cdk/core/test/evaluate-cfn'; import * as cxapi from '@aws-cdk/cx-api'; -import { UnboundStagingStackSynthesizer } from '../lib'; +import { AppScopedStagingSynthesizer } from '../lib'; const CFN_CONTEXT = { 'AWS::Region': 'the_region', @@ -11,13 +11,13 @@ const CFN_CONTEXT = { 'AWS::URLSuffix': 'domain.aws', }; -describe('bootstrap v3', () => { +describe(AppScopedStagingSynthesizer, () => { let app: App; let stack: Stack; beforeEach(() => { app = new App({ - defaultStackSynthesizer: new UnboundStagingStackSynthesizer(), + defaultStackSynthesizer: new AppScopedStagingSynthesizer(), }); stack = new Stack(app, 'Stack'); }); @@ -86,6 +86,13 @@ describe('bootstrap v3', () => { expect(evalCFN(location.imageUri)).toEqual(`the_account.dkr.ecr.the_region.domain.aws/${repo}:abcdef`); }); + test('throws with docker image asset without uniqueId', () => { + expect(() => stack.synthesizer.addDockerImageAsset({ + directoryName: '.', + sourceHash: 'abcdef', + })).toThrowError('Assets synthesized with AppScopedStagingSynthesizer must include a \'uniqueId\' in the asset source definition.'); + }); + test('separate docker image assets have separate repos', () => { // WHEN const location1 = stack.synthesizer.addDockerImageAsset({ @@ -133,7 +140,7 @@ describe('bootstrap v3', () => { }); // THEN - we have a fixed asset location - console.log(stack.dependencies); + console.log(location.bucketName); }); /** From d088ec884ed18fb042b09311cda5191696ed3e40 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 9 Mar 2023 16:33:59 -0500 Subject: [PATCH 019/120] more work --- .../core-synthesizer/lib/staging-stack.ts | 146 +++++++++++------- .../core-synthesizer/lib/synthesizer.ts | 8 +- .../core-synthesizer/test/synthesizer.test.ts | 18 ++- 3 files changed, 106 insertions(+), 66 deletions(-) diff --git a/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts b/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts index 2357ba5ceee69..59ec1d8bba8c3 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts @@ -1,9 +1,18 @@ import * as ecr from '@aws-cdk/aws-ecr'; import * as iam from '@aws-cdk/aws-iam'; import * as s3 from '@aws-cdk/aws-s3'; -import { DockerImageAssetSource, RemovalPolicy, Stack, StackProps } from '@aws-cdk/core'; +import { Arn, ArnFormat, Aws, DockerImageAssetSource, FileAssetSource, RemovalPolicy, Stack, StackProps } from '@aws-cdk/core'; import { Construct, IConstruct } from 'constructs'; +export interface FileAssetInfo { + readonly bucketName: string; + readonly assumeRoleArn: string; +} + +export interface DockerAssetInfo { + readonly repoName: string; + readonly assumeRoleArn: string; +} /** * Information on how a Staging Stack should look. */ @@ -11,12 +20,7 @@ export interface IStagingStack extends IConstruct { /** * The app-scoped, environment-keyed bucket created in this staging stack. */ - readonly stagingBucket: s3.Bucket; - - /** - * The well-known name of the app-scpoed, environment-keyed bucket created in this staging stack. - */ - readonly stagingBucketName: string; + readonly stagingBucket?: s3.Bucket; /** * The app-scoped, environment-keyed repositories created in this staging stack. @@ -25,20 +29,14 @@ export interface IStagingStack extends IConstruct { readonly stagingRepos: Record; /** - * The well-known name of the IAM role assumed for publishing to s3. + * // TODO */ - readonly fileAssetPublishingRoleArn: string; + addFile(asset: FileAssetSource): FileAssetInfo; /** - * The well-known name of the IAM role assumed for publishing to ecr. + * // TODO */ - readonly dockerAssetPublishingRoleArn: string; - - /** - * Returns the well-known name of the app-scoped, environment-keyed repository associated - * with the given asset. - */ - getRepoName(asset: DockerImageAssetSource): string; + addDockerImage(asset: DockerImageAssetSource): DockerAssetInfo; } /** @@ -57,14 +55,14 @@ export interface StagingStackProps extends StackProps { * * @default - a well-known name unique to this app/env. */ - readonly fileAssetPublishingRoleArn?: string; + readonly fileAssetPublishingRoleName?: string; /** * Pass in an existing role to be used as the image publishing role. * * @default - a well-known name unique to this app/env. */ - readonly dockerAssetPublishingRoleArn?: string; + readonly dockerAssetPublishingRoleName?: string; } /** @@ -72,75 +70,95 @@ export interface StagingStackProps extends StackProps { */ export class DefaultStagingStack extends Stack implements IStagingStack { /** - * Default asset publishing role ARN for file (S3) assets. + * Default asset publishing role name for file (S3) assets. */ - public static readonly DEFAULT_FILE_ASSET_PUBLISHING_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}'; + private static readonly DEFAULT_FILE_ASSET_PUBLISHING_ROLE_NAME = 'cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}'; /** - * Default asset publishing role ARN for docker (ECR) assets. + * Default asset publishing role name for docker (ECR) assets. */ - public static readonly DEFAULT_DOCKER_ASSET_PUBISHING_ROLE_ARN = 'cdk-${Qualifier}-asset-publishing-role-${AWS::AccountId}-${AWS::Region}'; + private static readonly DEFAULT_DOCKER_ASSET_PUBISHING_ROLE_NAME = 'cdk-${Qualifier}-asset-publishing-role-${AWS::AccountId}-${AWS::Region}'; /** * The app-scoped, evironment-keyed staging bucket. */ - public readonly stagingBucket: s3.Bucket; + public readonly stagingBucket?: s3.Bucket; /** * The app-scoped, environment-keyed ecr repositories associated with this app. */ public readonly stagingRepos: Record; - /** - * The well-known arn of the file asset publishing role. - */ - public readonly fileAssetPublishingRoleArn: string; + private readonly stagingBucketName?: string; + private fileAssetPublishingRoleName: string; + private dockerAssetPublishingRoleName: string; + + constructor(scope: Construct, id: string, props: StagingStackProps = {}) { + super(scope, id, props); + + this.stagingBucketName = props.stagingBucketName; + this.fileAssetPublishingRoleName = props.fileAssetPublishingRoleName ?? DefaultStagingStack.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_NAME; + this.dockerAssetPublishingRoleName = props.dockerAssetPublishingRoleName ?? DefaultStagingStack.DEFAULT_DOCKER_ASSET_PUBISHING_ROLE_NAME; + this.stagingRepos = {}; + } /** - * The well-known arn of the docker asset publishing role. + * Returns the well-known name of the file publishing role */ - public readonly dockerAssetPublishingRoleArn: string; + private getCreateFilePublishingRole() { + this.node.tryFindChild(this.fileAssetPublishingRoleName) as iam.Role ?? new iam.Role(this, this.fileAssetPublishingRoleName, { + roleName: DefaultStagingStack.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_NAME, + assumedBy: new iam.ServicePrincipal('sts.amazonaws.com'), // TODO actually create correct role + }); + return Arn.format({ + partition: this.partition ?? Aws.PARTITION, + account: this.account ?? Aws.ACCOUNT_ID, + region: this.region ?? Aws.REGION, + service: 'iam', + resource: 'role', + resourceName: DefaultStagingStack.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_NAME, + arnFormat: ArnFormat.COLON_RESOURCE_NAME, + }); + } /** - * The well known name of the staging bucket + * Returns the well-known name of the image publishing role */ - public readonly stagingBucketName: string; - - constructor(scope: Construct, id: string, props: StagingStackProps = {}) { - super(scope, id, props); - - this.fileAssetPublishingRoleArn = props.fileAssetPublishingRoleArn ?? DefaultStagingStack.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_ARN; - if (!props.fileAssetPublishingRoleArn) { - new iam.Role(this, 'File-Asset-Publishing-Role', { - roleName: DefaultStagingStack.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_ARN.split('/')[1], - assumedBy: new iam.ServicePrincipal('sts.amazonaws.com'), - }); - } - this.dockerAssetPublishingRoleArn = props.dockerAssetPublishingRoleArn ?? DefaultStagingStack.DEFAULT_DOCKER_ASSET_PUBISHING_ROLE_ARN; - if (!props.dockerAssetPublishingRoleArn) { - new iam.Role(this, 'Docker-Asset-Publishing-Role', { - roleName: DefaultStagingStack.DEFAULT_DOCKER_ASSET_PUBISHING_ROLE_ARN.split('/')[1], - assumedBy: new iam.ServicePrincipal('sts.amazonaws.com'), - }); - } + private getCreateImagePublishingRole() { + this.node.tryFindChild(this.dockerAssetPublishingRoleName) as iam.Role ?? new iam.Role(this, this.dockerAssetPublishingRoleName, { + roleName: DefaultStagingStack.DEFAULT_DOCKER_ASSET_PUBISHING_ROLE_NAME, + assumedBy: new iam.ServicePrincipal('sts.amazonaws.com'), // TODO actually create correct role + }); + return Arn.format({ + partition: this.partition ?? Aws.PARTITION, + account: this.account ?? Aws.ACCOUNT_ID, + region: this.region ?? Aws.REGION, + service: 'iam', + resource: 'role', + resourceName: DefaultStagingStack.DEFAULT_DOCKER_ASSET_PUBISHING_ROLE_NAME, + arnFormat: ArnFormat.COLON_RESOURCE_NAME, + }); + } - this.stagingBucketName = props.stagingBucketName ?? 'default-bucket'; //`cdk-${this.account}-${this.region}`; - this.stagingBucket = new s3.Bucket(this, 'StagingBucket', { - bucketName: this.stagingBucketName, + private getCreateBucket(): string { + const stagingBucketName = this.stagingBucketName ?? 'default-bucket'; //`cdk-${this.account}-${this.region}`; + this.node.tryFindChild(stagingBucketName) as s3.Bucket ?? new s3.Bucket(this, stagingBucketName, { + bucketName: stagingBucketName, autoDeleteObjects: true, removalPolicy: RemovalPolicy.DESTROY, }); - - this.stagingRepos = {}; + return stagingBucketName; } - public getRepoName(asset: DockerImageAssetSource): string { + /** + * Returns the well-known name of the repo + */ + private getCreateRepo(asset: DockerImageAssetSource): string { if (!asset.uniqueId) { throw new Error('Assets synthesized with AppScopedStagingSynthesizer must include a \'uniqueId\' in the asset source definition.'); } const repoName = `${asset.uniqueId}repo`.replace('.', '-'); // TODO: actually sanitize - console.log(repoName); if (this.stagingRepos[asset.uniqueId] === undefined) { this.stagingRepos[asset.uniqueId] = new ecr.Repository(this, repoName, { repositoryName: repoName, @@ -149,4 +167,18 @@ export class DefaultStagingStack extends Stack implements IStagingStack { } return repoName; } + + public addFile(_asset: FileAssetSource): FileAssetInfo { + return { + bucketName: this.getCreateBucket(), + assumeRoleArn: this.getCreateFilePublishingRole(), + }; + } + + public addDockerImage(asset: DockerImageAssetSource): DockerAssetInfo { + return { + repoName: this.getCreateRepo(asset), + assumeRoleArn: this.getCreateImagePublishingRole(), + }; + } } diff --git a/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts index 72dd4c7741a48..afc502699dd03 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts @@ -140,9 +140,10 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta * // TODO */ public addFileAsset(asset: FileAssetSource): FileAssetLocation { + const { bucketName, assumeRoleArn } = this.stagingStack.addFile(asset); const location = this.assetManifest.defaultAddFileAsset(this.boundStack, asset, { - bucketName: this.stagingStack.stagingBucketName, - role: { assumeRoleArn: this.stagingStack.fileAssetPublishingRoleArn }, + bucketName, + role: { assumeRoleArn }, }); return this.cloudFormationLocationFromFileAsset(location); } @@ -151,10 +152,11 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta * // TODO */ public addDockerImageAsset(asset: DockerImageAssetSource): DockerImageAssetLocation { - const repoName = this.stagingStack.getRepoName(asset); + const { repoName, assumeRoleArn } = this.stagingStack.addDockerImage(asset); const location = this.assetManifest.defaultAddDockerImageAsset(this.boundStack, asset, { repositoryName: repoName, + role: { assumeRoleArn }, // TODO: more props }); return this.cloudFormationLocationFromDockerImageAsset(location); diff --git a/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts b/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts index 092759999a7cf..7314f770f6730 100644 --- a/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts +++ b/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts @@ -1,6 +1,6 @@ import * as fs from 'fs'; import * as cxschema from '@aws-cdk/cloud-assembly-schema'; -import { App, Stack, CfnResource, FileAssetPackaging } from '@aws-cdk/core'; +import { App, Stack, CfnResource, FileAssetPackaging, Aws } from '@aws-cdk/core'; import { evaluateCFN } from '@aws-cdk/core/test/evaluate-cfn'; import * as cxapi from '@aws-cdk/cx-api'; import { AppScopedStagingSynthesizer } from '../lib'; @@ -19,7 +19,12 @@ describe(AppScopedStagingSynthesizer, () => { app = new App({ defaultStackSynthesizer: new AppScopedStagingSynthesizer(), }); - stack = new Stack(app, 'Stack'); + stack = new Stack(app, 'Stack', { + env: { + account: '000000000000', + region: 'us-east-1', + }, + }); }); test('stack template is in asset manifest', () => { @@ -47,10 +52,11 @@ describe(AppScopedStagingSynthesizer, () => { expect(firstFile).toEqual({ source: { path: 'Stack.template.json', packaging: 'file' }, destinations: { - 'current_account-current_region': { + '000000000000-us-east-1': { bucketName: 'default-bucket', objectKey: templateObjectKey, - assumeRoleArn: 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}', + region: 'us-east-1', + assumeRoleArn: 'arn:' + Aws.PARTITION + ':iam:us-east-1:000000000000:role:cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}', }, }, }); @@ -66,7 +72,7 @@ describe(AppScopedStagingSynthesizer, () => { // THEN - we have a fixed asset location expect(evalCFN(location.bucketName)).toEqual('default-bucket'); - expect(evalCFN(location.httpUrl)).toEqual('https://s3.the_region.domain.aws/default-bucket/abcdef.js'); + expect(evalCFN(location.httpUrl)).toEqual('https://s3.us-east-1.domain.aws/default-bucket/abcdef.js'); // THEN - object key contains source hash somewhere expect(location.objectKey.indexOf('abcdef')).toBeGreaterThan(-1); @@ -83,7 +89,7 @@ describe(AppScopedStagingSynthesizer, () => { // THEN - we have a fixed asset location const repo = 'abcdefrepo'; expect(evalCFN(location.repositoryName)).toEqual(repo); - expect(evalCFN(location.imageUri)).toEqual(`the_account.dkr.ecr.the_region.domain.aws/${repo}:abcdef`); + expect(evalCFN(location.imageUri)).toEqual(`000000000000.dkr.ecr.us-east-1.domain.aws/${repo}:abcdef`); }); test('throws with docker image asset without uniqueId', () => { From f3673ca209879b97cf54d6ee2348aaf0e0f1797e Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Fri, 10 Mar 2023 11:52:44 -0500 Subject: [PATCH 020/120] bootstrap role --- .../core-synthesizer/lib/synthesizer.ts | 41 ++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts index afc502699dd03..8825d70d06164 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts @@ -1,7 +1,44 @@ -import { App, AssetManifestBuilder, DockerImageAssetLocation, DockerImageAssetSource, Environment, FileAssetLocation, FileAssetSource, IBoundStackSynthesizer, IReusableStackSynthesizer, ISynthesisSession, Stack, StackSynthesizer, Stage, Token } from '@aws-cdk/core'; +import { + App, + Arn, + AssetManifestBuilder, + DockerImageAssetLocation, + DockerImageAssetSource, + Environment, + FileAssetLocation, + FileAssetSource, + IBoundStackSynthesizer, + IReusableStackSynthesizer, + ISynthesisSession, + Stack, + StackSynthesizer, + Stage, + Token, +} from '@aws-cdk/core'; import * as cxapi from '@aws-cdk/cx-api'; import { IStagingStack, DefaultStagingStack } from './staging-stack'; +export class BootstrapRole { + public static default() { + return new BootstrapRole('default'); + } + + public static fromRoleArn(arn: string) { + return new BootstrapRole(arn); + } + + public static fromRoleName(name: string) { + const arn = Arn.format({ + resource: 'role', + service: 'iam', + resourceName: name, + }); + return new BootstrapRole(arn); + } + + private constructor(public readonly roleArn: string) {} +} + /** * New stack synthesizer properties */ @@ -18,7 +55,7 @@ export interface StagingStackSynthesizerProps { * * @default - default role */ - readonly lookupRoleArn?: string; + readonly lookupRoleArn?: BootstrapRole; } /** From 4b6944b79f188a2c6b2208b974ae9718ec6dfa75 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 15 Mar 2023 17:49:18 -0400 Subject: [PATCH 021/120] basic functionality for file assets --- ...ging-stack.ts => default-staging-stack.ts} | 88 ++++- .../@aws-cdk/core-synthesizer/lib/index.ts | 2 +- .../core-synthesizer/lib/synthesizer.ts | 24 +- .../@aws-cdk/core-synthesizer/package.json | 5 +- .../core-synthesizer/test/assets/index.py | 1 + .../StagingStack.assets.json | 32 ++ .../StagingStack.template.json | 198 ++++++++++++ .../app-scoped-staging-test.assets.json | 32 ++ .../app-scoped-staging-test.template.json | 1 + .../__entrypoint__.js | 144 +++++++++ .../index.js | 78 +++++ .../integ.synthesizer.js.snapshot/cdk.out | 1 + .../integ.synthesizer.js.snapshot/integ.json | 12 + .../manifest.json | 163 ++++++++++ ...efaultTestDeployAssert208D3414.assets.json | 19 ++ ...aultTestDeployAssert208D3414.template.json | 36 +++ .../integ.synthesizer.js.snapshot/tree.json | 304 ++++++++++++++++++ .../test/integ.synthesizer.ts | 26 ++ .../core-synthesizer/test/synthesizer.test.ts | 23 +- .../@aws-cdk/core-synthesizer/tsconfig.json | 18 ++ 20 files changed, 1179 insertions(+), 28 deletions(-) rename packages/@aws-cdk/core-synthesizer/lib/{staging-stack.ts => default-staging-stack.ts} (63%) create mode 100644 packages/@aws-cdk/core-synthesizer/test/assets/index.py create mode 100644 packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.assets.json create mode 100644 packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.template.json create mode 100644 packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json create mode 100644 packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json create mode 100644 packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js create mode 100644 packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js create mode 100644 packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/integ.json create mode 100644 packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json create mode 100644 packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json create mode 100644 packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/tree.json create mode 100644 packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.ts diff --git a/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts b/packages/@aws-cdk/core-synthesizer/lib/default-staging-stack.ts similarity index 63% rename from packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts rename to packages/@aws-cdk/core-synthesizer/lib/default-staging-stack.ts index 59ec1d8bba8c3..7b794523dec06 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/staging-stack.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/default-staging-stack.ts @@ -3,6 +3,7 @@ import * as iam from '@aws-cdk/aws-iam'; import * as s3 from '@aws-cdk/aws-s3'; import { Arn, ArnFormat, Aws, DockerImageAssetSource, FileAssetSource, RemovalPolicy, Stack, StackProps } from '@aws-cdk/core'; import { Construct, IConstruct } from 'constructs'; +import { BootstrapRole } from './synthesizer'; export interface FileAssetInfo { readonly bucketName: string; @@ -16,7 +17,7 @@ export interface DockerAssetInfo { /** * Information on how a Staging Stack should look. */ -export interface IStagingStack extends IConstruct { +export interface IDefaultStagingStack extends IConstruct { /** * The app-scoped, environment-keyed bucket created in this staging stack. */ @@ -28,6 +29,13 @@ export interface IStagingStack extends IConstruct { */ readonly stagingRepos: Record; + /** + * Stack others can depend on. + * + * @default this + */ + readonly dependencyStack: Stack; + /** * // TODO */ @@ -40,9 +48,9 @@ export interface IStagingStack extends IConstruct { } /** - * Staging Stack Properties + * Default Staging Stack Properties */ -export interface StagingStackProps extends StackProps { +export interface DefaultStagingStackProps extends StackProps { /** * Explicit name for the staging bucket * @@ -55,7 +63,7 @@ export interface StagingStackProps extends StackProps { * * @default - a well-known name unique to this app/env. */ - readonly fileAssetPublishingRoleName?: string; + readonly fileAssetPublishingRole?: BootstrapRole; /** * Pass in an existing role to be used as the image publishing role. @@ -68,11 +76,11 @@ export interface StagingStackProps extends StackProps { /** * A default Staging Stack */ -export class DefaultStagingStack extends Stack implements IStagingStack { +export class DefaultStagingStack extends Stack implements IDefaultStagingStack { /** * Default asset publishing role name for file (S3) assets. */ - private static readonly DEFAULT_FILE_ASSET_PUBLISHING_ROLE_NAME = 'cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}'; + private static readonly DEFAULT_FILE_ASSET_PUBLISHING_ROLE_NAME = 'cdk-file-publishing-role'; /** * Default asset publishing role name for docker (ECR) assets. @@ -89,15 +97,20 @@ export class DefaultStagingStack extends Stack implements IStagingStack { */ public readonly stagingRepos: Record; + public readonly dependencyStack: Stack; + private readonly stagingBucketName?: string; - private fileAssetPublishingRoleName: string; + private fileAssetPublishingRole?: BootstrapRole; private dockerAssetPublishingRoleName: string; - constructor(scope: Construct, id: string, props: StagingStackProps = {}) { + constructor(scope: Construct, id: string, props: DefaultStagingStackProps = {}) { super(scope, id, props); + this.dependencyStack = this; + this.stagingBucketName = props.stagingBucketName; - this.fileAssetPublishingRoleName = props.fileAssetPublishingRoleName ?? DefaultStagingStack.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_NAME; + // eslint-disable-next-line max-len + this.fileAssetPublishingRole = props.fileAssetPublishingRole; this.dockerAssetPublishingRoleName = props.dockerAssetPublishingRoleName ?? DefaultStagingStack.DEFAULT_DOCKER_ASSET_PUBISHING_ROLE_NAME; this.stagingRepos = {}; } @@ -106,10 +119,54 @@ export class DefaultStagingStack extends Stack implements IStagingStack { * Returns the well-known name of the file publishing role */ private getCreateFilePublishingRole() { - this.node.tryFindChild(this.fileAssetPublishingRoleName) as iam.Role ?? new iam.Role(this, this.fileAssetPublishingRoleName, { - roleName: DefaultStagingStack.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_NAME, - assumedBy: new iam.ServicePrincipal('sts.amazonaws.com'), // TODO actually create correct role - }); + // FilePublishingRoleDefaultPolicy: + // Type: AWS::IAM::Policy + // Properties: + // PolicyDocument: + // Statement: + // - Action: + // - s3:GetObject* + // - s3:GetBucket* + // - s3:GetEncryptionConfiguration + // - s3:List* + // - s3:DeleteObject* + // - s3:PutObject* + // - s3:Abort* + // Resource: + // - Fn::Sub: "${StagingBucket.Arn}" + // - Fn::Sub: "${StagingBucket.Arn}/*" + // Effect: Allow + // - Action: + // - kms:Decrypt + // - kms:DescribeKey + // - kms:Encrypt + // - kms:ReEncrypt* + // - kms:GenerateDataKey* + // Effect: Allow + // Resource: + // Fn::If: + // - CreateNewKey + // - Fn::Sub: "${FileAssetsBucketEncryptionKey.Arn}" + // - Fn::Sub: arn:${AWS::Partition}:kms:${AWS::Region}:${AWS::AccountId}:key/${FileAssetsBucketKmsKeyId} + // Version: '2012-10-17' + // Roles: + // - Ref: FilePublishingRole + // PolicyName: + // Fn::Sub: cdk-${Qualifier}-file-publishing-role-default-policy-${AWS::AccountId}-${AWS::Region} + const createIamRole = () => { + const role = new iam.Role(this, roleName, { + roleName: roleName, + assumedBy: new iam.ServicePrincipal('sts.amazonaws.com'), // TODO actually create correct role + }); + role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('AdministratorAccess')); + return role; + }; + + if (this.fileAssetPublishingRole) { + return this.fileAssetPublishingRole.roleArn; + } + const roleName = DefaultStagingStack.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_NAME; + this.node.tryFindChild(roleName) as iam.Role ?? createIamRole(); return Arn.format({ partition: this.partition ?? Aws.PARTITION, account: this.account ?? Aws.ACCOUNT_ID, @@ -141,10 +198,11 @@ export class DefaultStagingStack extends Stack implements IStagingStack { } private getCreateBucket(): string { - const stagingBucketName = this.stagingBucketName ?? 'default-bucket'; //`cdk-${this.account}-${this.region}`; + // Error: Resolution error: ID components may not include unresolved tokens: + const stagingBucketName = this.stagingBucketName ?? 'cdk-489318732371-us-east-1'; //`cdk-${this.account}-${this.region}`; this.node.tryFindChild(stagingBucketName) as s3.Bucket ?? new s3.Bucket(this, stagingBucketName, { bucketName: stagingBucketName, - autoDeleteObjects: true, + // autoDeleteObjects: true, // this creates a custom resource with an asset removalPolicy: RemovalPolicy.DESTROY, }); return stagingBucketName; diff --git a/packages/@aws-cdk/core-synthesizer/lib/index.ts b/packages/@aws-cdk/core-synthesizer/lib/index.ts index df4d2cb14226a..e8df9691bee4d 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/index.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/index.ts @@ -1,2 +1,2 @@ -export * from './staging-stack'; +export * from './default-staging-stack'; export * from './synthesizer'; diff --git a/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts index 8825d70d06164..fc263f1afe11b 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts @@ -16,9 +16,12 @@ import { Token, } from '@aws-cdk/core'; import * as cxapi from '@aws-cdk/cx-api'; -import { IStagingStack, DefaultStagingStack } from './staging-stack'; +import { IDefaultStagingStack as IStagingStack, DefaultStagingStack } from './default-staging-stack'; export class BootstrapRole { + // public abstract arn(stack: Stack) { + // // stack.formatArn + // } public static default() { return new BootstrapRole('default'); } @@ -28,6 +31,7 @@ export class BootstrapRole { } public static fromRoleName(name: string) { + // wont work const arn = Arn.format({ resource: 'role', service: 'iam', @@ -37,6 +41,10 @@ export class BootstrapRole { } private constructor(public readonly roleArn: string) {} + + public get roleName() { + return this.roleArn.split('/')[1]; + } } /** @@ -55,7 +63,7 @@ export interface StagingStackSynthesizerProps { * * @default - default role */ - readonly lookupRoleArn?: BootstrapRole; + readonly lookupRole?: BootstrapRole; } /** @@ -130,11 +138,11 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta private assetManifest = new AssetManifestBuilder(); private lookupRoleArn: string; - constructor(stack: Stack, props: StagingStackSynthesizerProps = {}) { + constructor(private readonly stack: Stack, props: StagingStackSynthesizerProps = {}) { super(); super.bind(stack); - this.lookupRoleArn = props.lookupRoleArn ?? BoundStagingStackSynthesizer.DEFAULT_LOOKUP_ROLE_ARN; + this.lookupRoleArn = props.lookupRole?.roleArn ?? BoundStagingStackSynthesizer.DEFAULT_LOOKUP_ROLE_ARN; const app = App.of(stack); if (!app) { @@ -149,10 +157,13 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta private getCreateStagingStack(app: Stage, env: Environment): IStagingStack { // TODO: env could be tokens const stackName = `StagingStack${app.stageName}`; - return app.node.tryFindChild(stackName) as IStagingStack ?? new DefaultStagingStack(app, stackName, { + const stagingStack = app.node.tryFindChild(stackName) as DefaultStagingStack ?? new DefaultStagingStack(app, stackName, { env, stackName, }); + this.stack.addDependency(stagingStack, 'reason'); + + return stagingStack; } public synthesize(session: ISynthesisSession): void { @@ -167,8 +178,7 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta console.log(templateAsset, assetManifestId); this.emitArtifact(session, { - // TODO: uhh is this right? - additionalDependencies: [assetManifestId/*, this.stagingStack.node.id*/], + additionalDependencies: [assetManifestId], stackTemplateAssetObjectUrl: templateAsset.s3ObjectUrlWithPlaceholders, }); } diff --git a/packages/@aws-cdk/core-synthesizer/package.json b/packages/@aws-cdk/core-synthesizer/package.json index a63955c9d622f..334e8ead49ac9 100644 --- a/packages/@aws-cdk/core-synthesizer/package.json +++ b/packages/@aws-cdk/core-synthesizer/package.json @@ -84,6 +84,7 @@ }, "dependencies": { "@aws-cdk/core": "0.0.0", + "@aws-cdk/cx-api": "0.0.0", "constructs": "^10.0.0", "@aws-cdk/aws-s3": "0.0.0", "@aws-cdk/aws-ecr": "0.0.0", @@ -91,8 +92,10 @@ }, "devDependencies": { "@aws-cdk/core": "0.0.0", - "@aws-cdk/cx-api": "0.0.0", "@aws-cdk/cloud-assembly-schema": "0.0.0", + "@aws-cdk/integ-runner": "0.0.0", + "@aws-cdk/integ-tests": "0.0.0", + "@aws-cdk/aws-lambda": "0.0.0", "constructs": "^10.0.0", "@aws-cdk/aws-s3": "0.0.0", "@aws-cdk/aws-ecr": "0.0.0", diff --git a/packages/@aws-cdk/core-synthesizer/test/assets/index.py b/packages/@aws-cdk/core-synthesizer/test/assets/index.py new file mode 100644 index 0000000000000..ed0f110e2e61e --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/test/assets/index.py @@ -0,0 +1 @@ +print('hello') \ No newline at end of file diff --git a/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.assets.json b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.assets.json new file mode 100644 index 0000000000000..1368be8d66072 --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.assets.json @@ -0,0 +1,32 @@ +{ + "version": "30.1.0", + "files": { + "33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c": { + "source": { + "path": "asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c", + "packaging": "zip" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c.zip", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + }, + "cf71e98470e9434a04895f1736e006c3de6a6a9d2f563b2cef88849dacc1e1f6": { + "source": { + "path": "StagingStack.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "cf71e98470e9434a04895f1736e006c3de6a6a9d2f563b2cef88849dacc1e1f6.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/core-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.template.json b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.template.json new file mode 100644 index 0000000000000..d6d3fa3f8da78 --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.template.json @@ -0,0 +1,198 @@ +{ + "Resources": { + "defaultbucket4957B632": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketName": "default-bucket", + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ] + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "defaultbucketPolicyC116629D": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "defaultbucket4957B632" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:DeleteObject*", + "s3:GetBucket*", + "s3:List*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "defaultbucket4957B632", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "defaultbucket4957B632", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "defaultbucketAutoDeleteObjectsCustomResource08E22CE8": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "defaultbucket4957B632" + } + }, + "DependsOn": [ + "defaultbucketPolicyC116629D" + ], + "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": "33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c.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": "defaultbucket4957B632" + }, + " S3 bucket." + ] + ] + } + }, + "DependsOn": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092" + ] + }, + "cdkQualifierfilepublishingroleAWSAccountIdAWSRegionE5E2CED6": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "sts.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "RoleName": "cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + }, + "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/core-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json new file mode 100644 index 0000000000000..df9572539a3ab --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json @@ -0,0 +1,32 @@ +{ + "version": "30.1.0", + "files": { + "abcdef": { + "source": { + "path": "/Users/conroyka/Desktop/aws-cdk-kaizen/kaizen-aws-ACTUAL/aws-cdk/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "default-bucket", + "objectKey": "abcdef.js", + "assumeRoleArn": "arn:${Token[AWS.Partition.9]}:iam:${Token[AWS.Region.10]}:${Token[AWS.AccountId.6]}:role:cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + }, + "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a": { + "source": { + "path": "app-scoped-staging-test.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "default-bucket", + "objectKey": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a.json", + "assumeRoleArn": "arn:${Token[AWS.Partition.9]}:iam:${Token[AWS.Region.10]}:${Token[AWS.AccountId.6]}:role:cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json new file mode 100644 index 0000000000000..9e26dfeeb6e64 --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js new file mode 100644 index 0000000000000..1e3a3093c1706 --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js @@ -0,0 +1,144 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.withRetries = 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 }, + }; + const retryOptions = { + attempts: 5, + sleep: 1000, + }; + await withRetries(retryOptions, 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); +} +function withRetries(options, fn) { + return async (...xs) => { + let attempts = options.attempts; + let ms = options.sleep; + while (true) { + try { + return await fn(...xs); + } + catch (e) { + if (attempts-- <= 0) { + throw e; + } + await sleep(Math.floor(Math.random() * ms)); + ms *= 2; + } + } + }; +} +exports.withRetries = withRetries; +async function sleep(ms) { + return new Promise((ok) => setTimeout(ok, ms)); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9kZWpzLWVudHJ5cG9pbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJub2RlanMtZW50cnlwb2ludC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwrQkFBK0I7QUFDL0IsMkJBQTJCO0FBRTNCLGlCQUFpQjtBQUNKLFFBQUEsUUFBUSxHQUFHO0lBQ3RCLGVBQWUsRUFBRSxzQkFBc0I7SUFDdkMsR0FBRyxFQUFFLFVBQVU7SUFDZixrQkFBa0IsRUFBRSxJQUFJO0lBQ3hCLGdCQUFnQixFQUFFLFNBQVM7Q0FDNUIsQ0FBQztBQUVGLE1BQU0sZ0NBQWdDLEdBQUcsd0RBQXdELENBQUM7QUFDbEcsTUFBTSwwQkFBMEIsR0FBRyw4REFBOEQsQ0FBQztBQVczRixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtELEVBQUUsT0FBMEI7SUFDMUcsTUFBTSxjQUFjLEdBQUcsRUFBRSxHQUFHLEtBQUssRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLENBQUM7SUFDeEQsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxjQUFjLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFM0QsdUVBQXVFO0lBQ3ZFLHVFQUF1RTtJQUN2RSxhQUFhO0lBQ2IsSUFBSSxLQUFLLENBQUMsV0FBVyxLQUFLLFFBQVEsSUFBSSxLQUFLLENBQUMsa0JBQWtCLEtBQUssZ0NBQWdDLEVBQUU7UUFDbkcsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsdURBQXVELENBQUMsQ0FBQztRQUN0RSxNQUFNLGNBQWMsQ0FBQyxTQUFTLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDdkMsT0FBTztLQUNSO0lBRUQsSUFBSTtRQUNGLHlFQUF5RTtRQUN6RSxpRUFBaUU7UUFDakUsd0NBQXdDO1FBQ3hDLGlFQUFpRTtRQUNqRSxNQUFNLFdBQVcsR0FBWSxPQUFPLENBQUMsZ0JBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLE9BQU8sQ0FBQztRQUN4RSxNQUFNLE1BQU0sR0FBRyxNQUFNLFdBQVcsQ0FBQyxjQUFjLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFFMUQsdURBQXVEO1FBQ3ZELE1BQU0sYUFBYSxHQUFHLGNBQWMsQ0FBQyxLQUFLLEVBQUUsTUFBTSxDQUFDLENBQUM7UUFFcEQsMkJBQTJCO1FBQzNCLE1BQU0sY0FBYyxDQUFDLFNBQVMsRUFBRSxhQUFhLENBQUMsQ0FBQztLQUNoRDtJQUFDLE9BQU8sQ0FBQyxFQUFFO1FBQ1YsTUFBTSxJQUFJLEdBQWE7WUFDckIsR0FBRyxLQUFLO1lBQ1IsTUFBTSxFQUFFLGdCQUFRLENBQUMsa0JBQWtCLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPO1NBQzFELENBQUM7UUFFRixJQUFJLENBQUMsSUFBSSxDQUFDLGtCQUFrQixFQUFFO1lBQzVCLHlFQUF5RTtZQUN6RSxtRUFBbUU7WUFDbkUsd0VBQXdFO1lBQ3hFLHFFQUFxRTtZQUNyRSxnQ0FBZ0M7WUFDaEMsSUFBSSxLQUFLLENBQUMsV0FBVyxLQUFLLFFBQVEsRUFBRTtnQkFDbEMsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsNEdBQTRHLENBQUMsQ0FBQztnQkFDM0gsSUFBSSxDQUFDLGtCQUFrQixHQUFHLGdDQUFnQyxDQUFDO2FBQzVEO2lCQUFNO2dCQUNMLGtFQUFrRTtnQkFDbEUsNkRBQTZEO2dCQUM3RCxnQkFBUSxDQUFDLEdBQUcsQ0FBQyw2REFBNkQsSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUM7YUFDcEc7U0FDRjtRQUVELG1FQUFtRTtRQUNuRSxNQUFNLGNBQWMsQ0FBQyxRQUFRLEVBQUUsSUFBSSxDQUFDLENBQUM7S0FDdEM7QUFDSCxDQUFDO0FBbkRELDBCQW1EQztBQUVELFNBQVMsY0FBYyxDQUNyQixVQUF5RixFQUN6RixrQkFBMEMsRUFBRztJQUU3QyxzRUFBc0U7SUFDdEUsdUJBQXVCO0lBQ3ZCLE1BQU0sa0JBQWtCLEdBQUcsZUFBZSxDQUFDLGtCQUFrQixJQUFJLFVBQVUsQ0FBQyxrQkFBa0IsSUFBSSxVQUFVLENBQUMsU0FBUyxDQUFDO0lBRXZILGtFQUFrRTtJQUNsRSxJQUFJLFVBQVUsQ0FBQyxXQUFXLEtBQUssUUFBUSxJQUFJLGtCQUFrQixLQUFLLFVBQVUsQ0FBQyxrQkFBa0IsRUFBRTtRQUMvRixNQUFNLElBQUksS0FBSyxDQUFDLHdEQUF3RCxVQUFVLENBQUMsa0JBQWtCLFNBQVMsZUFBZSxDQUFDLGtCQUFrQixtQkFBbUIsQ0FBQyxDQUFDO0tBQ3RLO0lBRUQsMERBQTBEO0lBQzFELE9BQU87UUFDTCxHQUFHLFVBQVU7UUFDYixHQUFHLGVBQWU7UUFDbEIsa0JBQWtCLEVBQUUsa0JBQWtCO0tBQ3ZDLENBQUM7QUFDSixDQUFDO0FBRUQsS0FBSyxVQUFVLGNBQWMsQ0FBQyxNQUE0QixFQUFFLEtBQWU7SUFDekUsTUFBTSxJQUFJLEdBQW1EO1FBQzNELE1BQU0sRUFBRSxNQUFNO1FBQ2QsTUFBTSxFQUFFLEtBQUssQ0FBQyxNQUFNLElBQUksTUFBTTtRQUM5QixPQUFPLEVBQUUsS0FBSyxDQUFDLE9BQU87UUFDdEIsU0FBUyxFQUFFLEtBQUssQ0FBQyxTQUFTO1FBQzFCLGtCQUFrQixFQUFFLEtBQUssQ0FBQyxrQkFBa0IsSUFBSSwwQkFBMEI7UUFDMUUsaUJBQWlCLEVBQUUsS0FBSyxDQUFDLGlCQUFpQjtRQUMxQyxNQUFNLEVBQUUsS0FBSyxDQUFDLE1BQU07UUFDcEIsSUFBSSxFQUFFLEtBQUssQ0FBQyxJQUFJO0tBQ2pCLENBQUM7SUFFRixnQkFBUSxDQUFDLEdBQUcsQ0FBQyxtQ0FBbUMsRUFBRSxJQUFJLENBQUMsQ0FBQztJQUV4RCxNQUFNLFlBQVksR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQzFDLE1BQU0sU0FBUyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLFdBQVcsQ0FBQyxDQUFDO0lBQy9DLE1BQU0sR0FBRyxHQUFHO1FBQ1YsUUFBUSxFQUFFLFNBQVMsQ0FBQyxRQUFRO1FBQzVCLElBQUksRUFBRSxTQUFTLENBQUMsSUFBSTtRQUNwQixNQUFNLEVBQUUsS0FBSztRQUNiLE9BQU8sRUFBRSxFQUFFLGNBQWMsRUFBRSxFQUFFLEVBQUUsZ0JBQWdCLEVBQUUsWUFBWSxDQUFDLE1BQU0sRUFBRTtLQUN2RSxDQUFDO0lBRUYsTUFBTSxZQUFZLEdBQUc7UUFDbkIsUUFBUSxFQUFFLENBQUM7UUFDWCxLQUFLLEVBQUUsSUFBSTtLQUNaLENBQUM7SUFDRixNQUFNLFdBQVcsQ0FBQyxZQUFZLEVBQUUsZ0JBQVEsQ0FBQyxlQUFlLENBQUMsQ0FBQyxHQUFHLEVBQUUsWUFBWSxDQUFDLENBQUM7QUFDL0UsQ0FBQztBQUVELEtBQUssVUFBVSxzQkFBc0IsQ0FBQyxPQUE2QixFQUFFLFlBQW9CO0lBQ3ZGLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEVBQUU7UUFDckMsSUFBSTtZQUNGLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQztZQUN2RCxPQUFPLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQztZQUM1QixPQUFPLENBQUMsS0FBSyxDQUFDLFlBQVksQ0FBQyxDQUFDO1lBQzVCLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQztTQUNmO1FBQUMsT0FBTyxDQUFDLEVBQUU7WUFDVixNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7U0FDWDtJQUNILENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVELFNBQVMsVUFBVSxDQUFDLEdBQVcsRUFBRSxHQUFHLE1BQWE7SUFDL0Msc0NBQXNDO0lBQ3RDLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsTUFBTSxDQUFDLENBQUM7QUFDOUIsQ0FBQztBQVNELFNBQWdCLFdBQVcsQ0FBMEIsT0FBcUIsRUFBRSxFQUE0QjtJQUN0RyxPQUFPLEtBQUssRUFBRSxHQUFHLEVBQUssRUFBRSxFQUFFO1FBQ3hCLElBQUksUUFBUSxHQUFHLE9BQU8sQ0FBQyxRQUFRLENBQUM7UUFDaEMsSUFBSSxFQUFFLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQztRQUN2QixPQUFPLElBQUksRUFBRTtZQUNYLElBQUk7Z0JBQ0YsT0FBTyxNQUFNLEVBQUUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDO2FBQ3hCO1lBQUMsT0FBTyxDQUFDLEVBQUU7Z0JBQ1YsSUFBSSxRQUFRLEVBQUUsSUFBSSxDQUFDLEVBQUU7b0JBQ25CLE1BQU0sQ0FBQyxDQUFDO2lCQUNUO2dCQUNELE1BQU0sS0FBSyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUFDLENBQUM7Z0JBQzVDLEVBQUUsSUFBSSxDQUFDLENBQUM7YUFDVDtTQUNGO0lBQ0gsQ0FBQyxDQUFDO0FBQ0osQ0FBQztBQWhCRCxrQ0FnQkM7QUFFRCxLQUFLLFVBQVUsS0FBSyxDQUFDLEVBQVU7SUFDN0IsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsVUFBVSxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDO0FBQ2pELENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBodHRwcyBmcm9tICdodHRwcyc7XG5pbXBvcnQgKiBhcyB1cmwgZnJvbSAndXJsJztcblxuLy8gZm9yIHVuaXQgdGVzdHNcbmV4cG9ydCBjb25zdCBleHRlcm5hbCA9IHtcbiAgc2VuZEh0dHBSZXF1ZXN0OiBkZWZhdWx0U2VuZEh0dHBSZXF1ZXN0LFxuICBsb2c6IGRlZmF1bHRMb2csXG4gIGluY2x1ZGVTdGFja1RyYWNlczogdHJ1ZSxcbiAgdXNlckhhbmRsZXJJbmRleDogJy4vaW5kZXgnLFxufTtcblxuY29uc3QgQ1JFQVRFX0ZBSUxFRF9QSFlTSUNBTF9JRF9NQVJLRVIgPSAnQVdTQ0RLOjpDdXN0b21SZXNvdXJjZVByb3ZpZGVyRnJhbWV3b3JrOjpDUkVBVEVfRkFJTEVEJztcbmNvbnN0IE1JU1NJTkdfUEhZU0lDQUxfSURfTUFSS0VSID0gJ0FXU0NESzo6Q3VzdG9tUmVzb3VyY2VQcm92aWRlckZyYW1ld29yazo6TUlTU0lOR19QSFlTSUNBTF9JRCc7XG5cbmV4cG9ydCB0eXBlIFJlc3BvbnNlID0gQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCAmIEhhbmRsZXJSZXNwb25zZTtcbmV4cG9ydCB0eXBlIEhhbmRsZXIgPSAoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQsIGNvbnRleHQ6IEFXU0xhbWJkYS5Db250ZXh0KSA9PiBQcm9taXNlPEhhbmRsZXJSZXNwb25zZSB8IHZvaWQ+O1xuZXhwb3J0IHR5cGUgSGFuZGxlclJlc3BvbnNlID0gdW5kZWZpbmVkIHwge1xuICBEYXRhPzogYW55O1xuICBQaHlzaWNhbFJlc291cmNlSWQ/OiBzdHJpbmc7XG4gIFJlYXNvbj86IHN0cmluZztcbiAgTm9FY2hvPzogYm9vbGVhbjtcbn07XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBoYW5kbGVyKGV2ZW50OiBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50LCBjb250ZXh0OiBBV1NMYW1iZGEuQ29udGV4dCkge1xuICBjb25zdCBzYW5pdGl6ZWRFdmVudCA9IHsgLi4uZXZlbnQsIFJlc3BvbnNlVVJMOiAnLi4uJyB9O1xuICBleHRlcm5hbC5sb2coSlNPTi5zdHJpbmdpZnkoc2FuaXRpemVkRXZlbnQsIHVuZGVmaW5lZCwgMikpO1xuXG4gIC8vIGlnbm9yZSBERUxFVEUgZXZlbnQgd2hlbiB0aGUgcGh5c2ljYWwgcmVzb3VyY2UgSUQgaXMgdGhlIG1hcmtlciB0aGF0XG4gIC8vIGluZGljYXRlcyB0aGF0IHRoaXMgREVMRVRFIGlzIGEgc3Vic2VxdWVudCBERUxFVEUgdG8gYSBmYWlsZWQgQ1JFQVRFXG4gIC8vIG9wZXJhdGlvbi5cbiAgaWYgKGV2ZW50LlJlcXVlc3RUeXBlID09PSAnRGVsZXRlJyAmJiBldmVudC5QaHlzaWNhbFJlc291cmNlSWQgPT09IENSRUFURV9GQUlMRURfUEhZU0lDQUxfSURfTUFSS0VSKSB7XG4gICAgZXh0ZXJuYWwubG9nKCdpZ25vcmluZyBERUxFVEUgZXZlbnQgY2F1c2VkIGJ5IGEgZmFpbGVkIENSRUFURSBldmVudCcpO1xuICAgIGF3YWl0IHN1Ym1pdFJlc3BvbnNlKCdTVUNDRVNTJywgZXZlbnQpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIHRyeSB7XG4gICAgLy8gaW52b2tlIHRoZSB1c2VyIGhhbmRsZXIuIHRoaXMgaXMgaW50ZW50aW9uYWxseSBpbnNpZGUgdGhlIHRyeS1jYXRjaCB0b1xuICAgIC8vIGVuc3VyZSB0aGF0IGlmIHRoZXJlIGlzIGFuIGVycm9yIGl0J3MgcmVwb3J0ZWQgYXMgYSBmYWlsdXJlIHRvXG4gICAgLy8gY2xvdWRmb3JtYXRpb24gKG90aGVyd2lzZSBjZm4gd2FpdHMpLlxuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tcmVxdWlyZS1pbXBvcnRzXG4gICAgY29uc3QgdXNlckhhbmRsZXI6IEhhbmRsZXIgPSByZXF1aXJlKGV4dGVybmFsLnVzZXJIYW5kbGVySW5kZXgpLmhhbmRsZXI7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdXNlckhhbmRsZXIoc2FuaXRpemVkRXZlbnQsIGNvbnRleHQpO1xuXG4gICAgLy8gdmFsaWRhdGUgdXNlciByZXNwb25zZSBhbmQgY3JlYXRlIHRoZSBjb21iaW5lZCBldmVudFxuICAgIGNvbnN0IHJlc3BvbnNlRXZlbnQgPSByZW5kZXJSZXNwb25zZShldmVudCwgcmVzdWx0KTtcblxuICAgIC8vIHN1Ym1pdCB0byBjZm4gYXMgc3VjY2Vzc1xuICAgIGF3YWl0IHN1Ym1pdFJlc3BvbnNlKCdTVUNDRVNTJywgcmVzcG9uc2VFdmVudCk7XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICBjb25zdCByZXNwOiBSZXNwb25zZSA9IHtcbiAgICAgIC4uLmV2ZW50LFxuICAgICAgUmVhc29uOiBleHRlcm5hbC5pbmNsdWRlU3RhY2tUcmFjZXMgPyBlLnN0YWNrIDogZS5tZXNzYWdlLFxuICAgIH07XG5cbiAgICBpZiAoIXJlc3AuUGh5c2ljYWxSZXNvdXJjZUlkKSB7XG4gICAgICAvLyBzcGVjaWFsIGNhc2U6IGlmIENSRUFURSBmYWlscywgd2hpY2ggdXN1YWxseSBpbXBsaWVzLCB3ZSB1c3VhbGx5IGRvbid0XG4gICAgICAvLyBoYXZlIGEgcGh5c2ljYWwgcmVzb3VyY2UgaWQuIGluIHRoaXMgY2FzZSwgdGhlIHN1YnNlcXVlbnQgREVMRVRFXG4gICAgICAvLyBvcGVyYXRpb24gZG9lcyBub3QgaGF2ZSBhbnkgbWVhbmluZywgYW5kIHdpbGwgbGlrZWx5IGZhaWwgYXMgd2VsbC4gdG9cbiAgICAgIC8vIGFkZHJlc3MgdGhpcywgd2UgdXNlIGEgbWFya2VyIHNvIHRoZSBwcm92aWRlciBmcmFtZXdvcmsgY2FuIHNpbXBseVxuICAgICAgLy8gaWdub3JlIHRoZSBzdWJzZXF1ZW50IERFTEVURS5cbiAgICAgIGlmIChldmVudC5SZXF1ZXN0VHlwZSA9PT0gJ0NyZWF0ZScpIHtcbiAgICAgICAgZXh0ZXJuYWwubG9nKCdDUkVBVEUgZmFpbGVkLCByZXNwb25kaW5nIHdpdGggYSBtYXJrZXIgcGh5c2ljYWwgcmVzb3VyY2UgaWQgc28gdGhhdCB0aGUgc3Vic2VxdWVudCBERUxFVEUgd2lsbCBiZSBpZ25vcmVkJyk7XG4gICAgICAgIHJlc3AuUGh5c2ljYWxSZXNvdXJjZUlkID0gQ1JFQVRFX0ZBSUxFRF9QSFlTSUNBTF9JRF9NQVJLRVI7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBvdGhlcndpc2UsIGlmIFBoeXNpY2FsUmVzb3VyY2VJZCBpcyBub3Qgc3BlY2lmaWVkLCBzb21ldGhpbmcgaXNcbiAgICAgICAgLy8gdGVycmlibHkgd3JvbmcgYmVjYXVzZSBhbGwgb3RoZXIgZXZlbnRzIHNob3VsZCBoYXZlIGFuIElELlxuICAgICAgICBleHRlcm5hbC5sb2coYEVSUk9SOiBNYWxmb3JtZWQgZXZlbnQuIFwiUGh5c2ljYWxSZXNvdXJjZUlkXCIgaXMgcmVxdWlyZWQ6ICR7SlNPTi5zdHJpbmdpZnkoZXZlbnQpfWApO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIHRoaXMgaXMgYW4gYWN0dWFsIGVycm9yLCBmYWlsIHRoZSBhY3Rpdml0eSBhbHRvZ2V0aGVyIGFuZCBleGlzdC5cbiAgICBhd2FpdCBzdWJtaXRSZXNwb25zZSgnRkFJTEVEJywgcmVzcCk7XG4gIH1cbn1cblxuZnVuY3Rpb24gcmVuZGVyUmVzcG9uc2UoXG4gIGNmblJlcXVlc3Q6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQgJiB7IFBoeXNpY2FsUmVzb3VyY2VJZD86IHN0cmluZyB9LFxuICBoYW5kbGVyUmVzcG9uc2U6IHZvaWQgfCBIYW5kbGVyUmVzcG9uc2UgPSB7IH0pOiBSZXNwb25zZSB7XG5cbiAgLy8gaWYgcGh5c2ljYWwgSUQgaXMgbm90IHJldHVybmVkLCB3ZSBoYXZlIHNvbWUgZGVmYXVsdHMgZm9yIHlvdSBiYXNlZFxuICAvLyBvbiB0aGUgcmVxdWVzdCB0eXBlLlxuICBjb25zdCBwaHlzaWNhbFJlc291cmNlSWQgPSBoYW5kbGVyUmVzcG9uc2UuUGh5c2ljYWxSZXNvdXJjZUlkID8/IGNmblJlcXVlc3QuUGh5c2ljYWxSZXNvdXJjZUlkID8/IGNmblJlcXVlc3QuUmVxdWVzdElkO1xuXG4gIC8vIGlmIHdlIGFyZSBpbiBERUxFVEUgYW5kIHBoeXNpY2FsIElEIHdhcyBjaGFuZ2VkLCBpdCdzIGFuIGVycm9yLlxuICBpZiAoY2ZuUmVxdWVzdC5SZXF1ZXN0VHlwZSA9PT0gJ0RlbGV0ZScgJiYgcGh5c2ljYWxSZXNvdXJjZUlkICE9PSBjZm5SZXF1ZXN0LlBoeXNpY2FsUmVzb3VyY2VJZCkge1xuICAgIHRocm93IG5ldyBFcnJvcihgREVMRVRFOiBjYW5ub3QgY2hhbmdlIHRoZSBwaHlzaWNhbCByZXNvdXJjZSBJRCBmcm9tIFwiJHtjZm5SZXF1ZXN0LlBoeXNpY2FsUmVzb3VyY2VJZH1cIiB0byBcIiR7aGFuZGxlclJlc3BvbnNlLlBoeXNpY2FsUmVzb3VyY2VJZH1cIiBkdXJpbmcgZGVsZXRpb25gKTtcbiAgfVxuXG4gIC8vIG1lcmdlIHJlcXVlc3QgZXZlbnQgYW5kIHJlc3VsdCBldmVudCAocmVzdWx0IHByZXZhaWxzKS5cbiAgcmV0dXJuIHtcbiAgICAuLi5jZm5SZXF1ZXN0LFxuICAgIC4uLmhhbmRsZXJSZXNwb25zZSxcbiAgICBQaHlzaWNhbFJlc291cmNlSWQ6IHBoeXNpY2FsUmVzb3VyY2VJZCxcbiAgfTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gc3VibWl0UmVzcG9uc2Uoc3RhdHVzOiAnU1VDQ0VTUycgfCAnRkFJTEVEJywgZXZlbnQ6IFJlc3BvbnNlKSB7XG4gIGNvbnN0IGpzb246IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlUmVzcG9uc2UgPSB7XG4gICAgU3RhdHVzOiBzdGF0dXMsXG4gICAgUmVhc29uOiBldmVudC5SZWFzb24gPz8gc3RhdHVzLFxuICAgIFN0YWNrSWQ6IGV2ZW50LlN0YWNrSWQsXG4gICAgUmVxdWVzdElkOiBldmVudC5SZXF1ZXN0SWQsXG4gICAgUGh5c2ljYWxSZXNvdXJjZUlkOiBldmVudC5QaHlzaWNhbFJlc291cmNlSWQgfHwgTUlTU0lOR19QSFlTSUNBTF9JRF9NQVJLRVIsXG4gICAgTG9naWNhbFJlc291cmNlSWQ6IGV2ZW50LkxvZ2ljYWxSZXNvdXJjZUlkLFxuICAgIE5vRWNobzogZXZlbnQuTm9FY2hvLFxuICAgIERhdGE6IGV2ZW50LkRhdGEsXG4gIH07XG5cbiAgZXh0ZXJuYWwubG9nKCdzdWJtaXQgcmVzcG9uc2UgdG8gY2xvdWRmb3JtYXRpb24nLCBqc29uKTtcblxuICBjb25zdCByZXNwb25zZUJvZHkgPSBKU09OLnN0cmluZ2lmeShqc29uKTtcbiAgY29uc3QgcGFyc2VkVXJsID0gdXJsLnBhcnNlKGV2ZW50LlJlc3BvbnNlVVJMKTtcbiAgY29uc3QgcmVxID0ge1xuICAgIGhvc3RuYW1lOiBwYXJzZWRVcmwuaG9zdG5hbWUsXG4gICAgcGF0aDogcGFyc2VkVXJsLnBhdGgsXG4gICAgbWV0aG9kOiAnUFVUJyxcbiAgICBoZWFkZXJzOiB7ICdjb250ZW50LXR5cGUnOiAnJywgJ2NvbnRlbnQtbGVuZ3RoJzogcmVzcG9uc2VCb2R5Lmxlbmd0aCB9LFxuICB9O1xuXG4gIGNvbnN0IHJldHJ5T3B0aW9ucyA9IHtcbiAgICBhdHRlbXB0czogNSxcbiAgICBzbGVlcDogMTAwMCxcbiAgfTtcbiAgYXdhaXQgd2l0aFJldHJpZXMocmV0cnlPcHRpb25zLCBleHRlcm5hbC5zZW5kSHR0cFJlcXVlc3QpKHJlcSwgcmVzcG9uc2VCb2R5KTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZGVmYXVsdFNlbmRIdHRwUmVxdWVzdChvcHRpb25zOiBodHRwcy5SZXF1ZXN0T3B0aW9ucywgcmVzcG9uc2VCb2R5OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVxdWVzdCA9IGh0dHBzLnJlcXVlc3Qob3B0aW9ucywgXyA9PiByZXNvbHZlKCkpO1xuICAgICAgcmVxdWVzdC5vbignZXJyb3InLCByZWplY3QpO1xuICAgICAgcmVxdWVzdC53cml0ZShyZXNwb25zZUJvZHkpO1xuICAgICAgcmVxdWVzdC5lbmQoKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICByZWplY3QoZSk7XG4gICAgfVxuICB9KTtcbn1cblxuZnVuY3Rpb24gZGVmYXVsdExvZyhmbXQ6IHN0cmluZywgLi4ucGFyYW1zOiBhbnlbXSkge1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZVxuICBjb25zb2xlLmxvZyhmbXQsIC4uLnBhcmFtcyk7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUmV0cnlPcHRpb25zIHtcbiAgLyoqIEhvdyBtYW55IHJldHJpZXMgKHdpbGwgYXQgbGVhc3QgdHJ5IG9uY2UpICovXG4gIHJlYWRvbmx5IGF0dGVtcHRzOiBudW1iZXI7XG4gIC8qKiBTbGVlcCBiYXNlLCBpbiBtcyAqL1xuICByZWFkb25seSBzbGVlcDogbnVtYmVyO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gd2l0aFJldHJpZXM8QSBleHRlbmRzIEFycmF5PGFueT4sIEI+KG9wdGlvbnM6IFJldHJ5T3B0aW9ucywgZm46ICguLi54czogQSkgPT4gUHJvbWlzZTxCPik6ICguLi54czogQSkgPT4gUHJvbWlzZTxCPiB7XG4gIHJldHVybiBhc3luYyAoLi4ueHM6IEEpID0+IHtcbiAgICBsZXQgYXR0ZW1wdHMgPSBvcHRpb25zLmF0dGVtcHRzO1xuICAgIGxldCBtcyA9IG9wdGlvbnMuc2xlZXA7XG4gICAgd2hpbGUgKHRydWUpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIHJldHVybiBhd2FpdCBmbiguLi54cyk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGlmIChhdHRlbXB0cy0tIDw9IDApIHtcbiAgICAgICAgICB0aHJvdyBlO1xuICAgICAgICB9XG4gICAgICAgIGF3YWl0IHNsZWVwKE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIG1zKSk7XG4gICAgICAgIG1zICo9IDI7XG4gICAgICB9XG4gICAgfVxuICB9O1xufVxuXG5hc3luYyBmdW5jdGlvbiBzbGVlcChtczogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gIHJldHVybiBuZXcgUHJvbWlzZSgob2spID0+IHNldFRpbWVvdXQob2ssIG1zKSk7XG59Il19 \ No newline at end of file diff --git a/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js new file mode 100644 index 0000000000000..7ce4156d4ba41 --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/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/core-synthesizer/test/integ.synthesizer.js.snapshot/cdk.out b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/cdk.out new file mode 100644 index 0000000000000..b72fef144f05c --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"30.1.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/integ.json b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/integ.json new file mode 100644 index 0000000000000..54178327802a7 --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "30.1.0", + "testCases": { + "synthesizer-integ/DefaultTest": { + "stacks": [ + "app-scoped-staging-test" + ], + "assertionStack": "synthesizer-integ/DefaultTest/DeployAssert", + "assertionStackName": "synthesizerintegDefaultTestDeployAssert208D3414" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json new file mode 100644 index 0000000000000..5e4c80d2f3e33 --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json @@ -0,0 +1,163 @@ +{ + "version": "30.1.0", + "artifacts": { + "app-scoped-staging-test.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "app-scoped-staging-test.assets.json" + } + }, + "app-scoped-staging-test": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "app-scoped-staging-test.template.json", + "validateOnSynth": false, + "additionalDependencies": [ + "app-scoped-staging-test.assets" + ], + "stackTemplateAssetObjectUrl": "s3://default-bucket/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a.json" + }, + "dependencies": [ + "app-scoped-staging-test.assets" + ], + "displayName": "app-scoped-staging-test" + }, + "StagingStack.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "StagingStack.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "StagingStack": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "StagingStack.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}/cf71e98470e9434a04895f1736e006c3de6a6a9d2f563b2cef88849dacc1e1f6.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "StagingStack.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": [ + "StagingStack.assets" + ], + "metadata": { + "/StagingStack/default-bucket/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "defaultbucket4957B632" + } + ], + "/StagingStack/default-bucket/Policy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "defaultbucketPolicyC116629D" + } + ], + "/StagingStack/default-bucket/AutoDeleteObjectsCustomResource/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "defaultbucketAutoDeleteObjectsCustomResource08E22CE8" + } + ], + "/StagingStack/Custom::S3AutoDeleteObjectsCustomResourceProvider/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092" + } + ], + "/StagingStack/Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F" + } + ], + "/StagingStack/cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "cdkQualifierfilepublishingroleAWSAccountIdAWSRegionE5E2CED6" + } + ], + "/StagingStack/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/StagingStack/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "StagingStack" + }, + "synthesizerintegDefaultTestDeployAssert208D3414.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "synthesizerintegDefaultTestDeployAssert208D3414.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "synthesizerintegDefaultTestDeployAssert208D3414": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "synthesizerintegDefaultTestDeployAssert208D3414.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": [ + "synthesizerintegDefaultTestDeployAssert208D3414.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": [ + "synthesizerintegDefaultTestDeployAssert208D3414.assets" + ], + "metadata": { + "/synthesizer-integ/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/synthesizer-integ/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "synthesizer-integ/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json new file mode 100644 index 0000000000000..6a530e5336c5d --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json @@ -0,0 +1,19 @@ +{ + "version": "30.1.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "synthesizerintegDefaultTestDeployAssert208D3414.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/core-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.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/core-synthesizer/test/integ.synthesizer.js.snapshot/tree.json b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/tree.json new file mode 100644 index 0000000000000..50c68898dce48 --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/tree.json @@ -0,0 +1,304 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "app-scoped-staging-test": { + "id": "app-scoped-staging-test", + "path": "app-scoped-staging-test", + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "StagingStack": { + "id": "StagingStack", + "path": "StagingStack", + "children": { + "default-bucket": { + "id": "default-bucket", + "path": "StagingStack/default-bucket", + "children": { + "Resource": { + "id": "Resource", + "path": "StagingStack/default-bucket/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::S3::Bucket", + "aws:cdk:cloudformation:props": { + "bucketName": "default-bucket", + "tags": [ + { + "key": "aws-cdk:auto-delete-objects", + "value": "true" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-s3.CfnBucket", + "version": "0.0.0" + } + }, + "Policy": { + "id": "Policy", + "path": "StagingStack/default-bucket/Policy", + "children": { + "Resource": { + "id": "Resource", + "path": "StagingStack/default-bucket/Policy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::S3::BucketPolicy", + "aws:cdk:cloudformation:props": { + "bucket": { + "Ref": "defaultbucket4957B632" + }, + "policyDocument": { + "Statement": [ + { + "Action": [ + "s3:DeleteObject*", + "s3:GetBucket*", + "s3:List*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "defaultbucket4957B632", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "defaultbucket4957B632", + "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": "StagingStack/default-bucket/AutoDeleteObjectsCustomResource", + "children": { + "Default": { + "id": "Default", + "path": "StagingStack/default-bucket/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": "StagingStack/Custom::S3AutoDeleteObjectsCustomResourceProvider", + "children": { + "Staging": { + "id": "Staging", + "path": "StagingStack/Custom::S3AutoDeleteObjectsCustomResourceProvider/Staging", + "constructInfo": { + "fqn": "@aws-cdk/core.AssetStaging", + "version": "0.0.0" + } + }, + "Role": { + "id": "Role", + "path": "StagingStack/Custom::S3AutoDeleteObjectsCustomResourceProvider/Role", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + }, + "Handler": { + "id": "Handler", + "path": "StagingStack/Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.CustomResourceProvider", + "version": "0.0.0" + } + }, + "cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}": { + "id": "cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}", + "path": "StagingStack/cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}", + "children": { + "Importcdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}": { + "id": "Importcdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}", + "path": "StagingStack/cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}/Importcdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "StagingStack/cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "sts.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "roleName": "cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Role", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "StagingStack/BootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "StagingStack/CheckBootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core-synthesizer.DefaultStagingStack", + "version": "0.0.0" + } + }, + "synthesizer-integ": { + "id": "synthesizer-integ", + "path": "synthesizer-integ", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "synthesizer-integ/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "synthesizer-integ/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.264" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "synthesizer-integ/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "synthesizer-integ/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "synthesizer-integ/DefaultTest/DeployAssert/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/integ-tests.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.264" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.ts b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.ts new file mode 100644 index 0000000000000..ec5b72006bc78 --- /dev/null +++ b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.ts @@ -0,0 +1,26 @@ +import * as path from 'path'; +import * as lambda from '@aws-cdk/aws-lambda'; +import { App, Stack } from '@aws-cdk/core'; +import { AppScopedStagingSynthesizer, DefaultStagingStack } from '../lib'; + +const app = new App(); + +const stack = new Stack(app, 'app-scoped-staging-test', { + synthesizer: new AppScopedStagingSynthesizer({ + stagingStack: new DefaultStagingStack(app, 'StagingStackz', { + // fileAssetPublishingRole: BootstrapRole.fromRoleArn('arn:aws:iam::489318732371:role/cdk-0zn5h5s73a-file-publishing-role-489318732371-us-east-1'), + }), + }), + env: { + account: '489318732371', + region: 'us-east-1', + }, +}); + +new lambda.Function(stack, 'lambda', { + code: lambda.AssetCode.fromAsset(path.join(__dirname, 'assets')), + handler: 'index.handler', + runtime: lambda.Runtime.PYTHON_3_9, +}); + +app.synth(); diff --git a/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts b/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts index 7314f770f6730..98cd47c636c95 100644 --- a/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts +++ b/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts @@ -25,6 +25,7 @@ describe(AppScopedStagingSynthesizer, () => { region: 'us-east-1', }, }); + // TODO: test with tokens }); test('stack template is in asset manifest', () => { @@ -40,7 +41,7 @@ describe(AppScopedStagingSynthesizer, () => { const stackArtifact = asm.getStackArtifact('Stack'); const templateObjectKey = last(stackArtifact.stackTemplateAssetObjectUrl?.split('/')); - expect(stackArtifact.stackTemplateAssetObjectUrl).toEqual(`s3://default-bucket/${templateObjectKey}`); + expect(stackArtifact.stackTemplateAssetObjectUrl).toEqual(`s3://cdk-000000000000-us-east-1/${templateObjectKey}`); // THEN - the template is in the asset manifest const manifestArtifact = asm.artifacts.filter(isAssetManifest)[0]; @@ -53,7 +54,7 @@ describe(AppScopedStagingSynthesizer, () => { source: { path: 'Stack.template.json', packaging: 'file' }, destinations: { '000000000000-us-east-1': { - bucketName: 'default-bucket', + bucketName: 'cdk-000000000000-us-east-1', objectKey: templateObjectKey, region: 'us-east-1', assumeRoleArn: 'arn:' + Aws.PARTITION + ':iam:us-east-1:000000000000:role:cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}', @@ -62,6 +63,20 @@ describe(AppScopedStagingSynthesizer, () => { }); }); + test('stack depends on staging stack', () => { + // WHEN + stack.synthesizer.addFileAsset({ + fileName: __filename, + packaging: FileAssetPackaging.FILE, + sourceHash: 'abcdef', + }); + + // THEN - we have a stack dependency on the staging stack + expect(stack.dependencies.length).toEqual(1); + const depStack = stack.dependencies[0]; + expect(depStack.stackName).toEqual('StagingStack'); + }); + test('add file asset', () => { // WHEN const location = stack.synthesizer.addFileAsset({ @@ -71,8 +86,8 @@ describe(AppScopedStagingSynthesizer, () => { }); // THEN - we have a fixed asset location - expect(evalCFN(location.bucketName)).toEqual('default-bucket'); - expect(evalCFN(location.httpUrl)).toEqual('https://s3.us-east-1.domain.aws/default-bucket/abcdef.js'); + expect(evalCFN(location.bucketName)).toEqual('cdk-000000000000-us-east-1'); + expect(evalCFN(location.httpUrl)).toEqual('https://s3.us-east-1.domain.aws/cdk-000000000000-us-east-1/abcdef.js'); // THEN - object key contains source hash somewhere expect(location.objectKey.indexOf('abcdef')).toBeGreaterThan(-1); diff --git a/packages/@aws-cdk/core-synthesizer/tsconfig.json b/packages/@aws-cdk/core-synthesizer/tsconfig.json index ba8491cbabd52..bc0c185d18c5b 100644 --- a/packages/@aws-cdk/core-synthesizer/tsconfig.json +++ b/packages/@aws-cdk/core-synthesizer/tsconfig.json @@ -40,12 +40,30 @@ { "path": "../core" }, + { + "path": "../cx-api" + }, { "path": "../aws-s3" }, { "path": "../aws-ecr" }, + { + "path": "../aws-iam" + }, + { + "path": "../cloud-assembly-schema" + }, + { + "path": "../integ-runner" + }, + { + "path": "../integ-tests" + }, + { + "path": "../aws-lambda" + }, { "path": "../../../tools/@aws-cdk/cdk-build-tools" }, From 70d6d854df1f0d4e286fec550be7c0ad820d9127 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 15 Mar 2023 18:29:37 -0400 Subject: [PATCH 022/120] better role --- .../lib/default-staging-stack.ts | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/packages/@aws-cdk/core-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/core-synthesizer/lib/default-staging-stack.ts index 7b794523dec06..e14a5da623f2e 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/default-staging-stack.ts @@ -104,7 +104,10 @@ export class DefaultStagingStack extends Stack implements IDefaultStagingStack { private dockerAssetPublishingRoleName: string; constructor(scope: Construct, id: string, props: DefaultStagingStackProps = {}) { - super(scope, id, props); + super(scope, id, { + ...props, + // synthesizer: // TODO: need a synthesizer that will not create an asset for the template + }); this.dependencyStack = this; @@ -158,7 +161,11 @@ export class DefaultStagingStack extends Stack implements IDefaultStagingStack { roleName: roleName, assumedBy: new iam.ServicePrincipal('sts.amazonaws.com'), // TODO actually create correct role }); - role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('AdministratorAccess')); + role.addToPolicy(new iam.PolicyStatement({ + actions: ['s3:GetObject*', 's3:GetBucket*', 's3:GetEncryptionConfiguration', 's3:List*', 's3:DeleteObject*', 's3:PutObject*', 's3:Abort*'], + resources: [this.getCreateBucket().bucketArn, `${this.getCreateBucket().bucketArn}/*`], + effect: iam.Effect.ALLOW, + })); return role; }; @@ -197,15 +204,18 @@ export class DefaultStagingStack extends Stack implements IDefaultStagingStack { }); } - private getCreateBucket(): string { + private getCreateBucket() { // Error: Resolution error: ID components may not include unresolved tokens: const stagingBucketName = this.stagingBucketName ?? 'cdk-489318732371-us-east-1'; //`cdk-${this.account}-${this.region}`; - this.node.tryFindChild(stagingBucketName) as s3.Bucket ?? new s3.Bucket(this, stagingBucketName, { + const bucket = this.node.tryFindChild(stagingBucketName) as s3.Bucket ?? new s3.Bucket(this, stagingBucketName, { bucketName: stagingBucketName, // autoDeleteObjects: true, // this creates a custom resource with an asset removalPolicy: RemovalPolicy.DESTROY, }); - return stagingBucketName; + return { + bucketName: stagingBucketName, + bucketArn: bucket.bucketArn, + }; } /** @@ -228,7 +238,7 @@ export class DefaultStagingStack extends Stack implements IDefaultStagingStack { public addFile(_asset: FileAssetSource): FileAssetInfo { return { - bucketName: this.getCreateBucket(), + bucketName: this.getCreateBucket().bucketName, assumeRoleArn: this.getCreateFilePublishingRole(), }; } From 3a96ee0bff17fd38fdf7af43e5c92afb0476d809 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 16 Mar 2023 17:42:04 -0400 Subject: [PATCH 023/120] major clean up --- .../lib/default-staging-stack.ts | 119 +++++++++--------- .../core-synthesizer/lib/synthesizer.ts | 5 +- .../test/integ.synthesizer.ts | 16 +-- .../core-synthesizer/test/synthesizer.test.ts | 12 +- 4 files changed, 76 insertions(+), 76 deletions(-) diff --git a/packages/@aws-cdk/core-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/core-synthesizer/lib/default-staging-stack.ts index e14a5da623f2e..59bb23b9b5904 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/default-staging-stack.ts @@ -70,22 +70,30 @@ export interface DefaultStagingStackProps extends StackProps { * * @default - a well-known name unique to this app/env. */ - readonly dockerAssetPublishingRoleName?: string; + readonly dockerAssetPublishingRole?: BootstrapRole; } /** * A default Staging Stack */ export class DefaultStagingStack extends Stack implements IDefaultStagingStack { + private get AppId() { + return `${this.account}-${this.region}-${this.stackName.toLocaleLowerCase()}`; + } + /** * Default asset publishing role name for file (S3) assets. */ - private static readonly DEFAULT_FILE_ASSET_PUBLISHING_ROLE_NAME = 'cdk-file-publishing-role'; + private get DEFAULT_FILE_ASSET_PUBLISHING_ROLE_NAME() { + return `cdk-file-publishing-role-${this.AppId}`.slice(0, 63); + } /** * Default asset publishing role name for docker (ECR) assets. */ - private static readonly DEFAULT_DOCKER_ASSET_PUBISHING_ROLE_NAME = 'cdk-${Qualifier}-asset-publishing-role-${AWS::AccountId}-${AWS::Region}'; + private get DEFAULT_DOCKER_ASSET_PUBISHING_ROLE_NAME() { + return `cdk-asset-publishing-role-${this.AppId}`.slice(0, 63); + } /** * The app-scoped, evironment-keyed staging bucket. @@ -101,7 +109,7 @@ export class DefaultStagingStack extends Stack implements IDefaultStagingStack { private readonly stagingBucketName?: string; private fileAssetPublishingRole?: BootstrapRole; - private dockerAssetPublishingRoleName: string; + private dockerAssetPublishingRole?: BootstrapRole; constructor(scope: Construct, id: string, props: DefaultStagingStackProps = {}) { super(scope, id, { @@ -112,75 +120,55 @@ export class DefaultStagingStack extends Stack implements IDefaultStagingStack { this.dependencyStack = this; this.stagingBucketName = props.stagingBucketName; - // eslint-disable-next-line max-len this.fileAssetPublishingRole = props.fileAssetPublishingRole; - this.dockerAssetPublishingRoleName = props.dockerAssetPublishingRoleName ?? DefaultStagingStack.DEFAULT_DOCKER_ASSET_PUBISHING_ROLE_NAME; + this.dockerAssetPublishingRole = props.dockerAssetPublishingRole; this.stagingRepos = {}; } /** - * Returns the well-known name of the file publishing role + * Returns the well-known arn of the file publishing role */ private getCreateFilePublishingRole() { - // FilePublishingRoleDefaultPolicy: - // Type: AWS::IAM::Policy - // Properties: - // PolicyDocument: - // Statement: - // - Action: - // - s3:GetObject* - // - s3:GetBucket* - // - s3:GetEncryptionConfiguration - // - s3:List* - // - s3:DeleteObject* - // - s3:PutObject* - // - s3:Abort* - // Resource: - // - Fn::Sub: "${StagingBucket.Arn}" - // - Fn::Sub: "${StagingBucket.Arn}/*" - // Effect: Allow - // - Action: - // - kms:Decrypt - // - kms:DescribeKey - // - kms:Encrypt - // - kms:ReEncrypt* - // - kms:GenerateDataKey* - // Effect: Allow - // Resource: - // Fn::If: - // - CreateNewKey - // - Fn::Sub: "${FileAssetsBucketEncryptionKey.Arn}" - // - Fn::Sub: arn:${AWS::Partition}:kms:${AWS::Region}:${AWS::AccountId}:key/${FileAssetsBucketKmsKeyId} - // Version: '2012-10-17' - // Roles: - // - Ref: FilePublishingRole - // PolicyName: - // Fn::Sub: cdk-${Qualifier}-file-publishing-role-default-policy-${AWS::AccountId}-${AWS::Region} + if (this.fileAssetPublishingRole) { + return this.fileAssetPublishingRole.roleArn; + } + + const roleId = 'CdkFilePublishingRole'; + const roleName = this.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_NAME; + const createIamRole = () => { - const role = new iam.Role(this, roleName, { + const role = new iam.Role(this, roleId, { roleName: roleName, - assumedBy: new iam.ServicePrincipal('sts.amazonaws.com'), // TODO actually create correct role + assumedBy: new iam.ServicePrincipal('sts.amazonaws.com'), }); role.addToPolicy(new iam.PolicyStatement({ - actions: ['s3:GetObject*', 's3:GetBucket*', 's3:GetEncryptionConfiguration', 's3:List*', 's3:DeleteObject*', 's3:PutObject*', 's3:Abort*'], - resources: [this.getCreateBucket().bucketArn, `${this.getCreateBucket().bucketArn}/*`], + actions: [ + 's3:GetObject*', + 's3:GetBucket*', + 's3:GetEncryptionConfiguration', + 's3:List*', + 's3:DeleteObject*', + 's3:PutObject*', + 's3:Abort*', + ], + resources: [ + this.getCreateBucket().bucketArn, + `${this.getCreateBucket().bucketArn}/*`, + ], effect: iam.Effect.ALLOW, })); return role; }; - if (this.fileAssetPublishingRole) { - return this.fileAssetPublishingRole.roleArn; - } - const roleName = DefaultStagingStack.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_NAME; - this.node.tryFindChild(roleName) as iam.Role ?? createIamRole(); + // Create the default role if it does not exist yet + this.node.tryFindChild(roleId) as iam.Role ?? createIamRole(); return Arn.format({ partition: this.partition ?? Aws.PARTITION, account: this.account ?? Aws.ACCOUNT_ID, region: this.region ?? Aws.REGION, service: 'iam', resource: 'role', - resourceName: DefaultStagingStack.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_NAME, + resourceName: roleName, arnFormat: ArnFormat.COLON_RESOURCE_NAME, }); } @@ -189,25 +177,40 @@ export class DefaultStagingStack extends Stack implements IDefaultStagingStack { * Returns the well-known name of the image publishing role */ private getCreateImagePublishingRole() { - this.node.tryFindChild(this.dockerAssetPublishingRoleName) as iam.Role ?? new iam.Role(this, this.dockerAssetPublishingRoleName, { - roleName: DefaultStagingStack.DEFAULT_DOCKER_ASSET_PUBISHING_ROLE_NAME, - assumedBy: new iam.ServicePrincipal('sts.amazonaws.com'), // TODO actually create correct role - }); + if (this.dockerAssetPublishingRole) { + return this.dockerAssetPublishingRole.roleArn; + } + + const roleId = 'CdkDockerAssetPublishingRole'; + const roleName = this.DEFAULT_DOCKER_ASSET_PUBISHING_ROLE_NAME; + + const createIamRole = () => { + const role = new iam.Role(this, roleId, { + roleName: roleName, + assumedBy: new iam.ServicePrincipal('sts.amazonaws.com'), + }); + role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('AdministratorAccess')); + return role; + }; + + this.node.tryFindChild(roleId) as iam.Role ?? createIamRole(); + return Arn.format({ partition: this.partition ?? Aws.PARTITION, account: this.account ?? Aws.ACCOUNT_ID, region: this.region ?? Aws.REGION, service: 'iam', resource: 'role', - resourceName: DefaultStagingStack.DEFAULT_DOCKER_ASSET_PUBISHING_ROLE_NAME, + resourceName: roleName, arnFormat: ArnFormat.COLON_RESOURCE_NAME, }); } private getCreateBucket() { // Error: Resolution error: ID components may not include unresolved tokens: - const stagingBucketName = this.stagingBucketName ?? 'cdk-489318732371-us-east-1'; //`cdk-${this.account}-${this.region}`; - const bucket = this.node.tryFindChild(stagingBucketName) as s3.Bucket ?? new s3.Bucket(this, stagingBucketName, { + const stagingBucketName = this.stagingBucketName ?? `cdk-${this.AppId}`; + const bucketId = 'CdkStagingBucket'; + const bucket = this.node.tryFindChild(bucketId) as s3.Bucket ?? new s3.Bucket(this, bucketId, { bucketName: stagingBucketName, // autoDeleteObjects: true, // this creates a custom resource with an asset removalPolicy: RemovalPolicy.DESTROY, diff --git a/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts index fc263f1afe11b..a091c5e5acadf 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts @@ -156,8 +156,9 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta private getCreateStagingStack(app: Stage, env: Environment): IStagingStack { // TODO: env could be tokens - const stackName = `StagingStack${app.stageName}`; - const stagingStack = app.node.tryFindChild(stackName) as DefaultStagingStack ?? new DefaultStagingStack(app, stackName, { + const stackName = `StagingStack${app.node.addr.slice(0, 10)}`; + const stackId = 'StagingStack'; + const stagingStack = app.node.tryFindChild(stackId) as DefaultStagingStack ?? new DefaultStagingStack(app, stackId, { env, stackName, }); diff --git a/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.ts b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.ts index ec5b72006bc78..2191978e932b7 100644 --- a/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.ts +++ b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.ts @@ -1,20 +1,16 @@ import * as path from 'path'; import * as lambda from '@aws-cdk/aws-lambda'; import { App, Stack } from '@aws-cdk/core'; -import { AppScopedStagingSynthesizer, DefaultStagingStack } from '../lib'; +import { AppScopedStagingSynthesizer } from '../lib'; const app = new App(); const stack = new Stack(app, 'app-scoped-staging-test', { - synthesizer: new AppScopedStagingSynthesizer({ - stagingStack: new DefaultStagingStack(app, 'StagingStackz', { - // fileAssetPublishingRole: BootstrapRole.fromRoleArn('arn:aws:iam::489318732371:role/cdk-0zn5h5s73a-file-publishing-role-489318732371-us-east-1'), - }), - }), - env: { - account: '489318732371', - region: 'us-east-1', - }, + synthesizer: new AppScopedStagingSynthesizer(), + // env: { + // account: '489318732371', + // region: 'us-east-1', + // }, }); new lambda.Function(stack, 'lambda', { diff --git a/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts b/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts index 98cd47c636c95..eada97c6171b5 100644 --- a/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts +++ b/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts @@ -41,7 +41,7 @@ describe(AppScopedStagingSynthesizer, () => { const stackArtifact = asm.getStackArtifact('Stack'); const templateObjectKey = last(stackArtifact.stackTemplateAssetObjectUrl?.split('/')); - expect(stackArtifact.stackTemplateAssetObjectUrl).toEqual(`s3://cdk-000000000000-us-east-1/${templateObjectKey}`); + expect(stackArtifact.stackTemplateAssetObjectUrl).toEqual(`s3://cdk-000000000000-us-east-1-stagingstackc8adc83b19/${templateObjectKey}`); // THEN - the template is in the asset manifest const manifestArtifact = asm.artifacts.filter(isAssetManifest)[0]; @@ -54,10 +54,10 @@ describe(AppScopedStagingSynthesizer, () => { source: { path: 'Stack.template.json', packaging: 'file' }, destinations: { '000000000000-us-east-1': { - bucketName: 'cdk-000000000000-us-east-1', + bucketName: 'cdk-000000000000-us-east-1-stagingstackc8adc83b19', objectKey: templateObjectKey, region: 'us-east-1', - assumeRoleArn: 'arn:' + Aws.PARTITION + ':iam:us-east-1:000000000000:role:cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}', + assumeRoleArn: 'arn:' + Aws.PARTITION + ':iam:us-east-1:000000000000:role:cdk-file-publishing-role-000000000000-us-east-1-stagingstackc8a', }, }, }); @@ -74,7 +74,7 @@ describe(AppScopedStagingSynthesizer, () => { // THEN - we have a stack dependency on the staging stack expect(stack.dependencies.length).toEqual(1); const depStack = stack.dependencies[0]; - expect(depStack.stackName).toEqual('StagingStack'); + expect(depStack.stackName).toEqual('StagingStackc8adc83b19'); }); test('add file asset', () => { @@ -86,8 +86,8 @@ describe(AppScopedStagingSynthesizer, () => { }); // THEN - we have a fixed asset location - expect(evalCFN(location.bucketName)).toEqual('cdk-000000000000-us-east-1'); - expect(evalCFN(location.httpUrl)).toEqual('https://s3.us-east-1.domain.aws/cdk-000000000000-us-east-1/abcdef.js'); + expect(evalCFN(location.bucketName)).toEqual('cdk-000000000000-us-east-1-stagingstackc8adc83b19'); + expect(evalCFN(location.httpUrl)).toEqual('https://s3.us-east-1.domain.aws/cdk-000000000000-us-east-1-stagingstackc8adc83b19/abcdef.js'); // THEN - object key contains source hash somewhere expect(location.objectKey.indexOf('abcdef')).toBeGreaterThan(-1); From b7e0b6a6ca69a7caadf7acda130d431a0f4b1a1d Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 16 Mar 2023 17:56:16 -0400 Subject: [PATCH 024/120] use bootstrapless synth --- .../@aws-cdk/core-synthesizer/lib/default-staging-stack.ts | 7 +++++-- .../lib/stack-synthesizers/bootstrapless-synthesizer.ts | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/core-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/core-synthesizer/lib/default-staging-stack.ts index 59bb23b9b5904..26f1323c49a22 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/default-staging-stack.ts @@ -1,7 +1,7 @@ import * as ecr from '@aws-cdk/aws-ecr'; import * as iam from '@aws-cdk/aws-iam'; import * as s3 from '@aws-cdk/aws-s3'; -import { Arn, ArnFormat, Aws, DockerImageAssetSource, FileAssetSource, RemovalPolicy, Stack, StackProps } from '@aws-cdk/core'; +import { Arn, ArnFormat, Aws, BootstraplessSynthesizer, DockerImageAssetSource, FileAssetSource, RemovalPolicy, Stack, StackProps } from '@aws-cdk/core'; import { Construct, IConstruct } from 'constructs'; import { BootstrapRole } from './synthesizer'; @@ -77,6 +77,9 @@ export interface DefaultStagingStackProps extends StackProps { * A default Staging Stack */ export class DefaultStagingStack extends Stack implements IDefaultStagingStack { + /** + * An identifier unique to the App. + */ private get AppId() { return `${this.account}-${this.region}-${this.stackName.toLocaleLowerCase()}`; } @@ -114,7 +117,7 @@ export class DefaultStagingStack extends Stack implements IDefaultStagingStack { constructor(scope: Construct, id: string, props: DefaultStagingStackProps = {}) { super(scope, id, { ...props, - // synthesizer: // TODO: need a synthesizer that will not create an asset for the template + synthesizer: new BootstraplessSynthesizer(), }); this.dependencyStack = this; diff --git a/packages/@aws-cdk/core/lib/stack-synthesizers/bootstrapless-synthesizer.ts b/packages/@aws-cdk/core/lib/stack-synthesizers/bootstrapless-synthesizer.ts index ac1095c81f4ee..f9d949ff712b6 100644 --- a/packages/@aws-cdk/core/lib/stack-synthesizers/bootstrapless-synthesizer.ts +++ b/packages/@aws-cdk/core/lib/stack-synthesizers/bootstrapless-synthesizer.ts @@ -41,7 +41,7 @@ export interface BootstraplessSynthesizerProps { * synthesizer directly. */ export class BootstraplessSynthesizer extends DefaultStackSynthesizer { - constructor(props: BootstraplessSynthesizerProps) { + constructor(props: BootstraplessSynthesizerProps = {}) { super({ deployRoleArn: props.deployRoleArn, cloudFormationExecutionRole: props.cloudFormationExecutionRoleArn, From b3eb0a60eabd91eee522f2007928b8489fa92e93 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Fri, 17 Mar 2023 15:27:36 -0400 Subject: [PATCH 025/120] expose appId to user --- .../lib/default-staging-stack.ts | 38 ++++++++----------- .../core-synthesizer/lib/synthesizer.ts | 16 +++++--- .../test/integ.synthesizer.ts | 4 +- .../core-synthesizer/test/synthesizer.test.ts | 19 ++++++---- 4 files changed, 40 insertions(+), 37 deletions(-) diff --git a/packages/@aws-cdk/core-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/core-synthesizer/lib/default-staging-stack.ts index 26f1323c49a22..b2e9fd97f12c5 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/default-staging-stack.ts @@ -18,6 +18,11 @@ export interface DockerAssetInfo { * Information on how a Staging Stack should look. */ export interface IDefaultStagingStack extends IConstruct { + /** + * App level unique identifier + */ + readonly appId: string; + /** * The app-scoped, environment-keyed bucket created in this staging stack. */ @@ -29,13 +34,6 @@ export interface IDefaultStagingStack extends IConstruct { */ readonly stagingRepos: Record; - /** - * Stack others can depend on. - * - * @default this - */ - readonly dependencyStack: Stack; - /** * // TODO */ @@ -71,31 +69,29 @@ export interface DefaultStagingStackProps extends StackProps { * @default - a well-known name unique to this app/env. */ readonly dockerAssetPublishingRole?: BootstrapRole; + + /** + * Application identifier unique to the app. + */ + readonly appId: string; } /** * A default Staging Stack */ export class DefaultStagingStack extends Stack implements IDefaultStagingStack { - /** - * An identifier unique to the App. - */ - private get AppId() { - return `${this.account}-${this.region}-${this.stackName.toLocaleLowerCase()}`; - } - /** * Default asset publishing role name for file (S3) assets. */ private get DEFAULT_FILE_ASSET_PUBLISHING_ROLE_NAME() { - return `cdk-file-publishing-role-${this.AppId}`.slice(0, 63); + return `cdk-file-publishing-role-${this.region}-${this.appId}`.slice(0, 63); } /** * Default asset publishing role name for docker (ECR) assets. */ private get DEFAULT_DOCKER_ASSET_PUBISHING_ROLE_NAME() { - return `cdk-asset-publishing-role-${this.AppId}`.slice(0, 63); + return `cdk-asset-publishing-role-${this.region}-${this.appId}`.slice(0, 63); } /** @@ -108,19 +104,18 @@ export class DefaultStagingStack extends Stack implements IDefaultStagingStack { */ public readonly stagingRepos: Record; - public readonly dependencyStack: Stack; - + public readonly appId: string; private readonly stagingBucketName?: string; private fileAssetPublishingRole?: BootstrapRole; private dockerAssetPublishingRole?: BootstrapRole; - constructor(scope: Construct, id: string, props: DefaultStagingStackProps = {}) { + constructor(scope: Construct, id: string, props: DefaultStagingStackProps) { super(scope, id, { ...props, synthesizer: new BootstraplessSynthesizer(), }); - this.dependencyStack = this; + this.appId = props.appId; this.stagingBucketName = props.stagingBucketName; this.fileAssetPublishingRole = props.fileAssetPublishingRole; @@ -211,11 +206,10 @@ export class DefaultStagingStack extends Stack implements IDefaultStagingStack { private getCreateBucket() { // Error: Resolution error: ID components may not include unresolved tokens: - const stagingBucketName = this.stagingBucketName ?? `cdk-${this.AppId}`; + const stagingBucketName = this.stagingBucketName ?? `cdk-${this.account}-${this.region}-${this.appId.toLocaleLowerCase()}`; const bucketId = 'CdkStagingBucket'; const bucket = this.node.tryFindChild(bucketId) as s3.Bucket ?? new s3.Bucket(this, bucketId, { bucketName: stagingBucketName, - // autoDeleteObjects: true, // this creates a custom resource with an asset removalPolicy: RemovalPolicy.DESTROY, }); return { diff --git a/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts index a091c5e5acadf..37c4a1530b2a6 100644 --- a/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts +++ b/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts @@ -64,13 +64,15 @@ export interface StagingStackSynthesizerProps { * @default - default role */ readonly lookupRole?: BootstrapRole; + + readonly appId: string; } /** * New Stack Synthesizer */ export class AppScopedStagingSynthesizer extends StackSynthesizer implements IReusableStackSynthesizer { - constructor(private readonly props: StagingStackSynthesizerProps = {}) { + constructor(private readonly props: StagingStackSynthesizerProps) { super(); for (const key in props) { @@ -136,13 +138,15 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta private stagingStack: IStagingStack; private assetManifest = new AssetManifestBuilder(); - private lookupRoleArn: string; + private readonly lookupRoleArn: string; + private readonly appId: string; - constructor(private readonly stack: Stack, props: StagingStackSynthesizerProps = {}) { + constructor(private readonly stack: Stack, props: StagingStackSynthesizerProps) { super(); super.bind(stack); - this.lookupRoleArn = props.lookupRole?.roleArn ?? BoundStagingStackSynthesizer.DEFAULT_LOOKUP_ROLE_ARN; + this.lookupRoleArn = props.lookupRole ? props.lookupRole.roleArn : BoundStagingStackSynthesizer.DEFAULT_LOOKUP_ROLE_ARN; + this.appId = props.appId; const app = App.of(stack); if (!app) { @@ -155,12 +159,12 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta } private getCreateStagingStack(app: Stage, env: Environment): IStagingStack { - // TODO: env could be tokens - const stackName = `StagingStack${app.node.addr.slice(0, 10)}`; + const stackName = `StagingStack${this.appId}`; const stackId = 'StagingStack'; const stagingStack = app.node.tryFindChild(stackId) as DefaultStagingStack ?? new DefaultStagingStack(app, stackId, { env, stackName, + appId: this.appId, }); this.stack.addDependency(stagingStack, 'reason'); diff --git a/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.ts b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.ts index 2191978e932b7..c67da4d936c3c 100644 --- a/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.ts +++ b/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.ts @@ -6,7 +6,9 @@ import { AppScopedStagingSynthesizer } from '../lib'; const app = new App(); const stack = new Stack(app, 'app-scoped-staging-test', { - synthesizer: new AppScopedStagingSynthesizer(), + synthesizer: new AppScopedStagingSynthesizer({ + appId: '1', + }), // env: { // account: '489318732371', // region: 'us-east-1', diff --git a/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts b/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts index eada97c6171b5..71cb9a3a22af6 100644 --- a/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts +++ b/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts @@ -10,6 +10,7 @@ const CFN_CONTEXT = { 'AWS::AccountId': 'the_account', 'AWS::URLSuffix': 'domain.aws', }; +const APP_ID = 'appId'; describe(AppScopedStagingSynthesizer, () => { let app: App; @@ -17,7 +18,9 @@ describe(AppScopedStagingSynthesizer, () => { beforeEach(() => { app = new App({ - defaultStackSynthesizer: new AppScopedStagingSynthesizer(), + defaultStackSynthesizer: new AppScopedStagingSynthesizer({ + appId: APP_ID, + }), }); stack = new Stack(app, 'Stack', { env: { @@ -41,7 +44,7 @@ describe(AppScopedStagingSynthesizer, () => { const stackArtifact = asm.getStackArtifact('Stack'); const templateObjectKey = last(stackArtifact.stackTemplateAssetObjectUrl?.split('/')); - expect(stackArtifact.stackTemplateAssetObjectUrl).toEqual(`s3://cdk-000000000000-us-east-1-stagingstackc8adc83b19/${templateObjectKey}`); + expect(stackArtifact.stackTemplateAssetObjectUrl).toEqual(`s3://cdk-000000000000-us-east-1-${APP_ID.toLocaleLowerCase()}/${templateObjectKey}`); // THEN - the template is in the asset manifest const manifestArtifact = asm.artifacts.filter(isAssetManifest)[0]; @@ -54,10 +57,10 @@ describe(AppScopedStagingSynthesizer, () => { source: { path: 'Stack.template.json', packaging: 'file' }, destinations: { '000000000000-us-east-1': { - bucketName: 'cdk-000000000000-us-east-1-stagingstackc8adc83b19', + bucketName: `cdk-000000000000-us-east-1-${APP_ID.toLocaleLowerCase()}`, objectKey: templateObjectKey, region: 'us-east-1', - assumeRoleArn: 'arn:' + Aws.PARTITION + ':iam:us-east-1:000000000000:role:cdk-file-publishing-role-000000000000-us-east-1-stagingstackc8a', + assumeRoleArn: `arn:${Aws.PARTITION}:iam:us-east-1:000000000000:role:cdk-file-publishing-role-us-east-1-${APP_ID}`, }, }, }); @@ -74,7 +77,7 @@ describe(AppScopedStagingSynthesizer, () => { // THEN - we have a stack dependency on the staging stack expect(stack.dependencies.length).toEqual(1); const depStack = stack.dependencies[0]; - expect(depStack.stackName).toEqual('StagingStackc8adc83b19'); + expect(depStack.stackName).toEqual(`StagingStack${APP_ID}`); }); test('add file asset', () => { @@ -86,8 +89,8 @@ describe(AppScopedStagingSynthesizer, () => { }); // THEN - we have a fixed asset location - expect(evalCFN(location.bucketName)).toEqual('cdk-000000000000-us-east-1-stagingstackc8adc83b19'); - expect(evalCFN(location.httpUrl)).toEqual('https://s3.us-east-1.domain.aws/cdk-000000000000-us-east-1-stagingstackc8adc83b19/abcdef.js'); + expect(evalCFN(location.bucketName)).toEqual(`cdk-000000000000-us-east-1-${APP_ID.toLocaleLowerCase()}`); + expect(evalCFN(location.httpUrl)).toEqual(`https://s3.us-east-1.domain.aws/cdk-000000000000-us-east-1-${APP_ID.toLocaleLowerCase()}/abcdef.js`); // THEN - object key contains source hash somewhere expect(location.objectKey.indexOf('abcdef')).toBeGreaterThan(-1); @@ -161,7 +164,7 @@ describe(AppScopedStagingSynthesizer, () => { }); // THEN - we have a fixed asset location - console.log(location.bucketName); + console.log(stack.dependencies); }); /** From 26985ab42ca614c266b1d19261f032e2df766e03 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Fri, 17 Mar 2023 15:29:58 -0400 Subject: [PATCH 026/120] rename folder --- .../.eslintrc.js | 0 .../.gitignore | 0 .../.npmignore | 0 .../LICENSE | 0 .../NOTICE | 0 .../README.md | 0 .../jest.config.js | 0 .../lib/default-staging-stack.ts | 0 .../lib/index.ts | 0 .../lib/synthesizer.ts | 0 .../package.json | 0 .../test/assets/index.py | 0 .../StagingStack.assets.json | 0 .../StagingStack.template.json | 0 .../app-scoped-staging-test.assets.json | 0 .../app-scoped-staging-test.template.json | 0 .../__entrypoint__.js | 0 .../index.js | 0 .../integ.synthesizer.js.snapshot/cdk.out | 0 .../integ.synthesizer.js.snapshot/integ.json | 0 .../manifest.json | 0 ...efaultTestDeployAssert208D3414.assets.json | 0 ...aultTestDeployAssert208D3414.template.json | 0 .../integ.synthesizer.js.snapshot/tree.json | 0 .../test/integ.synthesizer.ts | 0 .../test/synthesizer.test.ts | 0 .../@aws-cdk/core-synthesizer/tsconfig.json | 75 ------------------- 27 files changed, 75 deletions(-) rename packages/@aws-cdk/{core-synthesizer => core-app-scoped-synthesizer}/.eslintrc.js (100%) rename packages/@aws-cdk/{core-synthesizer => core-app-scoped-synthesizer}/.gitignore (100%) rename packages/@aws-cdk/{core-synthesizer => core-app-scoped-synthesizer}/.npmignore (100%) rename packages/@aws-cdk/{core-synthesizer => core-app-scoped-synthesizer}/LICENSE (100%) rename packages/@aws-cdk/{core-synthesizer => core-app-scoped-synthesizer}/NOTICE (100%) rename packages/@aws-cdk/{core-synthesizer => core-app-scoped-synthesizer}/README.md (100%) rename packages/@aws-cdk/{core-synthesizer => core-app-scoped-synthesizer}/jest.config.js (100%) rename packages/@aws-cdk/{core-synthesizer => core-app-scoped-synthesizer}/lib/default-staging-stack.ts (100%) rename packages/@aws-cdk/{core-synthesizer => core-app-scoped-synthesizer}/lib/index.ts (100%) rename packages/@aws-cdk/{core-synthesizer => core-app-scoped-synthesizer}/lib/synthesizer.ts (100%) rename packages/@aws-cdk/{core-synthesizer => core-app-scoped-synthesizer}/package.json (100%) rename packages/@aws-cdk/{core-synthesizer => core-app-scoped-synthesizer}/test/assets/index.py (100%) rename packages/@aws-cdk/{core-synthesizer => core-app-scoped-synthesizer}/test/integ.synthesizer.js.snapshot/StagingStack.assets.json (100%) rename packages/@aws-cdk/{core-synthesizer => core-app-scoped-synthesizer}/test/integ.synthesizer.js.snapshot/StagingStack.template.json (100%) rename packages/@aws-cdk/{core-synthesizer => core-app-scoped-synthesizer}/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json (100%) rename packages/@aws-cdk/{core-synthesizer => core-app-scoped-synthesizer}/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json (100%) rename packages/@aws-cdk/{core-synthesizer => core-app-scoped-synthesizer}/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js (100%) rename packages/@aws-cdk/{core-synthesizer => core-app-scoped-synthesizer}/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js (100%) rename packages/@aws-cdk/{core-synthesizer => core-app-scoped-synthesizer}/test/integ.synthesizer.js.snapshot/cdk.out (100%) rename packages/@aws-cdk/{core-synthesizer => core-app-scoped-synthesizer}/test/integ.synthesizer.js.snapshot/integ.json (100%) rename packages/@aws-cdk/{core-synthesizer => core-app-scoped-synthesizer}/test/integ.synthesizer.js.snapshot/manifest.json (100%) rename packages/@aws-cdk/{core-synthesizer => core-app-scoped-synthesizer}/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json (100%) rename packages/@aws-cdk/{core-synthesizer => core-app-scoped-synthesizer}/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json (100%) rename packages/@aws-cdk/{core-synthesizer => core-app-scoped-synthesizer}/test/integ.synthesizer.js.snapshot/tree.json (100%) rename packages/@aws-cdk/{core-synthesizer => core-app-scoped-synthesizer}/test/integ.synthesizer.ts (100%) rename packages/@aws-cdk/{core-synthesizer => core-app-scoped-synthesizer}/test/synthesizer.test.ts (100%) delete mode 100644 packages/@aws-cdk/core-synthesizer/tsconfig.json diff --git a/packages/@aws-cdk/core-synthesizer/.eslintrc.js b/packages/@aws-cdk/core-app-scoped-synthesizer/.eslintrc.js similarity index 100% rename from packages/@aws-cdk/core-synthesizer/.eslintrc.js rename to packages/@aws-cdk/core-app-scoped-synthesizer/.eslintrc.js diff --git a/packages/@aws-cdk/core-synthesizer/.gitignore b/packages/@aws-cdk/core-app-scoped-synthesizer/.gitignore similarity index 100% rename from packages/@aws-cdk/core-synthesizer/.gitignore rename to packages/@aws-cdk/core-app-scoped-synthesizer/.gitignore diff --git a/packages/@aws-cdk/core-synthesizer/.npmignore b/packages/@aws-cdk/core-app-scoped-synthesizer/.npmignore similarity index 100% rename from packages/@aws-cdk/core-synthesizer/.npmignore rename to packages/@aws-cdk/core-app-scoped-synthesizer/.npmignore diff --git a/packages/@aws-cdk/core-synthesizer/LICENSE b/packages/@aws-cdk/core-app-scoped-synthesizer/LICENSE similarity index 100% rename from packages/@aws-cdk/core-synthesizer/LICENSE rename to packages/@aws-cdk/core-app-scoped-synthesizer/LICENSE diff --git a/packages/@aws-cdk/core-synthesizer/NOTICE b/packages/@aws-cdk/core-app-scoped-synthesizer/NOTICE similarity index 100% rename from packages/@aws-cdk/core-synthesizer/NOTICE rename to packages/@aws-cdk/core-app-scoped-synthesizer/NOTICE diff --git a/packages/@aws-cdk/core-synthesizer/README.md b/packages/@aws-cdk/core-app-scoped-synthesizer/README.md similarity index 100% rename from packages/@aws-cdk/core-synthesizer/README.md rename to packages/@aws-cdk/core-app-scoped-synthesizer/README.md diff --git a/packages/@aws-cdk/core-synthesizer/jest.config.js b/packages/@aws-cdk/core-app-scoped-synthesizer/jest.config.js similarity index 100% rename from packages/@aws-cdk/core-synthesizer/jest.config.js rename to packages/@aws-cdk/core-app-scoped-synthesizer/jest.config.js diff --git a/packages/@aws-cdk/core-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/core-app-scoped-synthesizer/lib/default-staging-stack.ts similarity index 100% rename from packages/@aws-cdk/core-synthesizer/lib/default-staging-stack.ts rename to packages/@aws-cdk/core-app-scoped-synthesizer/lib/default-staging-stack.ts diff --git a/packages/@aws-cdk/core-synthesizer/lib/index.ts b/packages/@aws-cdk/core-app-scoped-synthesizer/lib/index.ts similarity index 100% rename from packages/@aws-cdk/core-synthesizer/lib/index.ts rename to packages/@aws-cdk/core-app-scoped-synthesizer/lib/index.ts diff --git a/packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/core-app-scoped-synthesizer/lib/synthesizer.ts similarity index 100% rename from packages/@aws-cdk/core-synthesizer/lib/synthesizer.ts rename to packages/@aws-cdk/core-app-scoped-synthesizer/lib/synthesizer.ts diff --git a/packages/@aws-cdk/core-synthesizer/package.json b/packages/@aws-cdk/core-app-scoped-synthesizer/package.json similarity index 100% rename from packages/@aws-cdk/core-synthesizer/package.json rename to packages/@aws-cdk/core-app-scoped-synthesizer/package.json diff --git a/packages/@aws-cdk/core-synthesizer/test/assets/index.py b/packages/@aws-cdk/core-app-scoped-synthesizer/test/assets/index.py similarity index 100% rename from packages/@aws-cdk/core-synthesizer/test/assets/index.py rename to packages/@aws-cdk/core-app-scoped-synthesizer/test/assets/index.py diff --git a/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.assets.json b/packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.assets.json similarity index 100% rename from packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.assets.json rename to packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.assets.json diff --git a/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.template.json b/packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.template.json similarity index 100% rename from packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.template.json rename to packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.template.json diff --git a/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json b/packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json similarity index 100% rename from packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json rename to packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json diff --git a/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json b/packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json similarity index 100% rename from packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json rename to packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json diff --git a/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js b/packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js similarity index 100% rename from packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js rename to packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js diff --git a/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js b/packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js similarity index 100% rename from packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js rename to packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js diff --git a/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/cdk.out b/packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/cdk.out similarity index 100% rename from packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/cdk.out rename to packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/cdk.out diff --git a/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/integ.json b/packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/integ.json similarity index 100% rename from packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/integ.json rename to packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/integ.json diff --git a/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json b/packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json similarity index 100% rename from packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json rename to packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json diff --git a/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json b/packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json similarity index 100% rename from packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json rename to packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json diff --git a/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json b/packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json similarity index 100% rename from packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json rename to packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json diff --git a/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/tree.json b/packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/tree.json similarity index 100% rename from packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js.snapshot/tree.json rename to packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/tree.json diff --git a/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.ts b/packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.ts similarity index 100% rename from packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.ts rename to packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.ts diff --git a/packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts b/packages/@aws-cdk/core-app-scoped-synthesizer/test/synthesizer.test.ts similarity index 100% rename from packages/@aws-cdk/core-synthesizer/test/synthesizer.test.ts rename to packages/@aws-cdk/core-app-scoped-synthesizer/test/synthesizer.test.ts diff --git a/packages/@aws-cdk/core-synthesizer/tsconfig.json b/packages/@aws-cdk/core-synthesizer/tsconfig.json deleted file mode 100644 index bc0c185d18c5b..0000000000000 --- a/packages/@aws-cdk/core-synthesizer/tsconfig.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "compilerOptions": { - "declarationMap": false, - "inlineSourceMap": true, - "inlineSources": true, - "alwaysStrict": true, - "charset": "utf8", - "declaration": true, - "experimentalDecorators": true, - "incremental": true, - "lib": [ - "es2020" - ], - "module": "CommonJS", - "newLine": "lf", - "noEmitOnError": true, - "noFallthroughCasesInSwitch": true, - "noImplicitAny": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "strict": true, - "strictNullChecks": true, - "strictPropertyInitialization": true, - "stripInternal": false, - "target": "ES2020", - "composite": true, - "tsBuildInfoFile": "tsconfig.tsbuildinfo" - }, - "include": [ - "**/*.ts" - ], - "exclude": [ - "node_modules" - ], - "references": [ - { - "path": "../core" - }, - { - "path": "../cx-api" - }, - { - "path": "../aws-s3" - }, - { - "path": "../aws-ecr" - }, - { - "path": "../aws-iam" - }, - { - "path": "../cloud-assembly-schema" - }, - { - "path": "../integ-runner" - }, - { - "path": "../integ-tests" - }, - { - "path": "../aws-lambda" - }, - { - "path": "../../../tools/@aws-cdk/cdk-build-tools" - }, - { - "path": "../../../tools/@aws-cdk/pkglint" - } - ], - "_generated_by_jsii_": "Generated by jsii - safe to delete, and ideally should be in .gitignore" -} From f1462dd034793056a5e5f8045f94f6fe453ff079 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Fri, 17 Mar 2023 15:32:58 -0400 Subject: [PATCH 027/120] rename --- .../.eslintrc.js | 0 .../.gitignore | 0 .../.npmignore | 0 .../LICENSE | 0 .../NOTICE | 0 .../README.md | 0 .../jest.config.js | 0 .../lib/default-staging-stack.ts | 0 .../lib/index.ts | 0 .../lib/synthesizer.ts | 0 .../package.json | 19 ++++++++++--------- .../test/assets/index.py | 0 .../StagingStack.assets.json | 0 .../StagingStack.template.json | 0 .../app-scoped-staging-test.assets.json | 0 .../app-scoped-staging-test.template.json | 0 .../__entrypoint__.js | 0 .../index.js | 0 .../integ.synthesizer.js.snapshot/cdk.out | 0 .../integ.synthesizer.js.snapshot/integ.json | 0 .../manifest.json | 0 ...efaultTestDeployAssert208D3414.assets.json | 0 ...aultTestDeployAssert208D3414.template.json | 0 .../integ.synthesizer.js.snapshot/tree.json | 0 .../test/integ.synthesizer.ts | 0 .../test/synthesizer.test.ts | 0 26 files changed, 10 insertions(+), 9 deletions(-) rename packages/@aws-cdk/{core-app-scoped-synthesizer => core-app-scoped-staging-synthesizer}/.eslintrc.js (100%) rename packages/@aws-cdk/{core-app-scoped-synthesizer => core-app-scoped-staging-synthesizer}/.gitignore (100%) rename packages/@aws-cdk/{core-app-scoped-synthesizer => core-app-scoped-staging-synthesizer}/.npmignore (100%) rename packages/@aws-cdk/{core-app-scoped-synthesizer => core-app-scoped-staging-synthesizer}/LICENSE (100%) rename packages/@aws-cdk/{core-app-scoped-synthesizer => core-app-scoped-staging-synthesizer}/NOTICE (100%) rename packages/@aws-cdk/{core-app-scoped-synthesizer => core-app-scoped-staging-synthesizer}/README.md (100%) rename packages/@aws-cdk/{core-app-scoped-synthesizer => core-app-scoped-staging-synthesizer}/jest.config.js (100%) rename packages/@aws-cdk/{core-app-scoped-synthesizer => core-app-scoped-staging-synthesizer}/lib/default-staging-stack.ts (100%) rename packages/@aws-cdk/{core-app-scoped-synthesizer => core-app-scoped-staging-synthesizer}/lib/index.ts (100%) rename packages/@aws-cdk/{core-app-scoped-synthesizer => core-app-scoped-staging-synthesizer}/lib/synthesizer.ts (100%) rename packages/@aws-cdk/{core-app-scoped-synthesizer => core-app-scoped-staging-synthesizer}/package.json (80%) rename packages/@aws-cdk/{core-app-scoped-synthesizer => core-app-scoped-staging-synthesizer}/test/assets/index.py (100%) rename packages/@aws-cdk/{core-app-scoped-synthesizer => core-app-scoped-staging-synthesizer}/test/integ.synthesizer.js.snapshot/StagingStack.assets.json (100%) rename packages/@aws-cdk/{core-app-scoped-synthesizer => core-app-scoped-staging-synthesizer}/test/integ.synthesizer.js.snapshot/StagingStack.template.json (100%) rename packages/@aws-cdk/{core-app-scoped-synthesizer => core-app-scoped-staging-synthesizer}/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json (100%) rename packages/@aws-cdk/{core-app-scoped-synthesizer => core-app-scoped-staging-synthesizer}/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json (100%) rename packages/@aws-cdk/{core-app-scoped-synthesizer => core-app-scoped-staging-synthesizer}/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js (100%) rename packages/@aws-cdk/{core-app-scoped-synthesizer => core-app-scoped-staging-synthesizer}/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js (100%) rename packages/@aws-cdk/{core-app-scoped-synthesizer => core-app-scoped-staging-synthesizer}/test/integ.synthesizer.js.snapshot/cdk.out (100%) rename packages/@aws-cdk/{core-app-scoped-synthesizer => core-app-scoped-staging-synthesizer}/test/integ.synthesizer.js.snapshot/integ.json (100%) rename packages/@aws-cdk/{core-app-scoped-synthesizer => core-app-scoped-staging-synthesizer}/test/integ.synthesizer.js.snapshot/manifest.json (100%) rename packages/@aws-cdk/{core-app-scoped-synthesizer => core-app-scoped-staging-synthesizer}/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json (100%) rename packages/@aws-cdk/{core-app-scoped-synthesizer => core-app-scoped-staging-synthesizer}/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json (100%) rename packages/@aws-cdk/{core-app-scoped-synthesizer => core-app-scoped-staging-synthesizer}/test/integ.synthesizer.js.snapshot/tree.json (100%) rename packages/@aws-cdk/{core-app-scoped-synthesizer => core-app-scoped-staging-synthesizer}/test/integ.synthesizer.ts (100%) rename packages/@aws-cdk/{core-app-scoped-synthesizer => core-app-scoped-staging-synthesizer}/test/synthesizer.test.ts (100%) diff --git a/packages/@aws-cdk/core-app-scoped-synthesizer/.eslintrc.js b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/.eslintrc.js similarity index 100% rename from packages/@aws-cdk/core-app-scoped-synthesizer/.eslintrc.js rename to packages/@aws-cdk/core-app-scoped-staging-synthesizer/.eslintrc.js diff --git a/packages/@aws-cdk/core-app-scoped-synthesizer/.gitignore b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/.gitignore similarity index 100% rename from packages/@aws-cdk/core-app-scoped-synthesizer/.gitignore rename to packages/@aws-cdk/core-app-scoped-staging-synthesizer/.gitignore diff --git a/packages/@aws-cdk/core-app-scoped-synthesizer/.npmignore b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/.npmignore similarity index 100% rename from packages/@aws-cdk/core-app-scoped-synthesizer/.npmignore rename to packages/@aws-cdk/core-app-scoped-staging-synthesizer/.npmignore diff --git a/packages/@aws-cdk/core-app-scoped-synthesizer/LICENSE b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/LICENSE similarity index 100% rename from packages/@aws-cdk/core-app-scoped-synthesizer/LICENSE rename to packages/@aws-cdk/core-app-scoped-staging-synthesizer/LICENSE diff --git a/packages/@aws-cdk/core-app-scoped-synthesizer/NOTICE b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/NOTICE similarity index 100% rename from packages/@aws-cdk/core-app-scoped-synthesizer/NOTICE rename to packages/@aws-cdk/core-app-scoped-staging-synthesizer/NOTICE diff --git a/packages/@aws-cdk/core-app-scoped-synthesizer/README.md b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/README.md similarity index 100% rename from packages/@aws-cdk/core-app-scoped-synthesizer/README.md rename to packages/@aws-cdk/core-app-scoped-staging-synthesizer/README.md diff --git a/packages/@aws-cdk/core-app-scoped-synthesizer/jest.config.js b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/jest.config.js similarity index 100% rename from packages/@aws-cdk/core-app-scoped-synthesizer/jest.config.js rename to packages/@aws-cdk/core-app-scoped-staging-synthesizer/jest.config.js diff --git a/packages/@aws-cdk/core-app-scoped-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts similarity index 100% rename from packages/@aws-cdk/core-app-scoped-synthesizer/lib/default-staging-stack.ts rename to packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts diff --git a/packages/@aws-cdk/core-app-scoped-synthesizer/lib/index.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/index.ts similarity index 100% rename from packages/@aws-cdk/core-app-scoped-synthesizer/lib/index.ts rename to packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/index.ts diff --git a/packages/@aws-cdk/core-app-scoped-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts similarity index 100% rename from packages/@aws-cdk/core-app-scoped-synthesizer/lib/synthesizer.ts rename to packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts diff --git a/packages/@aws-cdk/core-app-scoped-synthesizer/package.json b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/package.json similarity index 80% rename from packages/@aws-cdk/core-app-scoped-synthesizer/package.json rename to packages/@aws-cdk/core-app-scoped-staging-synthesizer/package.json index 334e8ead49ac9..99bab16b328fa 100644 --- a/packages/@aws-cdk/core-app-scoped-synthesizer/package.json +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/package.json @@ -1,8 +1,8 @@ { - "name": "@aws-cdk/core-synthesizer", + "name": "@aws-cdk/core-app-scoped-staging-synthesizer", "private": true, "version": "0.0.0", - "description": "Cdk synthesizer for Bootstrap v3", + "description": "Cdk synthesizer for app-scoped bootstrap", "main": "lib/index.js", "types": "lib/index.d.ts", "jsii": { @@ -19,20 +19,20 @@ "java": { "maven": { "groupId": "software.amazon.awscdk", - "artifactId": "cdk-core-synthesizer" + "artifactId": "cdk-core-app-scoped-staging-synthesizer" }, - "package": "software.amazon.awscdk.core.synthesizer" + "package": "software.amazon.awscdk.core.app.scoped.staging.synthesizer" }, "python": { - "distName": "aws-cdk.core-synthesizer", - "module": "aws_cdk.core_synthesizer", + "distName": "aws-cdk.core-app-scoped-staging-synthesizer", + "module": "aws_cdk.core_app_scoped_staging_synthesizer", "classifiers": [ "Framework :: AWS CDK", "Framework :: AWS CDK :: 2" ] }, "dotnet": { - "namespace": "Amazon.CDK.Core.Synthesizer", + "namespace": "Amazon.CDK.Core.App.Scoped.Staging.Synthesizer", "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/main/logo/default-256-dark.png" } } @@ -40,7 +40,7 @@ "repository": { "type": "git", "url": "https://github.com/aws/aws-cdk.git", - "directory": "packages/@aws-cdk/core-synthesizer" + "directory": "packages/@aws-cdk/core-app-scoped-staging-synthesizer" }, "scripts": { "build": "cdk-build", @@ -108,6 +108,7 @@ "@aws-cdk/core": "0.0.0", "constructs": "^10.0.0", "@aws-cdk/aws-s3": "0.0.0", - "@aws-cdk/aws-ecr": "0.0.0" + "@aws-cdk/aws-ecr": "0.0.0", + "@aws-cdk/aws-iam": "0.0.0" } } diff --git a/packages/@aws-cdk/core-app-scoped-synthesizer/test/assets/index.py b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/assets/index.py similarity index 100% rename from packages/@aws-cdk/core-app-scoped-synthesizer/test/assets/index.py rename to packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/assets/index.py diff --git a/packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.assets.json b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.assets.json similarity index 100% rename from packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.assets.json rename to packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.assets.json diff --git a/packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.template.json b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.template.json similarity index 100% rename from packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.template.json rename to packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.template.json diff --git a/packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json similarity index 100% rename from packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json rename to packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json diff --git a/packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json similarity index 100% rename from packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json rename to packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json diff --git a/packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js similarity index 100% rename from packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js rename to packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js diff --git a/packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js similarity index 100% rename from packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js rename to packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js diff --git a/packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/cdk.out b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/cdk.out similarity index 100% rename from packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/cdk.out rename to packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/cdk.out diff --git a/packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/integ.json b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/integ.json similarity index 100% rename from packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/integ.json rename to packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/integ.json diff --git a/packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json similarity index 100% rename from packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json rename to packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json diff --git a/packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json similarity index 100% rename from packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json rename to packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json diff --git a/packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json similarity index 100% rename from packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json rename to packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json diff --git a/packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/tree.json b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json similarity index 100% rename from packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.js.snapshot/tree.json rename to packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json diff --git a/packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.ts similarity index 100% rename from packages/@aws-cdk/core-app-scoped-synthesizer/test/integ.synthesizer.ts rename to packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.ts diff --git a/packages/@aws-cdk/core-app-scoped-synthesizer/test/synthesizer.test.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts similarity index 100% rename from packages/@aws-cdk/core-app-scoped-synthesizer/test/synthesizer.test.ts rename to packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts From 5dc7a5f420d79603f3cd53b46b18ebc2ea5c6d57 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Fri, 17 Mar 2023 17:31:36 -0400 Subject: [PATCH 028/120] adr --- .../adr/resource-names.md | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 packages/@aws-cdk/core-app-scoped-staging-synthesizer/adr/resource-names.md diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/adr/resource-names.md b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/adr/resource-names.md new file mode 100644 index 0000000000000..f718afc5ca0e6 --- /dev/null +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/adr/resource-names.md @@ -0,0 +1,30 @@ +# Staging Stack Resource Names + +The Staging Stack can produce the following types of resources, depending on what is needed for the app: + +- iam role (file publishing role and asset publishing role) +- s3 bucket (one per app) +- ecr repository (one per image asset family) + +These resources need to be named unique to their scope to avoid CloudFormation errors when trying to create +a resource with an existing name. The resource specific limitations are as follows: + +- iam role names: must be unique to their account +- s3 bucket names: must be globally unique +- ecr repository names: must be unique to their account/region + +The attributes we can use to name our resources are as follows: + +- account number (i.e. `123456789012`) +- region name (i.e. `us-east-1`) +- app id (a user-specified id that should be unique to the app) +- image id (a user-specified id added on image assets) + +This information can be distilled into the following table, which shows what identifiers are necessary to +make each resource name unique: + +| Resource | Account | Region | App Id | Image Id | +| --------- | ------- | ------ | ------ | -------- | +| iam roles | | ✔️ | ✔️ | | +| s3 bucket | ✔️ | ✔️ | ✔️ ️️ | | +| ecr repos | | | ✔️ | ✔️ | From 42b8566400c4e5e2c154bf218755dbceb816aa56 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Fri, 17 Mar 2023 19:10:19 -0400 Subject: [PATCH 029/120] readme start --- .../README.md | 133 +++++++++++++++++- .../lib/synthesizer.ts | 12 +- 2 files changed, 136 insertions(+), 9 deletions(-) diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/README.md b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/README.md index abb7b38766db4..ff3092f7371ca 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/README.md +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/README.md @@ -1 +1,132 @@ -# Synthesizer +# App Scoped Staging Synthesizer + + +--- + +![cdk-constructs: Experimental](https://img.shields.io/badge/cdk--constructs-experimental-important.svg?style=for-the-badge) + +> The APIs of higher level constructs in this module are experimental and under active development. +> They are subject to non-backward compatible changes or removal in any future version. These are +> not subject to the [Semantic Versioning](https://semver.org/) model and breaking changes will be +> announced in the release notes. This means that while you may use them, you may need to update +> your source code when upgrading to a newer version of this package. + +--- + + + +This library includes constructs aimed at replacing the current model of bootstrapping and providing +greater control of the bootstrap experience to the CDK user. The important constructs in this library +are the `IStagingStack`, a framework for an app-level bootstrap stack that handles file assets and +docker assets and the `DefaultStagingStack`, which is a works-out-of-the-box implementation of the +interface. Additionally, there is an `AppScopedStagingSynthesizer` that will synthesize CDK applications +built with this new model of bootstrapping. + +## Bootstrap Model + +Our most current bootstrap model looks like this, when you run `cdk bootstrap aws:///` : + +```mermaid +graph TD + A(Bootstrap Stack) --- B(CloudFormationExecutionRole
DeploymentActionRole
LookupRole
FilePublishingRole
ImagePublishingRole
StagingBucket
ContainerAssetsRepository
FileAssetsBucketEncryptionKey) +``` + +Your CDK Applicaiton utilizes some of these resources when deploying. For example, if you have a file asset, +it gets uploaded to the `StagingBucket` using the `FilePublishingRole` when you run `cdk deploy`. + +This library introduces an alternate model to bootstrapping, by splitting out essential CloudFormation iam roles +and staging resources. There will still be a Bootstrap Stack, but this will only contain IAM roles necessary for +CloudFormation deployment. Each CDK App will instead be in charge of it's own staging resources, including the +S3 Bucket, ECR Repository, and associated IAM roles. + +```mermaid +graph TD + A(Bootstrap Stack) --- B(CloudFormationExecutionRole
DeploymentActionRole
LookupRole) + C(CDK App with File Asset) --- D(Staging Stack A) + C --- I(Stack A) + I --- J(File Asset) + F(CDK App with File/Image Asset) --- G(Staging Stack B) + F --- K(Stack B) + K --- L(File Asset
Image Asset) + G --- H(FilePublishingRole
ImagePublishingRole
StagingBucket
ContainerAssetsRepository
FileAssetsBucketEncryptionKey) + D --- E(FilePublishingRole
StagingBucket
FileAssetsBucketEncryptionKey) + M(CDK App with no Assets) --- N(Stack C) + N --- O(
) +``` + +This allows staging resources to be created when needed next to the CDK App. It has the following +benefits: + +- Bootstrapping will be faster since the heavy resource of a KMS key is no longer involved. +- Because roles are a global resource, every account now only needs to be bootstrapped once. +- Users have a familiar way to customize staging resources in the CDK Application. + +> As this library is `experimental`, the accompanying Bootstrap Stack is not yet implemented. To use this +> library right now, you must reuse roles that have been traditionally bootstrapped. + +## Example + +To use this library, supply the `AppScopedStagingSynthesizer` in as the default synthesizer to the app. +This will ensure that a Staging Stack will be created next to the CDK App to hold the staging resources. +In this example, the Staging Stack is the `DefaultStagingStack` construct. + +```ts +import * as path from 'path'; +import * as lambda from 'aws-cdk-lib/aws-lambda'; +import { App, Stack } from 'aws-cdk-lib'; +import { AppScopedStagingSynthesizer } from 'aws-cdk-lib/core-app-scoped-staging-synthesizer'; + +const app = new App({ + defaultSynthesizer: new AppScopedStagingSynthesizer({ + appId: 'my-app-id', + }), +}); + +const stack = new Stack(app, 'my-stack'); + +new lambda.Function(stack, 'lambda', { + code: lambda.AssetCode.fromAsset(path.join(__dirname, 'assets')), + handler: 'index.handler', + runtime: lambda.Runtime.PYTHON_3_9, +}); + +app.synth(); +``` + +You can customize some or all of the roles you'd like to use in the synthesizer as well: + +```ts +import { App } from 'aws-cdk-lib'; +import { AppScopedStagingSynthesizer } from 'aws-cdk-lib/core-app-scoped-staging-synthesizer'; + +const app = new App({ + defaultSynthesizer: new AppScopedStagingSynthesizer({ + appId: 'my-app-id', + roles: { + cloudFormationExecutionRole: BoostrapRole.fromRoleArn('arn'), + deploymentActionRole: BootstrapRole.fromRoleArn('arn'), + lookupRole: BoostrapRole.fromRoleArn('arn'), + fileAssetPublishingRole: BootstrapRole.fromRoleArn('arn'), + imageAssetPublishingRole: BootstrapRole.fromRoleArn('arn'), + }, + }), +}); +``` + +## Custom Staging Stack + +You can supply your own staging stack if you need further customization from what the +`DefaultStagingStack` has to offer. Simply implement the `IStagingStack` interface and +supply your custom construct in as the `defaultSynthesizer`. + +```ts +class CustomStagingStack implements IStagingStack { + // ... +} + +const app = new App({ + defaultSynthesizer = new AppScopedStagingSynthesizer({ + stagingStack: new CustomStagingStack(), + }), +}); +``` \ No newline at end of file diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts index 37c4a1530b2a6..1308bc6f11cb9 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts @@ -161,6 +161,7 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta private getCreateStagingStack(app: Stage, env: Environment): IStagingStack { const stackName = `StagingStack${this.appId}`; const stackId = 'StagingStack'; + // TODO: this needs to be an IStagingStack const stagingStack = app.node.tryFindChild(stackId) as DefaultStagingStack ?? new DefaultStagingStack(app, stackId, { env, stackName, @@ -175,12 +176,7 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta const templateAssetSource = this.synthesizeTemplate(session, this.lookupRoleArn); const templateAsset = this.addFileAsset(templateAssetSource); - const assetManifestId = this.assetManifest.emitManifest(this.boundStack, session, { - // TODO: handle verison props here - }); - - // eslint-disable-next-line no-console - console.log(templateAsset, assetManifestId); + const assetManifestId = this.assetManifest.emitManifest(this.boundStack, session); this.emitArtifact(session, { additionalDependencies: [assetManifestId], @@ -189,7 +185,7 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta } /** - * // TODO + * Add a file asset to the manifest. */ public addFileAsset(asset: FileAssetSource): FileAssetLocation { const { bucketName, assumeRoleArn } = this.stagingStack.addFile(asset); @@ -201,7 +197,7 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta } /** - * // TODO + * Add a docker image asset to the manifest. */ public addDockerImageAsset(asset: DockerImageAssetSource): DockerImageAssetLocation { const { repoName, assumeRoleArn } = this.stagingStack.addDockerImage(asset); From 6e57c2906cddf7878c2a8d858142beb99540bdf1 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Mon, 20 Mar 2023 08:57:06 -0400 Subject: [PATCH 030/120] minor changes --- .../README.md | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/README.md b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/README.md index ff3092f7371ca..b8d5a4ca0b9c6 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/README.md +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/README.md @@ -64,7 +64,7 @@ benefits: > As this library is `experimental`, the accompanying Bootstrap Stack is not yet implemented. To use this > library right now, you must reuse roles that have been traditionally bootstrapped. -## Example +## Basic Example To use this library, supply the `AppScopedStagingSynthesizer` in as the default synthesizer to the app. This will ensure that a Staging Stack will be created next to the CDK App to hold the staging resources. @@ -93,11 +93,14 @@ new lambda.Function(stack, 'lambda', { app.synth(); ``` -You can customize some or all of the roles you'd like to use in the synthesizer as well: +## Custom Roles + +You can customize some or all of the roles you'd like to use in the synthesizer as well, +if all you need is to supply custom roles (and not change anything else in the `DefaultStagingStack`): ```ts import { App } from 'aws-cdk-lib'; -import { AppScopedStagingSynthesizer } from 'aws-cdk-lib/core-app-scoped-staging-synthesizer'; +import { AppScopedStagingSynthesizer, BootstrapRole } from 'aws-cdk-lib/core-app-scoped-staging-synthesizer'; const app = new App({ defaultSynthesizer: new AppScopedStagingSynthesizer({ @@ -113,6 +116,24 @@ const app = new App({ }); ``` +You can also supply specific roles to the Staging Stack: + +```ts +import { App } from 'aws-cdk-lib'; +import { AppScopedStagingSynthesizer, BootstrapRole } from 'aws-cdk-lib/core-app-scoped-staging-synthesizer'; + +const app = new App({ + defaultSynthesizer: new AppScopedStagingSynthesizer({ + appId: 'my-app-id', + stagingStack: new DefaultStagingStack(this, 'StagingStack', { + fileAssetPublishingRole: BootstrapRole.fromRoleArn('arn'), + dockerAssetPublishingRole: BootstrapRole.fromRoleArn('arn'), + // additional properties + }), + }), +}); +``` + ## Custom Staging Stack You can supply your own staging stack if you need further customization from what the @@ -120,6 +141,9 @@ You can supply your own staging stack if you need further customization from wha supply your custom construct in as the `defaultSynthesizer`. ```ts +import { App } from 'aws-cdk-lib'; +import { AppScopedStagingSynthesizer } from 'aws-cdk-lib/core-app-scoped-staging-synthesizer'; + class CustomStagingStack implements IStagingStack { // ... } @@ -129,4 +153,4 @@ const app = new App({ stagingStack: new CustomStagingStack(), }), }); -``` \ No newline at end of file +``` From 2a5c44ec960f088e9af6a7f453c608be428f0281 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Mon, 20 Mar 2023 09:56:53 -0400 Subject: [PATCH 031/120] move appId to the app props --- .../README.md | 4 ++ .../lib/default-staging-stack.ts | 21 +++++---- .../lib/synthesizer.ts | 44 ++++++++++--------- .../test/integ.synthesizer.ts | 8 ++-- .../test/synthesizer.test.ts | 7 ++- packages/@aws-cdk/core/lib/app.ts | 16 +++++++ 6 files changed, 61 insertions(+), 39 deletions(-) diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/README.md b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/README.md index b8d5a4ca0b9c6..78496464d9ae2 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/README.md +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/README.md @@ -93,6 +93,10 @@ new lambda.Function(stack, 'lambda', { app.synth(); ``` +Every CDK App that uses the `AppScopedStagingSynthesizer` must include an `appId`. This should +be an identifier unique to the app and is used to differentiate staging resources associated +with the app. + ## Custom Roles You can customize some or all of the roles you'd like to use in the synthesizer as well, diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts index b2e9fd97f12c5..eb9314eb79347 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts @@ -1,8 +1,8 @@ import * as ecr from '@aws-cdk/aws-ecr'; import * as iam from '@aws-cdk/aws-iam'; import * as s3 from '@aws-cdk/aws-s3'; -import { Arn, ArnFormat, Aws, BootstraplessSynthesizer, DockerImageAssetSource, FileAssetSource, RemovalPolicy, Stack, StackProps } from '@aws-cdk/core'; -import { Construct, IConstruct } from 'constructs'; +import { App, Arn, ArnFormat, Aws, BootstraplessSynthesizer, DockerImageAssetSource, FileAssetSource, RemovalPolicy, Stack, StackProps } from '@aws-cdk/core'; +import { IConstruct } from 'constructs'; import { BootstrapRole } from './synthesizer'; export interface FileAssetInfo { @@ -17,7 +17,7 @@ export interface DockerAssetInfo { /** * Information on how a Staging Stack should look. */ -export interface IDefaultStagingStack extends IConstruct { +export interface IStagingStack extends IConstruct { /** * App level unique identifier */ @@ -69,17 +69,12 @@ export interface DefaultStagingStackProps extends StackProps { * @default - a well-known name unique to this app/env. */ readonly dockerAssetPublishingRole?: BootstrapRole; - - /** - * Application identifier unique to the app. - */ - readonly appId: string; } /** * A default Staging Stack */ -export class DefaultStagingStack extends Stack implements IDefaultStagingStack { +export class DefaultStagingStack extends Stack implements IStagingStack { /** * Default asset publishing role name for file (S3) assets. */ @@ -109,13 +104,17 @@ export class DefaultStagingStack extends Stack implements IDefaultStagingStack { private fileAssetPublishingRole?: BootstrapRole; private dockerAssetPublishingRole?: BootstrapRole; - constructor(scope: Construct, id: string, props: DefaultStagingStackProps) { + constructor(scope: App, id: string, props: DefaultStagingStackProps) { super(scope, id, { ...props, synthesizer: new BootstraplessSynthesizer(), }); - this.appId = props.appId; + if (scope._appId === undefined) { + throw new Error('DefaultStagingStack can only be used on Apps with a user-specified appId, but no appId found.'); + } + + this.appId = scope._appId; this.stagingBucketName = props.stagingBucketName; this.fileAssetPublishingRole = props.fileAssetPublishingRole; diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts index 1308bc6f11cb9..a00c3935473f5 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts @@ -12,11 +12,10 @@ import { ISynthesisSession, Stack, StackSynthesizer, - Stage, Token, } from '@aws-cdk/core'; import * as cxapi from '@aws-cdk/cx-api'; -import { IDefaultStagingStack as IStagingStack, DefaultStagingStack } from './default-staging-stack'; +import { IStagingStack as IStagingStack, DefaultStagingStack } from './default-staging-stack'; export class BootstrapRole { // public abstract arn(stack: Stack) { @@ -47,10 +46,18 @@ export class BootstrapRole { } } +export interface BootstrapRoles { + readonly cloudFormationExecutionRole?: BootstrapRole; + readonly deploymentActionRole?: BootstrapRole; + readonly lookupRole?: BootstrapRole; + readonly fileAssetPublishingRole?: BootstrapRole; + readonly imageAssetPublishingRole?: BootstrapRole; +} + /** * New stack synthesizer properties */ -export interface StagingStackSynthesizerProps { +export interface AppScopedStagingSynthesizerProps { /** * Bring a custom staging stack into the app. * @@ -59,29 +66,27 @@ export interface StagingStackSynthesizerProps { readonly stagingStack?: IStagingStack; /** - * Custom lookup role arn + * Custom roles * - * @default - default role + * @default - no custom roles */ - readonly lookupRole?: BootstrapRole; - - readonly appId: string; + readonly bootstrapRoles?: BootstrapRoles; } /** * New Stack Synthesizer */ export class AppScopedStagingSynthesizer extends StackSynthesizer implements IReusableStackSynthesizer { - constructor(private readonly props: StagingStackSynthesizerProps) { + constructor(private readonly props: AppScopedStagingSynthesizerProps = {}) { super(); for (const key in props) { if (props.hasOwnProperty(key)) { - validateNoToken(key as keyof StagingStackSynthesizerProps); + validateNoToken(key as keyof AppScopedStagingSynthesizerProps); } } - function validateNoToken
(key: A) { + function validateNoToken(key: A) { const prop = props[key]; if (typeof prop === 'string' && Token.isUnresolved(prop)) { throw new Error(`AppScopedStagingSynthesizer property '${key}' cannot contain tokens; only the following placeholder strings are allowed: ` + [ @@ -139,33 +144,32 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta private stagingStack: IStagingStack; private assetManifest = new AssetManifestBuilder(); private readonly lookupRoleArn: string; - private readonly appId: string; - constructor(private readonly stack: Stack, props: StagingStackSynthesizerProps) { + constructor(private readonly stack: Stack, props: AppScopedStagingSynthesizerProps) { super(); super.bind(stack); - this.lookupRoleArn = props.lookupRole ? props.lookupRole.roleArn : BoundStagingStackSynthesizer.DEFAULT_LOOKUP_ROLE_ARN; - this.appId = props.appId; + this.lookupRoleArn = props.bootstrapRoles?.lookupRole ? + props.bootstrapRoles.lookupRole.roleArn : BoundStagingStackSynthesizer.DEFAULT_LOOKUP_ROLE_ARN; const app = App.of(stack); - if (!app) { - throw new Error(`stack ${stack.stackName} must be part of an App`); + if (!App.isApp(app)) { + throw new Error(`Stack ${stack.stackName} must be part of an App`); } + this.stagingStack = props.stagingStack ?? this.getCreateStagingStack(app, { account: stack.account, region: stack.region, }); } - private getCreateStagingStack(app: Stage, env: Environment): IStagingStack { - const stackName = `StagingStack${this.appId}`; + private getCreateStagingStack(app: App, env: Environment): IStagingStack { + const stackName = `StagingStack${app._appId}`; const stackId = 'StagingStack'; // TODO: this needs to be an IStagingStack const stagingStack = app.node.tryFindChild(stackId) as DefaultStagingStack ?? new DefaultStagingStack(app, stackId, { env, stackName, - appId: this.appId, }); this.stack.addDependency(stagingStack, 'reason'); diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.ts index c67da4d936c3c..bf7e29809cd51 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.ts +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.ts @@ -3,12 +3,12 @@ import * as lambda from '@aws-cdk/aws-lambda'; import { App, Stack } from '@aws-cdk/core'; import { AppScopedStagingSynthesizer } from '../lib'; -const app = new App(); +const app = new App({ + appId: 'app1', +}); const stack = new Stack(app, 'app-scoped-staging-test', { - synthesizer: new AppScopedStagingSynthesizer({ - appId: '1', - }), + synthesizer: new AppScopedStagingSynthesizer(), // env: { // account: '489318732371', // region: 'us-east-1', diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts index 71cb9a3a22af6..1a4844a2d2af3 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts @@ -18,9 +18,8 @@ describe(AppScopedStagingSynthesizer, () => { beforeEach(() => { app = new App({ - defaultStackSynthesizer: new AppScopedStagingSynthesizer({ - appId: APP_ID, - }), + appId: APP_ID, + defaultStackSynthesizer: new AppScopedStagingSynthesizer(), }); stack = new Stack(app, 'Stack', { env: { @@ -157,7 +156,7 @@ describe(AppScopedStagingSynthesizer, () => { test('file asset depends on staging stack', () => { // WHEN - const location = stack.synthesizer.addFileAsset({ + stack.synthesizer.addFileAsset({ fileName: __filename, packaging: FileAssetPackaging.FILE, sourceHash: 'abcdef', diff --git a/packages/@aws-cdk/core/lib/app.ts b/packages/@aws-cdk/core/lib/app.ts index 1ae43828c8c3e..078a87bf6931b 100644 --- a/packages/@aws-cdk/core/lib/app.ts +++ b/packages/@aws-cdk/core/lib/app.ts @@ -118,6 +118,13 @@ export interface AppProps { * @default - A `DefaultStackSynthesizer` with default settings */ readonly defaultStackSynthesizer?: IReusableStackSynthesizer; + + /** + * A unique identifier for the app. + * + * @default - no identifier + */ + readonly appId?: string; } /** @@ -152,6 +159,13 @@ export class App extends Stage { */ public readonly _treeMetadata: boolean; + /** + * Id of the app + * + * @internal + */ + public readonly _appId?: string; + /** * Initializes a CDK application. * @param props initialization properties @@ -173,6 +187,8 @@ export class App extends Stage { this.node.setContext(PRIVATE_CONTEXT_DEFAULT_STACK_SYNTHESIZER, props.defaultStackSynthesizer); } + this._appId = props.appId; + const analyticsReporting = props.analyticsReporting ?? props.runtimeInfo; if (analyticsReporting !== undefined) { From fb50b3dd4a4de54917acdae71b172813562004be Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Mon, 20 Mar 2023 13:15:14 -0400 Subject: [PATCH 032/120] new tests --- .../lib/synthesizer.ts | 31 ++-- .../test/synthesizer.test.ts | 151 ++++++++++++++++-- 2 files changed, 160 insertions(+), 22 deletions(-) diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts index a00c3935473f5..5c5f15f2bad80 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts @@ -51,7 +51,7 @@ export interface BootstrapRoles { readonly deploymentActionRole?: BootstrapRole; readonly lookupRole?: BootstrapRole; readonly fileAssetPublishingRole?: BootstrapRole; - readonly imageAssetPublishingRole?: BootstrapRole; + readonly dockerAssetPublishingRole?: BootstrapRole; } /** @@ -74,7 +74,7 @@ export interface AppScopedStagingSynthesizerProps { } /** - * New Stack Synthesizer + * App Scoped Staging Stack Synthesizer */ export class AppScopedStagingSynthesizer extends StackSynthesizer implements IReusableStackSynthesizer { constructor(private readonly props: AppScopedStagingSynthesizerProps = {}) { @@ -136,21 +136,23 @@ export class AppScopedStagingSynthesizer extends StackSynthesizer implements IRe } class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundStackSynthesizer { - /** - * Default lookup role ARN for missing values. - */ - public static readonly DEFAULT_LOOKUP_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-lookup-role-${AWS::AccountId}-${AWS::Region}'; - private stagingStack: IStagingStack; private assetManifest = new AssetManifestBuilder(); - private readonly lookupRoleArn: string; + private readonly lookupRoleArn?: string; + private readonly cloudFormationExecutionRoleArn?: string; + private readonly deploymentActionRoleArn?: string; + private readonly fileAssetPublishingRole?: BootstrapRole; + private readonly dockerAssetPublishingRole?: BootstrapRole; - constructor(private readonly stack: Stack, props: AppScopedStagingSynthesizerProps) { + constructor(private readonly stack: Stack, props: AppScopedStagingSynthesizerProps = {}) { super(); super.bind(stack); - this.lookupRoleArn = props.bootstrapRoles?.lookupRole ? - props.bootstrapRoles.lookupRole.roleArn : BoundStagingStackSynthesizer.DEFAULT_LOOKUP_ROLE_ARN; + this.lookupRoleArn = props.bootstrapRoles?.lookupRole?.roleArn; + this.cloudFormationExecutionRoleArn = props.bootstrapRoles?.cloudFormationExecutionRole?.roleArn; + this.deploymentActionRoleArn = props.bootstrapRoles?.deploymentActionRole?.roleArn; + this.fileAssetPublishingRole = props.bootstrapRoles?.fileAssetPublishingRole; + this.dockerAssetPublishingRole = props.bootstrapRoles?.dockerAssetPublishingRole; const app = App.of(stack); if (!App.isApp(app)) { @@ -170,6 +172,8 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta const stagingStack = app.node.tryFindChild(stackId) as DefaultStagingStack ?? new DefaultStagingStack(app, stackId, { env, stackName, + fileAssetPublishingRole: this.fileAssetPublishingRole, + dockerAssetPublishingRole: this.dockerAssetPublishingRole, }); this.stack.addDependency(stagingStack, 'reason'); @@ -183,8 +187,13 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta const assetManifestId = this.assetManifest.emitManifest(this.boundStack, session); this.emitArtifact(session, { + assumeRoleArn: this.deploymentActionRoleArn, additionalDependencies: [assetManifestId], stackTemplateAssetObjectUrl: templateAsset.s3ObjectUrlWithPlaceholders, + cloudFormationExecutionRoleArn: this.cloudFormationExecutionRoleArn, + lookupRole: this.lookupRoleArn ? { + arn: this.lookupRoleArn, + }: undefined, }); } diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts index 1a4844a2d2af3..2c6315f813c8a 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts @@ -1,9 +1,9 @@ import * as fs from 'fs'; import * as cxschema from '@aws-cdk/cloud-assembly-schema'; -import { App, Stack, CfnResource, FileAssetPackaging, Aws } from '@aws-cdk/core'; +import { App, Stack, CfnResource, FileAssetPackaging, Aws, FileAssetSource, DockerImageAssetSource, Token } from '@aws-cdk/core'; import { evaluateCFN } from '@aws-cdk/core/test/evaluate-cfn'; import * as cxapi from '@aws-cdk/cx-api'; -import { AppScopedStagingSynthesizer } from '../lib'; +import { AppScopedStagingSynthesizer, AppScopedStagingSynthesizerProps, BootstrapRole, DockerAssetInfo, FileAssetInfo, IStagingStack } from '../lib'; const CFN_CONTEXT = { 'AWS::Region': 'the_region', @@ -11,6 +11,9 @@ const CFN_CONTEXT = { 'AWS::URLSuffix': 'domain.aws', }; const APP_ID = 'appId'; +const CLOUDFORMATION_EXECUTION_ROLE = 'role'; +const DEPLOY_ACTION_ROLE = 'role'; +const LOOKUP_ROLE = 'role'; describe(AppScopedStagingSynthesizer, () => { let app: App; @@ -19,7 +22,7 @@ describe(AppScopedStagingSynthesizer, () => { beforeEach(() => { app = new App({ appId: APP_ID, - defaultStackSynthesizer: new AppScopedStagingSynthesizer(), + defaultStackSynthesizer: new TestAppScopedStagingSynthesizer(), }); stack = new Stack(app, 'Stack', { env: { @@ -65,6 +68,54 @@ describe(AppScopedStagingSynthesizer, () => { }); }); + test('stack template is in the asset manifest - environment tokens', () => { + const app2 = new App({ + appId: APP_ID, + defaultStackSynthesizer: new TestAppScopedStagingSynthesizer(), + }); + const accountToken = Token.asString('111111111111'); + const regionToken = Token.asString('us-east-2'); + const stack2 = new Stack(app2, 'Stack2', { + env: { + account: accountToken, + region: regionToken, + }, + }); + + // GIVEN + new CfnResource(stack2, 'Resource', { + type: 'Some::Resource', + }); + + // WHEN + const asm = app2.synth(); + + // THEN -- the S3 url is advertised on the stack artifact + const stackArtifact = asm.getStackArtifact('Stack2'); + + const templateObjectKey = last(stackArtifact.stackTemplateAssetObjectUrl?.split('/')); + expect(stackArtifact.stackTemplateAssetObjectUrl).toEqual(`s3://cdk-${accountToken}-${regionToken}-${APP_ID.toLocaleLowerCase()}/${templateObjectKey}`); + + // THEN - the template is in the asset manifest + const manifestArtifact = asm.artifacts.filter(isAssetManifest)[0]; + expect(manifestArtifact).toBeDefined(); + const manifest: cxschema.AssetManifest = JSON.parse(fs.readFileSync(manifestArtifact.file, { encoding: 'utf-8' })); + + const firstFile = (manifest.files ? manifest.files[Object.keys(manifest.files)[0]] : undefined) ?? {}; + + expect(firstFile).toEqual({ + source: { path: 'Stack2.template.json', packaging: 'file' }, + destinations: { + '111111111111-us-east-2': { + bucketName: `cdk-111111111111-us-east-2-${APP_ID.toLocaleLowerCase()}`, + objectKey: templateObjectKey, + region: 'us-east-2', + assumeRoleArn: `arn:${Aws.PARTITION}:iam:us-east-2:111111111111:role:cdk-file-publishing-role-us-east-2-${APP_ID}`, + }, + }, + }); + }); + test('stack depends on staging stack', () => { // WHEN stack.synthesizer.addFileAsset({ @@ -154,16 +205,37 @@ describe(AppScopedStagingSynthesizer, () => { expect(evalCFN(location1.repositoryName)).toEqual(evalCFN(location2.repositoryName)); }); - test('file asset depends on staging stack', () => { - // WHEN - stack.synthesizer.addFileAsset({ - fileName: __filename, - packaging: FileAssetPackaging.FILE, - sourceHash: 'abcdef', + test('throws when App uses DefaultStagingStack and does not have appId', () => { + const app2 = new App({ + defaultStackSynthesizer: new TestAppScopedStagingSynthesizer(), }); + expect(() => new Stack(app2, 'Stack')).toThrowError('DefaultStagingStack can only be used on Apps with a user-specified appId, but no appId found.'); + }); - // THEN - we have a fixed asset location - console.log(stack.dependencies); + test('does not throw when App does not have appId and does not use DefaultStagingStack', () => { + class TestStagingStack extends Stack implements IStagingStack { + readonly appId = 'appId'; + readonly stagingRepos = {}; + addFile(_asset: FileAssetSource): FileAssetInfo { + return { + assumeRoleArn: 'assumeRoleArn', + bucketName: 'bucketName', + }; + } + addDockerImage(_asset: DockerImageAssetSource): DockerAssetInfo { + return { + assumeRoleArn: 'assumeRoleArn', + repoName: 'repoName', + }; + } + } + + const app2 = new App({ + defaultStackSynthesizer: new TestAppScopedStagingSynthesizer({ + stagingStack: new TestStagingStack(), + }), + }); + expect(() => new Stack(app2, 'Stack')).not.toThrow(); }); /** @@ -174,7 +246,51 @@ describe(AppScopedStagingSynthesizer, () => { function evalCFN(value: any) { return evaluateCFN(stack.resolve(value), CFN_CONTEXT); } +}); + +describe('Custom Roles on AppScopedStagingSynthesizer', () => { + test('Can supply different roles', () => { + const app = new App({ + appId: APP_ID, + defaultStackSynthesizer: new AppScopedStagingSynthesizer({ + bootstrapRoles: { + cloudFormationExecutionRole: BootstrapRole.fromRoleArn(CLOUDFORMATION_EXECUTION_ROLE), + lookupRole: BootstrapRole.fromRoleArn(LOOKUP_ROLE), + deploymentActionRole: BootstrapRole.fromRoleArn(DEPLOY_ACTION_ROLE), + fileAssetPublishingRole: BootstrapRole.fromRoleArn('arn'), + dockerAssetPublishingRole: BootstrapRole.fromRoleArn('arn'), + }, + }), + }); + + const stack = new Stack(app, 'Stack', { + env: { + account: '000000000000', + region: 'us-east-1', + }, + }); + new CfnResource(stack, 'Resource', { + type: 'Some::Resource', + }); + + // WHEN + const asm = app.synth(); + // THEN + const stackArtifact = asm.getStackArtifact('Stack'); + + // CloudFormation roles are as advertised + expect(stackArtifact.cloudFormationExecutionRoleArn).toEqual(CLOUDFORMATION_EXECUTION_ROLE); + expect(stackArtifact.lookupRole).toEqual({ arn: LOOKUP_ROLE }); + expect(stackArtifact.assumeRoleArn).toEqual(DEPLOY_ACTION_ROLE); + + const manifestArtifact = asm.artifacts.filter(isAssetManifest)[0]; + expect(manifestArtifact).toBeDefined(); + const manifest: cxschema.AssetManifest = JSON.parse(fs.readFileSync(manifestArtifact.file, { encoding: 'utf-8' })); + const firstFile: any = (manifest.files ? manifest.files[Object.keys(manifest.files)[0]] : undefined) ?? {}; + + expect(firstFile.destinations['000000000000-us-east-1'].assumeRoleArn).toEqual('arn'); + }); }); function isAssetManifest(x: cxapi.CloudArtifact): x is cxapi.AssetManifestArtifact { @@ -184,3 +300,16 @@ function isAssetManifest(x: cxapi.CloudArtifact): x is cxapi.AssetManifestArtifa function last(xs?: A[]): A | undefined { return xs ? xs[xs.length - 1] : undefined; } + +class TestAppScopedStagingSynthesizer extends AppScopedStagingSynthesizer { + public constructor(props: Partial = {}) { + super({ + bootstrapRoles: { + cloudFormationExecutionRole: BootstrapRole.fromRoleArn(CLOUDFORMATION_EXECUTION_ROLE), + deploymentActionRole: BootstrapRole.fromRoleArn(DEPLOY_ACTION_ROLE), + lookupRole: BootstrapRole.fromRoleArn(LOOKUP_ROLE), + }, + ...props, + }); + } +} \ No newline at end of file From 5483fe245d2920ea5f27fd594b3fb7df9a09c56d Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Mon, 20 Mar 2023 13:20:03 -0400 Subject: [PATCH 033/120] IStagingStack --- .../lib/default-staging-stack.ts | 4 ++++ .../core-app-scoped-staging-synthesizer/lib/synthesizer.ts | 4 ++-- .../test/synthesizer.test.ts | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts index eb9314eb79347..d0bc759ea5f96 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts @@ -34,6 +34,8 @@ export interface IStagingStack extends IConstruct { */ readonly stagingRepos: Record; + readonly dependencyStack: Stack; + /** * // TODO */ @@ -99,6 +101,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { */ public readonly stagingRepos: Record; + public readonly dependencyStack: Stack; public readonly appId: string; private readonly stagingBucketName?: string; private fileAssetPublishingRole?: BootstrapRole; @@ -115,6 +118,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { } this.appId = scope._appId; + this.dependencyStack = this; this.stagingBucketName = props.stagingBucketName; this.fileAssetPublishingRole = props.fileAssetPublishingRole; diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts index 5c5f15f2bad80..63f02db6933f4 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts @@ -169,13 +169,13 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta const stackName = `StagingStack${app._appId}`; const stackId = 'StagingStack'; // TODO: this needs to be an IStagingStack - const stagingStack = app.node.tryFindChild(stackId) as DefaultStagingStack ?? new DefaultStagingStack(app, stackId, { + const stagingStack = app.node.tryFindChild(stackId) as IStagingStack ?? new DefaultStagingStack(app, stackId, { env, stackName, fileAssetPublishingRole: this.fileAssetPublishingRole, dockerAssetPublishingRole: this.dockerAssetPublishingRole, }); - this.stack.addDependency(stagingStack, 'reason'); + this.stack.addDependency(stagingStack.dependencyStack, 'reason'); return stagingStack; } diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts index 2c6315f813c8a..7122ca4f39758 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts @@ -216,6 +216,7 @@ describe(AppScopedStagingSynthesizer, () => { class TestStagingStack extends Stack implements IStagingStack { readonly appId = 'appId'; readonly stagingRepos = {}; + readonly dependencyStack = this; addFile(_asset: FileAssetSource): FileAssetInfo { return { assumeRoleArn: 'assumeRoleArn', From 543bafe8249d2897784fe3da273a4668b4b75ae5 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Mon, 20 Mar 2023 18:03:47 -0400 Subject: [PATCH 034/120] lifecylce rules and kms key --- .../README.md | 61 +++++++++- .../lib/default-staging-stack.ts | 106 +++++++++++++++++- .../package.json | 2 + .../test/integ.synthesizer.ts | 2 +- .../test/synthesizer.test.ts | 4 +- 5 files changed, 160 insertions(+), 15 deletions(-) diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/README.md b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/README.md index 78496464d9ae2..5817b44a4f991 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/README.md +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/README.md @@ -37,7 +37,13 @@ it gets uploaded to the `StagingBucket` using the `FilePublishingRole` when you This library introduces an alternate model to bootstrapping, by splitting out essential CloudFormation iam roles and staging resources. There will still be a Bootstrap Stack, but this will only contain IAM roles necessary for CloudFormation deployment. Each CDK App will instead be in charge of it's own staging resources, including the -S3 Bucket, ECR Repository, and associated IAM roles. +S3 Bucket, ECR Repositories, and associated IAM roles. It works like this: + +The Staging Stack will contain, on a per-need basis, + +- 1 S3 Bucket with KMS encryption for all file assets in the CDK App. +- An ECR Repository _per_ image (and it's revisions). +- IAM roles with access to the Bucket and Repositories. ```mermaid graph TD @@ -77,9 +83,8 @@ import { App, Stack } from 'aws-cdk-lib'; import { AppScopedStagingSynthesizer } from 'aws-cdk-lib/core-app-scoped-staging-synthesizer'; const app = new App({ - defaultSynthesizer: new AppScopedStagingSynthesizer({ - appId: 'my-app-id', - }), + appId: 'my-app-id', + defaultSynthesizer: new AppScopedStagingSynthesizer(), }); const stack = new Stack(app, 'my-stack'); @@ -107,8 +112,8 @@ import { App } from 'aws-cdk-lib'; import { AppScopedStagingSynthesizer, BootstrapRole } from 'aws-cdk-lib/core-app-scoped-staging-synthesizer'; const app = new App({ + appId: 'my-app-id', defaultSynthesizer: new AppScopedStagingSynthesizer({ - appId: 'my-app-id', roles: { cloudFormationExecutionRole: BoostrapRole.fromRoleArn('arn'), deploymentActionRole: BootstrapRole.fromRoleArn('arn'), @@ -127,8 +132,8 @@ import { App } from 'aws-cdk-lib'; import { AppScopedStagingSynthesizer, BootstrapRole } from 'aws-cdk-lib/core-app-scoped-staging-synthesizer'; const app = new App({ + appId: 'my-app-id', defaultSynthesizer: new AppScopedStagingSynthesizer({ - appId: 'my-app-id', stagingStack: new DefaultStagingStack(this, 'StagingStack', { fileAssetPublishingRole: BootstrapRole.fromRoleArn('arn'), dockerAssetPublishingRole: BootstrapRole.fromRoleArn('arn'), @@ -158,3 +163,47 @@ const app = new App({ }), }); ``` + +## Repository Lifecycle Rules + +You can specify your own lifecycle rules on images. In the example below, we set lifecycle rules for both +images by referencing their `imageId`. `imageId` is a required property on docker assets that use the +`DefaultStagingStack`. + +```ts +import { App, Stack, Duration } from 'aws-cdk-lib'; +import { AppScopedStagingSynthesizer, BootstrapRole } from 'aws-cdk-lib/core-app-scoped-staging-synthesizer'; +import * as ecr_assets from 'aws-cdk-lib/aws-ecr-assets'; +import * as path from 'path'; + +const IMAGE_ID = 'image-id'; +const IMAGE_ID_2 = 'image-id-2'; +const app = new App({ + appId: 'my-app-id', + defaultSynthesizer: new AppScopedStagingSynthesizer({ + stagingStack: new DefaultStagingStack(this, 'StagingStack', { + repositoryLifecycleRules: [{ + assets: [IMAGE_ID], + lifecycleRules: [{ + { maxImageCount: 3 }, + }], + }, { + asset: [IMAGE_ID, IMAGE_ID_2], + lifecycleRules: [{ + expiration: Duration.days(30), + }], + }], + }), + }), +}); + +const stack = new Stack(app, 'my-stack'); +const dockerAsset = new ecr_assets.DockerImageAsset(stack, 'Assets', { + imageId: IMAGE_ID, + directory: path.join(__dirname, './docker.assets'), +}); +const dockerAsset2 = new ecr_assets.DockerImageAsset(stack, 'Assets2', { + imageId: IMAGE_ID_2, + directory: path.join(__dirname, './docker2.assets'), +}); +``` \ No newline at end of file diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts index d0bc759ea5f96..8da053bac491a 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts @@ -1,6 +1,7 @@ import * as ecr from '@aws-cdk/aws-ecr'; import * as iam from '@aws-cdk/aws-iam'; import * as s3 from '@aws-cdk/aws-s3'; +import * as kms from '@aws-cdk/aws-kms'; import { App, Arn, ArnFormat, Aws, BootstraplessSynthesizer, DockerImageAssetSource, FileAssetSource, RemovalPolicy, Stack, StackProps } from '@aws-cdk/core'; import { IConstruct } from 'constructs'; import { BootstrapRole } from './synthesizer'; @@ -71,6 +72,13 @@ export interface DefaultStagingStackProps extends StackProps { * @default - a well-known name unique to this app/env. */ readonly dockerAssetPublishingRole?: BootstrapRole; + + readonly repositoryLifecycleRules?: StagingRepoLifecycleRule[]; +} + +export interface StagingRepoLifecycleRule { + readonly lifecycleRules: ecr.LifecycleRule[]; + readonly assets: string[]; } /** @@ -106,6 +114,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { private readonly stagingBucketName?: string; private fileAssetPublishingRole?: BootstrapRole; private dockerAssetPublishingRole?: BootstrapRole; + private readonly repositoryLifecycleRules: Record; constructor(scope: App, id: string, props: DefaultStagingStackProps) { super(scope, id, { @@ -123,9 +132,23 @@ export class DefaultStagingStack extends Stack implements IStagingStack { this.stagingBucketName = props.stagingBucketName; this.fileAssetPublishingRole = props.fileAssetPublishingRole; this.dockerAssetPublishingRole = props.dockerAssetPublishingRole; + this.repositoryLifecycleRules = this.processLifecycleRules(props.repositoryLifecycleRules ?? []); this.stagingRepos = {}; } + private processLifecycleRules(rules: StagingRepoLifecycleRule[]) { + const ruleMap: Record = {}; + for (const rule of rules) { + for (const asset of rule.assets) { + if (ruleMap[asset] === undefined) { + ruleMap[asset] = []; + } + ruleMap[asset].push(...rule.lifecycleRules); + } + } + return ruleMap; + } + /** * Returns the well-known arn of the file publishing role */ @@ -142,6 +165,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { roleName: roleName, assumedBy: new iam.ServicePrincipal('sts.amazonaws.com'), }); + const bucket = this.getCreateBucket(); role.addToPolicy(new iam.PolicyStatement({ actions: [ 's3:GetObject*', @@ -153,8 +177,8 @@ export class DefaultStagingStack extends Stack implements IStagingStack { 's3:Abort*', ], resources: [ - this.getCreateBucket().bucketArn, - `${this.getCreateBucket().bucketArn}/*`, + bucket.bucketArn, + `${bucket.bucketArn}/*`, ], effect: iam.Effect.ALLOW, })); @@ -207,13 +231,83 @@ export class DefaultStagingStack extends Stack implements IStagingStack { }); } + private createBucketKey(): kms.IKey { + const bucketKeyId = 'BucketKey'; + const key = this.node.tryFindChild(bucketKeyId) as kms.IKey ?? new kms.Key(this, bucketKeyId, { + policy: new iam.PolicyDocument({ + statements: [ + new iam.PolicyStatement({ + actions: [ + 'kms:Create*', + 'kms:Describe*', + 'kms:Enable*', + 'kms:List*', + 'kms:Put*', + 'kms:Update*', + 'kms:Revoke*', + 'kms:Disable*', + 'kms:Get*', + 'kms:Delete*', + 'kms:ScheduleKeyDeletion', + 'kms:CancelKeyDeletion', + 'kms:GenerateDataKey', + 'kms:TagResource', + 'kms:UntagResource', + ], + resources: ['*'], + principals: [ + new iam.AccountPrincipal(this.account), + ], + effect: iam.Effect.ALLOW, + }), + new iam.PolicyStatement({ + actions: [ + 'kms:Decrypt', + 'kms:DescribeKey', + 'kms:Encrypt', + 'kms:ReEncrypt*', + 'kms:GenerateDataKey*', + ], + resources: ['*'], + principals: [ + new iam.AnyPrincipal(), + ], + conditions: { + StringEquals: { + 'kms:CallerAccount': this.account, + 'kms:ViaService': `s3.${this.partition}.amazonaws.com`, + }, + }, + effect: iam.Effect.ALLOW, + }), + // new iam.PolicyStatement({ + // actions: [ + // 'kms:Decrypt', + // 'kms:DescribeKey', + // 'kms:Encrypt', + // 'kms:ReEncrypt*', + // 'kms:GenerateDataKey*', + // ], + // resources: ['*'], + // principals: [ + // new iam.ArnPrincipal(this.getCreateFilePublishingRole()), + // ], + // effect: iam.Effect.ALLOW, + // }), + ], + }), + }); + return key; + } + private getCreateBucket() { - // Error: Resolution error: ID components may not include unresolved tokens: const stagingBucketName = this.stagingBucketName ?? `cdk-${this.account}-${this.region}-${this.appId.toLocaleLowerCase()}`; const bucketId = 'CdkStagingBucket'; const bucket = this.node.tryFindChild(bucketId) as s3.Bucket ?? new s3.Bucket(this, bucketId, { bucketName: stagingBucketName, - removalPolicy: RemovalPolicy.DESTROY, + removalPolicy: RemovalPolicy.RETAIN, + encryption: s3.BucketEncryption.KMS, + encryptionKey: this.createBucketKey(), }); return { bucketName: stagingBucketName, @@ -229,11 +323,11 @@ export class DefaultStagingStack extends Stack implements IStagingStack { throw new Error('Assets synthesized with AppScopedStagingSynthesizer must include a \'uniqueId\' in the asset source definition.'); } - const repoName = `${asset.uniqueId}repo`.replace('.', '-'); // TODO: actually sanitize + const repoName = `${asset.uniqueId}`.replace('.', '-'); // TODO: actually sanitize if (this.stagingRepos[asset.uniqueId] === undefined) { this.stagingRepos[asset.uniqueId] = new ecr.Repository(this, repoName, { repositoryName: repoName, - // TODO: lifecycle rules + lifecycleRules: this.repositoryLifecycleRules[asset.uniqueId], }); } return repoName; diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/package.json b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/package.json index 99bab16b328fa..43290ec939dab 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/package.json +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/package.json @@ -87,6 +87,7 @@ "@aws-cdk/cx-api": "0.0.0", "constructs": "^10.0.0", "@aws-cdk/aws-s3": "0.0.0", + "@aws-cdk/aws-kms": "0.0.0", "@aws-cdk/aws-ecr": "0.0.0", "@aws-cdk/aws-iam": "0.0.0" }, @@ -98,6 +99,7 @@ "@aws-cdk/aws-lambda": "0.0.0", "constructs": "^10.0.0", "@aws-cdk/aws-s3": "0.0.0", + "@aws-cdk/aws-kms": "0.0.0", "@aws-cdk/aws-ecr": "0.0.0", "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.ts index bf7e29809cd51..a08f1332a46fd 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.ts +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.ts @@ -4,7 +4,7 @@ import { App, Stack } from '@aws-cdk/core'; import { AppScopedStagingSynthesizer } from '../lib'; const app = new App({ - appId: 'app1', + appId: 'app2', }); const stack = new Stack(app, 'app-scoped-staging-test', { diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts index 7122ca4f39758..baa90c84b6114 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts @@ -155,7 +155,7 @@ describe(AppScopedStagingSynthesizer, () => { }); // THEN - we have a fixed asset location - const repo = 'abcdefrepo'; + const repo = 'abcdef'; expect(evalCFN(location.repositoryName)).toEqual(repo); expect(evalCFN(location.imageUri)).toEqual(`000000000000.dkr.ecr.us-east-1.domain.aws/${repo}:abcdef`); }); @@ -200,7 +200,7 @@ describe(AppScopedStagingSynthesizer, () => { }); // THEN - images share same ecr repo - const repo = 'abcdefrepo'; + const repo = 'abcdef'; expect(evalCFN(location1.repositoryName)).toEqual(repo); expect(evalCFN(location1.repositoryName)).toEqual(evalCFN(location2.repositoryName)); }); From 4085c79096dda89e4671fe9502b9396fa4d73093 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Tue, 21 Mar 2023 23:48:50 -0400 Subject: [PATCH 035/120] wip of kms key debacle --- .../lib/default-staging-stack.ts | 163 +++++++++++------- .../lib/synthesizer.ts | 7 +- .../test/integ.synthesizer.ts | 8 +- .../test/synthesizer.test.ts | 5 +- 4 files changed, 113 insertions(+), 70 deletions(-) diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts index 8da053bac491a..e64cd8130e6d3 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts @@ -150,52 +150,36 @@ export class DefaultStagingStack extends Stack implements IStagingStack { } /** - * Returns the well-known arn of the file publishing role + * Returns the file publishing role arn */ - private getCreateFilePublishingRole() { + private getFilePublishingRoleArn(): string { if (this.fileAssetPublishingRole) { return this.fileAssetPublishingRole.roleArn; } - - const roleId = 'CdkFilePublishingRole'; - const roleName = this.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_NAME; - - const createIamRole = () => { - const role = new iam.Role(this, roleId, { - roleName: roleName, - assumedBy: new iam.ServicePrincipal('sts.amazonaws.com'), - }); - const bucket = this.getCreateBucket(); - role.addToPolicy(new iam.PolicyStatement({ - actions: [ - 's3:GetObject*', - 's3:GetBucket*', - 's3:GetEncryptionConfiguration', - 's3:List*', - 's3:DeleteObject*', - 's3:PutObject*', - 's3:Abort*', - ], - resources: [ - bucket.bucketArn, - `${bucket.bucketArn}/*`, - ], - effect: iam.Effect.ALLOW, - })); - return role; - }; - - // Create the default role if it does not exist yet - this.node.tryFindChild(roleId) as iam.Role ?? createIamRole(); - return Arn.format({ - partition: this.partition ?? Aws.PARTITION, - account: this.account ?? Aws.ACCOUNT_ID, - region: this.region ?? Aws.REGION, + const role = this.node.tryFindChild('CdkFilePublishingRole') as iam.Role; + if (role === undefined) { + throw new Error('Cannot call getFilePublishingRoleArn before createFilePublishingRole'); + } + return Stack.of(this).formatArn({ + partition: 'aws', // TODO: token partition doesn't work + region: '', // iam is global service: 'iam', resource: 'role', - resourceName: roleName, - arnFormat: ArnFormat.COLON_RESOURCE_NAME, + resourceName: this.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_NAME, + arnFormat: ArnFormat.SLASH_RESOURCE_NAME, + }); + } + + /** + * Creates the file publishing role + */ + private createFilePublishingRole() { + const roleName = this.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_NAME; + const role = new iam.Role(this, 'CdkFilePublishingRole', { + roleName: roleName, + assumedBy: new iam.AccountPrincipal(this.account), }); + return role; } /** @@ -234,6 +218,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { private createBucketKey(): kms.IKey { const bucketKeyId = 'BucketKey'; const key = this.node.tryFindChild(bucketKeyId) as kms.IKey ?? new kms.Key(this, bucketKeyId, { + alias: 'StagingBucketKey', policy: new iam.PolicyDocument({ statements: [ new iam.PolicyStatement({ @@ -280,20 +265,20 @@ export class DefaultStagingStack extends Stack implements IStagingStack { }, effect: iam.Effect.ALLOW, }), - // new iam.PolicyStatement({ - // actions: [ - // 'kms:Decrypt', - // 'kms:DescribeKey', - // 'kms:Encrypt', - // 'kms:ReEncrypt*', - // 'kms:GenerateDataKey*', - // ], - // resources: ['*'], - // principals: [ - // new iam.ArnPrincipal(this.getCreateFilePublishingRole()), - // ], - // effect: iam.Effect.ALLOW, - // }), + new iam.PolicyStatement({ + actions: [ + 'kms:Decrypt', + 'kms:DescribeKey', + 'kms:Encrypt', + 'kms:ReEncrypt*', + 'kms:GenerateDataKey*', + ], + resources: ['*'], + principals: [ + new iam.ArnPrincipal(this.getFilePublishingRoleArn()), + ], + effect: iam.Effect.ALLOW, + }), ], }), }); @@ -303,16 +288,72 @@ export class DefaultStagingStack extends Stack implements IStagingStack { private getCreateBucket() { const stagingBucketName = this.stagingBucketName ?? `cdk-${this.account}-${this.region}-${this.appId.toLocaleLowerCase()}`; const bucketId = 'CdkStagingBucket'; - const bucket = this.node.tryFindChild(bucketId) as s3.Bucket ?? new s3.Bucket(this, bucketId, { + const createdBucket = this.node.tryFindChild(bucketId) as s3.Bucket; + if (createdBucket) { + return stagingBucketName; + } + + // Create the role that the KMS key depends on + const role = this.createFilePublishingRole(); + // Create the KMS key that the bucket depends on + const key = this.createBucketKey(); + key.node.addDependency(role); + const keyArn = Stack.of(this).formatArn({ + service: 'kms', + resource: 'key', + resourceName: 'StagingBucketKey', // TODO change name + arnFormat: ArnFormat.SLASH_RESOURCE_NAME, + }); + + // Create the bucket once the dependencies have been created + new s3.Bucket(this, bucketId, { bucketName: stagingBucketName, removalPolicy: RemovalPolicy.RETAIN, encryption: s3.BucketEncryption.KMS, - encryptionKey: this.createBucketKey(), + encryptionKey: key, }); - return { - bucketName: stagingBucketName, - bucketArn: bucket.bucketArn, - }; + const bucketArn = Stack.of(this).formatArn({ + region: '', + account: '', + service: 's3', + resource: 'bucket', + resourceName: stagingBucketName, + arnFormat: ArnFormat.COLON_RESOURCE_NAME, + }); + + // Add policy to bucket role. Need to use computed arn + // so we don't add a circular dependency + role.addToPolicy(new iam.PolicyStatement({ + actions: [ + 's3:GetObject*', + 's3:GetBucket*', + 's3:GetEncryptionConfiguration', + 's3:List*', + 's3:DeleteObject*', + 's3:PutObject*', + 's3:Abort*', + ], + resources: [ + bucketArn, + `${bucketArn}/*`, + ], + effect: iam.Effect.ALLOW, + })); + + // Need to use computed arn so we don't add a circular dependency. + role.addToPolicy(new iam.PolicyStatement({ + actions: [ + 'kms:Decrypt', + 'kms:DescribeKey', + 'kms:Encrypt', + 'kms:ReEncrypt*', + 'kms:GenerateDataKey*', + ], + resources: [keyArn], + effect: iam.Effect.ALLOW, + })); + + return stagingBucketName; } /** @@ -335,8 +376,8 @@ export class DefaultStagingStack extends Stack implements IStagingStack { public addFile(_asset: FileAssetSource): FileAssetInfo { return { - bucketName: this.getCreateBucket().bucketName, - assumeRoleArn: this.getCreateFilePublishingRole(), + bucketName: this.getCreateBucket(), + assumeRoleArn: this.getFilePublishingRoleArn(), }; } diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts index 63f02db6933f4..f3b7bf82d84f2 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts @@ -203,8 +203,11 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta public addFileAsset(asset: FileAssetSource): FileAssetLocation { const { bucketName, assumeRoleArn } = this.stagingStack.addFile(asset); const location = this.assetManifest.defaultAddFileAsset(this.boundStack, asset, { - bucketName, - role: { assumeRoleArn }, + bucketName: bucketName, + // bucketPrefix: bucketPrefix, + role: { + assumeRoleArn, + }, }); return this.cloudFormationLocationFromFileAsset(location); } diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.ts index a08f1332a46fd..b17129cbe52c3 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.ts +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.ts @@ -9,10 +9,10 @@ const app = new App({ const stack = new Stack(app, 'app-scoped-staging-test', { synthesizer: new AppScopedStagingSynthesizer(), - // env: { - // account: '489318732371', - // region: 'us-east-1', - // }, + env: { + account: '489318732371', + region: 'us-east-2', + }, }); new lambda.Function(stack, 'lambda', { diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts index baa90c84b6114..1d76364028250 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts @@ -30,7 +30,6 @@ describe(AppScopedStagingSynthesizer, () => { region: 'us-east-1', }, }); - // TODO: test with tokens }); test('stack template is in asset manifest', () => { @@ -62,7 +61,7 @@ describe(AppScopedStagingSynthesizer, () => { bucketName: `cdk-000000000000-us-east-1-${APP_ID.toLocaleLowerCase()}`, objectKey: templateObjectKey, region: 'us-east-1', - assumeRoleArn: `arn:${Aws.PARTITION}:iam:us-east-1:000000000000:role:cdk-file-publishing-role-us-east-1-${APP_ID}`, + assumeRoleArn: `arn:${Aws.PARTITION}:iam::000000000000:role/cdk-file-publishing-role-us-east-1-${APP_ID}`, }, }, }); @@ -110,7 +109,7 @@ describe(AppScopedStagingSynthesizer, () => { bucketName: `cdk-111111111111-us-east-2-${APP_ID.toLocaleLowerCase()}`, objectKey: templateObjectKey, region: 'us-east-2', - assumeRoleArn: `arn:${Aws.PARTITION}:iam:us-east-2:111111111111:role:cdk-file-publishing-role-us-east-2-${APP_ID}`, + assumeRoleArn: `arn:${Aws.PARTITION}:iam::111111111111:role/cdk-file-publishing-role-us-east-2-${APP_ID}`, }, }, }); From f4074b6f2d5ddab2dee0c777032855c46da2b3a4 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 22 Mar 2023 16:10:54 -0400 Subject: [PATCH 036/120] kms key works --- .../lib/default-staging-stack.ts | 127 ++---------------- .../lib/synthesizer.ts | 1 - .../test/synthesizer.test.ts | 10 +- packages/@aws-cdk/core/lib/assets.ts | 8 +- 4 files changed, 19 insertions(+), 127 deletions(-) diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts index e64cd8130e6d3..8cdf172a8b5c6 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts @@ -161,7 +161,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { throw new Error('Cannot call getFilePublishingRoleArn before createFilePublishingRole'); } return Stack.of(this).formatArn({ - partition: 'aws', // TODO: token partition doesn't work + partition: '${AWS::Partition}', // TODO: token partition doesn't work region: '', // iam is global service: 'iam', resource: 'role', @@ -218,69 +218,8 @@ export class DefaultStagingStack extends Stack implements IStagingStack { private createBucketKey(): kms.IKey { const bucketKeyId = 'BucketKey'; const key = this.node.tryFindChild(bucketKeyId) as kms.IKey ?? new kms.Key(this, bucketKeyId, { - alias: 'StagingBucketKey', - policy: new iam.PolicyDocument({ - statements: [ - new iam.PolicyStatement({ - actions: [ - 'kms:Create*', - 'kms:Describe*', - 'kms:Enable*', - 'kms:List*', - 'kms:Put*', - 'kms:Update*', - 'kms:Revoke*', - 'kms:Disable*', - 'kms:Get*', - 'kms:Delete*', - 'kms:ScheduleKeyDeletion', - 'kms:CancelKeyDeletion', - 'kms:GenerateDataKey', - 'kms:TagResource', - 'kms:UntagResource', - ], - resources: ['*'], - principals: [ - new iam.AccountPrincipal(this.account), - ], - effect: iam.Effect.ALLOW, - }), - new iam.PolicyStatement({ - actions: [ - 'kms:Decrypt', - 'kms:DescribeKey', - 'kms:Encrypt', - 'kms:ReEncrypt*', - 'kms:GenerateDataKey*', - ], - resources: ['*'], - principals: [ - new iam.AnyPrincipal(), - ], - conditions: { - StringEquals: { - 'kms:CallerAccount': this.account, - 'kms:ViaService': `s3.${this.partition}.amazonaws.com`, - }, - }, - effect: iam.Effect.ALLOW, - }), - new iam.PolicyStatement({ - actions: [ - 'kms:Decrypt', - 'kms:DescribeKey', - 'kms:Encrypt', - 'kms:ReEncrypt*', - 'kms:GenerateDataKey*', - ], - resources: ['*'], - principals: [ - new iam.ArnPrincipal(this.getFilePublishingRoleArn()), - ], - effect: iam.Effect.ALLOW, - }), - ], - }), + // TODO add alias + admins: [new iam.AccountPrincipal(this.account)], }); return key; } @@ -297,61 +236,15 @@ export class DefaultStagingStack extends Stack implements IStagingStack { const role = this.createFilePublishingRole(); // Create the KMS key that the bucket depends on const key = this.createBucketKey(); - key.node.addDependency(role); - const keyArn = Stack.of(this).formatArn({ - service: 'kms', - resource: 'key', - resourceName: 'StagingBucketKey', // TODO change name - arnFormat: ArnFormat.SLASH_RESOURCE_NAME, - }); // Create the bucket once the dependencies have been created - new s3.Bucket(this, bucketId, { + const bucket = new s3.Bucket(this, bucketId, { bucketName: stagingBucketName, removalPolicy: RemovalPolicy.RETAIN, encryption: s3.BucketEncryption.KMS, encryptionKey: key, }); - const bucketArn = Stack.of(this).formatArn({ - region: '', - account: '', - service: 's3', - resource: 'bucket', - resourceName: stagingBucketName, - arnFormat: ArnFormat.COLON_RESOURCE_NAME, - }); - - // Add policy to bucket role. Need to use computed arn - // so we don't add a circular dependency - role.addToPolicy(new iam.PolicyStatement({ - actions: [ - 's3:GetObject*', - 's3:GetBucket*', - 's3:GetEncryptionConfiguration', - 's3:List*', - 's3:DeleteObject*', - 's3:PutObject*', - 's3:Abort*', - ], - resources: [ - bucketArn, - `${bucketArn}/*`, - ], - effect: iam.Effect.ALLOW, - })); - - // Need to use computed arn so we don't add a circular dependency. - role.addToPolicy(new iam.PolicyStatement({ - actions: [ - 'kms:Decrypt', - 'kms:DescribeKey', - 'kms:Encrypt', - 'kms:ReEncrypt*', - 'kms:GenerateDataKey*', - ], - resources: [keyArn], - effect: iam.Effect.ALLOW, - })); + bucket.grantReadWrite(role); return stagingBucketName; } @@ -360,15 +253,15 @@ export class DefaultStagingStack extends Stack implements IStagingStack { * Returns the well-known name of the repo */ private getCreateRepo(asset: DockerImageAssetSource): string { - if (!asset.uniqueId) { + if (!asset.assetName) { throw new Error('Assets synthesized with AppScopedStagingSynthesizer must include a \'uniqueId\' in the asset source definition.'); } - const repoName = `${asset.uniqueId}`.replace('.', '-'); // TODO: actually sanitize - if (this.stagingRepos[asset.uniqueId] === undefined) { - this.stagingRepos[asset.uniqueId] = new ecr.Repository(this, repoName, { + const repoName = `${asset.assetName}`.replace('.', '-'); // TODO: actually sanitize + if (this.stagingRepos[asset.assetName] === undefined) { + this.stagingRepos[asset.assetName] = new ecr.Repository(this, repoName, { repositoryName: repoName, - lifecycleRules: this.repositoryLifecycleRules[asset.uniqueId], + lifecycleRules: this.repositoryLifecycleRules[asset.assetName], }); } return repoName; diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts index f3b7bf82d84f2..2ef7594df6495 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts @@ -168,7 +168,6 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta private getCreateStagingStack(app: App, env: Environment): IStagingStack { const stackName = `StagingStack${app._appId}`; const stackId = 'StagingStack'; - // TODO: this needs to be an IStagingStack const stagingStack = app.node.tryFindChild(stackId) as IStagingStack ?? new DefaultStagingStack(app, stackId, { env, stackName, diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts index 1d76364028250..a569494a094aa 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts @@ -150,7 +150,7 @@ describe(AppScopedStagingSynthesizer, () => { const location = stack.synthesizer.addDockerImageAsset({ directoryName: '.', sourceHash: 'abcdef', - uniqueId: 'abcdef', + assetName: 'abcdef', }); // THEN - we have a fixed asset location @@ -171,13 +171,13 @@ describe(AppScopedStagingSynthesizer, () => { const location1 = stack.synthesizer.addDockerImageAsset({ directoryName: '.', sourceHash: 'abcdef', - uniqueId: 'abcdef', + assetName: 'abcdef', }); const location2 = stack.synthesizer.addDockerImageAsset({ directoryName: './hello', sourceHash: 'abcdefg', - uniqueId: 'abcdefg', + assetName: 'abcdefg', }); // THEN - images have different asset locations @@ -189,13 +189,13 @@ describe(AppScopedStagingSynthesizer, () => { const location1 = stack.synthesizer.addDockerImageAsset({ directoryName: '.', sourceHash: 'abcdef', - uniqueId: 'abcdef', + assetName: 'abcdef', }); const location2 = stack.synthesizer.addDockerImageAsset({ directoryName: './hello', sourceHash: 'abcdefg', - uniqueId: 'abcdef', + assetName: 'abcdef', }); // THEN - images share same ecr repo diff --git a/packages/@aws-cdk/core/lib/assets.ts b/packages/@aws-cdk/core/lib/assets.ts index 185b40ea34863..bd0d4795cc8b5 100644 --- a/packages/@aws-cdk/core/lib/assets.ts +++ b/packages/@aws-cdk/core/lib/assets.ts @@ -244,12 +244,12 @@ export interface DockerImageAssetSource { /** * Unique identifier of the docker image asset and its potential revisions. - * Required if using Bootstrap v3. + * Required if using AppScopedStagingSynthesizer. * - * @default - no unique id + * @default - no asset name */ - readonly uniqueId?: string; - + readonly assetName?: string; + /* * Cache from options to pass to the `docker build` command. * From c3d1fff1f01c15b66e40a90ce6e1fc3f93a9bb84 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 22 Mar 2023 17:04:19 -0400 Subject: [PATCH 037/120] update readme for new api --- .../README.md | 166 ++++++++---------- 1 file changed, 75 insertions(+), 91 deletions(-) diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/README.md b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/README.md index 5817b44a4f991..30ee6cb4f27d4 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/README.md +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/README.md @@ -24,7 +24,7 @@ built with this new model of bootstrapping. ## Bootstrap Model -Our most current bootstrap model looks like this, when you run `cdk bootstrap aws:///` : +Our current bootstrap model looks like this, when you run `cdk bootstrap aws:///` : ```mermaid graph TD @@ -45,6 +45,8 @@ The Staging Stack will contain, on a per-need basis, - An ECR Repository _per_ image (and it's revisions). - IAM roles with access to the Bucket and Repositories. +> The Staging Stack is being actively worked on right now and currently does not support image assets. + ```mermaid graph TD A(Bootstrap Stack) --- B(CloudFormationExecutionRole
DeploymentActionRole
LookupRole) @@ -70,140 +72,122 @@ benefits: > As this library is `experimental`, the accompanying Bootstrap Stack is not yet implemented. To use this > library right now, you must reuse roles that have been traditionally bootstrapped. -## Basic Example +## Synthesizer To use this library, supply the `AppScopedStagingSynthesizer` in as the default synthesizer to the app. This will ensure that a Staging Stack will be created next to the CDK App to hold the staging resources. -In this example, the Staging Stack is the `DefaultStagingStack` construct. - -```ts -import * as path from 'path'; -import * as lambda from 'aws-cdk-lib/aws-lambda'; -import { App, Stack } from 'aws-cdk-lib'; -import { AppScopedStagingSynthesizer } from 'aws-cdk-lib/core-app-scoped-staging-synthesizer'; - -const app = new App({ - appId: 'my-app-id', - defaultSynthesizer: new AppScopedStagingSynthesizer(), -}); -const stack = new Stack(app, 'my-stack'); +`AppScopedStagingSynthesizer` comes with static methods covering the use-cases for the synthesizer. -new lambda.Function(stack, 'lambda', { - code: lambda.AssetCode.fromAsset(path.join(__dirname, 'assets')), - handler: 'index.handler', - runtime: lambda.Runtime.PYTHON_3_9, -}); +### Using the Default Staging Stack per Environment -app.synth(); -``` - -Every CDK App that uses the `AppScopedStagingSynthesizer` must include an `appId`. This should -be an identifier unique to the app and is used to differentiate staging resources associated -with the app. - -## Custom Roles - -You can customize some or all of the roles you'd like to use in the synthesizer as well, -if all you need is to supply custom roles (and not change anything else in the `DefaultStagingStack`): +The most common use case will be to use the built-in `DefaultStagingStack` on a per-environment basis. +That means that in each environment the CDK App is deployed to, the synthesizer will create a +Staging Stack to store its resources. To use this kind of synthesizer, use +`AppScopedStagingSynthesizer.stackPerEnv()`. ```ts import { App } from 'aws-cdk-lib'; -import { AppScopedStagingSynthesizer, BootstrapRole } from 'aws-cdk-lib/core-app-scoped-staging-synthesizer'; +import { AppScopedStagingSynthesizer } from '@aws-cdk/core-app-scoped-staging-synthesizer'; const app = new App({ - appId: 'my-app-id', - defaultSynthesizer: new AppScopedStagingSynthesizer({ - roles: { - cloudFormationExecutionRole: BoostrapRole.fromRoleArn('arn'), - deploymentActionRole: BootstrapRole.fromRoleArn('arn'), - lookupRole: BoostrapRole.fromRoleArn('arn'), - fileAssetPublishingRole: BootstrapRole.fromRoleArn('arn'), - imageAssetPublishingRole: BootstrapRole.fromRoleArn('arn'), - }, + defaultSynthesizer: AppScopedStagingSynthesizer.stackPerEnv({ + appId: 'my-app-id', }), }); ``` -You can also supply specific roles to the Staging Stack: +### Using a Custom Staging Stack per Environment + +To use a custom stack, but still on a per-environment basis, use `AppScopedStagingSynthesizer.customFactory()`. +This has the benefit of providing a custom Staging Stack that can be created in every environment the CDK App +is deployed to. ```ts import { App } from 'aws-cdk-lib'; -import { AppScopedStagingSynthesizer, BootstrapRole } from 'aws-cdk-lib/core-app-scoped-staging-synthesizer'; +import { AppScopedStagingSynthesizer } from '@aws-cdk/core-app-scoped-staging-synthesizer'; const app = new App({ - appId: 'my-app-id', - defaultSynthesizer: new AppScopedStagingSynthesizer({ - stagingStack: new DefaultStagingStack(this, 'StagingStack', { - fileAssetPublishingRole: BootstrapRole.fromRoleArn('arn'), - dockerAssetPublishingRole: BootstrapRole.fromRoleArn('arn'), - // additional properties - }), + defaultSynthesizer: AppScopedStagingSynthesizer.customFactory({ + createStack() { + return new CustomStagingStack({ appId: 'my-app-id' }), + }, }), }); ``` -## Custom Staging Stack +### Using an Existing Staging Stack -You can supply your own staging stack if you need further customization from what the -`DefaultStagingStack` has to offer. Simply implement the `IStagingStack` interface and -supply your custom construct in as the `defaultSynthesizer`. +If you need to pass in an existing stack as the Staging Stack to the CDK App, use +`AppScopedStagingSynthesizer.customStack()`. Make sure that the custom stack you provide implements +`IStagingStack`. ```ts -import { App } from 'aws-cdk-lib'; -import { AppScopedStagingSynthesizer } from 'aws-cdk-lib/core-app-scoped-staging-synthesizer'; +import { App, Stack } from 'aws-cdk-lib'; +import { AppScopedStagingSynthesizer, IStagingStack } from '@aws-cdk/core-app-scoped-staging-synthesizer'; class CustomStagingStack implements IStagingStack { // ... } const app = new App({ - defaultSynthesizer = new AppScopedStagingSynthesizer({ - stagingStack: new CustomStagingStack(), - }), + defaultSynthesizer: AppScopedStagingSynthesizer.customStack(new CustomStagingStack(this, 'StagingStack')), }); ``` -## Repository Lifecycle Rules +## Default Staging Stack + +> The Default Staging Stack is being actively worked on right now and currently does not support image assets. -You can specify your own lifecycle rules on images. In the example below, we set lifecycle rules for both -images by referencing their `imageId`. `imageId` is a required property on docker assets that use the -`DefaultStagingStack`. +The default Staging Stack includes all the staging resources necessary for CDK Assets. The below example +is of a CDK App using the `AppScopedStagingSynthesizer` and creating a file asset for the Lambda Function +source code. As part of the `DefaultStagingStack`, an s3 bucket and iam role will be created that will be +used to upload the asset to s3. ```ts -import { App, Stack, Duration } from 'aws-cdk-lib'; -import { AppScopedStagingSynthesizer, BootstrapRole } from 'aws-cdk-lib/core-app-scoped-staging-synthesizer'; -import * as ecr_assets from 'aws-cdk-lib/aws-ecr-assets'; import * as path from 'path'; +import * as lambda from 'aws-cdk-lib/aws-lambda'; +import { App, Stack } from 'aws-cdk-lib'; +import { AppScopedStagingSynthesizer } from 'aws-cdk-lib/core-app-scoped-staging-synthesizer'; -const IMAGE_ID = 'image-id'; -const IMAGE_ID_2 = 'image-id-2'; const app = new App({ - appId: 'my-app-id', - defaultSynthesizer: new AppScopedStagingSynthesizer({ - stagingStack: new DefaultStagingStack(this, 'StagingStack', { - repositoryLifecycleRules: [{ - assets: [IMAGE_ID], - lifecycleRules: [{ - { maxImageCount: 3 }, - }], - }, { - asset: [IMAGE_ID, IMAGE_ID_2], - lifecycleRules: [{ - expiration: Duration.days(30), - }], - }], - }), - }), + defaultSynthesizer: new AppScopedStagingSynthesizer.stackPerEnv({appId: 'my-app-id'}), }); const stack = new Stack(app, 'my-stack'); -const dockerAsset = new ecr_assets.DockerImageAsset(stack, 'Assets', { - imageId: IMAGE_ID, - directory: path.join(__dirname, './docker.assets'), + +new lambda.Function(stack, 'lambda', { + code: lambda.AssetCode.fromAsset(path.join(__dirname, 'assets')), + handler: 'index.handler', + runtime: lambda.Runtime.PYTHON_3_9, }); -const dockerAsset2 = new ecr_assets.DockerImageAsset(stack, 'Assets2', { - imageId: IMAGE_ID_2, - directory: path.join(__dirname, './docker2.assets'), + +app.synth(); +``` + +Every CDK App that uses the `DefaultStagingStack` must include an `appId`. This should +be an identifier unique to the app and is used to differentiate staging resources associated +with the app. + +### Custom Roles + +You can customize some or all of the roles you'd like to use in the synthesizer as well, +if all you need is to supply custom roles (and not change anything else in the `DefaultStagingStack`): + +```ts +import { App } from 'aws-cdk-lib'; +import { AppScopedStagingSynthesizer, BootstrapRole } from 'aws-cdk-lib/core-app-scoped-staging-synthesizer'; + +const app = new App({ + defaultSynthesizer: new AppScopedStagingSynthesizer.stackFromEnv({ + appId: 'my-app-id', + roles: { + cloudFormationExecutionRole: BoostrapRole.fromRoleArn('arn'), + deploymentActionRole: BootstrapRole.fromRoleArn('arn'), + lookupRole: BoostrapRole.fromRoleArn('arn'), + fileAssetPublishingRole: BootstrapRole.fromRoleArn('arn'), + imageAssetPublishingRole: BootstrapRole.fromRoleArn('arn'), + }, + }), }); -``` \ No newline at end of file +``` From 827fea1afdd438c12db5fdae88c39201ee2de57b Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 22 Mar 2023 17:08:26 -0400 Subject: [PATCH 038/120] remove app chnages --- packages/@aws-cdk/core/lib/app.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/packages/@aws-cdk/core/lib/app.ts b/packages/@aws-cdk/core/lib/app.ts index 078a87bf6931b..7e01ebf1779af 100644 --- a/packages/@aws-cdk/core/lib/app.ts +++ b/packages/@aws-cdk/core/lib/app.ts @@ -118,13 +118,6 @@ export interface AppProps { * @default - A `DefaultStackSynthesizer` with default settings */ readonly defaultStackSynthesizer?: IReusableStackSynthesizer; - - /** - * A unique identifier for the app. - * - * @default - no identifier - */ - readonly appId?: string; } /** @@ -159,13 +152,6 @@ export class App extends Stage { */ public readonly _treeMetadata: boolean; - /** - * Id of the app - * - * @internal - */ - public readonly _appId?: string; - /** * Initializes a CDK application. * @param props initialization properties From b77eaf9b8cf32ac279c9accde83c428fa1d148ee Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 22 Mar 2023 17:09:23 -0400 Subject: [PATCH 039/120] remove app chnages --- packages/@aws-cdk/core/lib/app.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/@aws-cdk/core/lib/app.ts b/packages/@aws-cdk/core/lib/app.ts index 7e01ebf1779af..1ae43828c8c3e 100644 --- a/packages/@aws-cdk/core/lib/app.ts +++ b/packages/@aws-cdk/core/lib/app.ts @@ -173,8 +173,6 @@ export class App extends Stage { this.node.setContext(PRIVATE_CONTEXT_DEFAULT_STACK_SYNTHESIZER, props.defaultStackSynthesizer); } - this._appId = props.appId; - const analyticsReporting = props.analyticsReporting ?? props.runtimeInfo; if (analyticsReporting !== undefined) { From b8cd873c5d29a82bb02cd746a64fa2c6c7d5a5be Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 22 Mar 2023 17:27:12 -0400 Subject: [PATCH 040/120] appid --- .../lib/default-staging-stack.ts | 14 +++++++++----- .../lib/synthesizer.ts | 14 +++++++++++--- .../test/integ.synthesizer.ts | 6 ++---- .../test/synthesizer.test.ts | 5 ++--- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts index 8cdf172a8b5c6..7557154ff02a6 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts @@ -52,6 +52,11 @@ export interface IStagingStack extends IConstruct { * Default Staging Stack Properties */ export interface DefaultStagingStackProps extends StackProps { + /** + * App id + */ + readonly appId: string; + /** * Explicit name for the staging bucket * @@ -73,6 +78,9 @@ export interface DefaultStagingStackProps extends StackProps { */ readonly dockerAssetPublishingRole?: BootstrapRole; + /** + * Repository lifecycle rules (not fully implemented) + */ readonly repositoryLifecycleRules?: StagingRepoLifecycleRule[]; } @@ -122,11 +130,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { synthesizer: new BootstraplessSynthesizer(), }); - if (scope._appId === undefined) { - throw new Error('DefaultStagingStack can only be used on Apps with a user-specified appId, but no appId found.'); - } - - this.appId = scope._appId; + this.appId = props.appId; this.dependencyStack = this; this.stagingBucketName = props.stagingBucketName; diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts index 2ef7594df6495..fecda3bce00ff 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts @@ -58,6 +58,11 @@ export interface BootstrapRoles { * New stack synthesizer properties */ export interface AppScopedStagingSynthesizerProps { + /** + * App identifier that is unique to the app and used in the resource names of the Staging Stack. + */ + readonly appId: string; + /** * Bring a custom staging stack into the app. * @@ -77,7 +82,7 @@ export interface AppScopedStagingSynthesizerProps { * App Scoped Staging Stack Synthesizer */ export class AppScopedStagingSynthesizer extends StackSynthesizer implements IReusableStackSynthesizer { - constructor(private readonly props: AppScopedStagingSynthesizerProps = {}) { + constructor(private readonly props: AppScopedStagingSynthesizerProps) { super(); for (const key in props) { @@ -143,11 +148,13 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta private readonly deploymentActionRoleArn?: string; private readonly fileAssetPublishingRole?: BootstrapRole; private readonly dockerAssetPublishingRole?: BootstrapRole; + private readonly appId: string; - constructor(private readonly stack: Stack, props: AppScopedStagingSynthesizerProps = {}) { + constructor(private readonly stack: Stack, props: AppScopedStagingSynthesizerProps) { super(); super.bind(stack); + this.appId = props.appId; this.lookupRoleArn = props.bootstrapRoles?.lookupRole?.roleArn; this.cloudFormationExecutionRoleArn = props.bootstrapRoles?.cloudFormationExecutionRole?.roleArn; this.deploymentActionRoleArn = props.bootstrapRoles?.deploymentActionRole?.roleArn; @@ -166,9 +173,10 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta } private getCreateStagingStack(app: App, env: Environment): IStagingStack { - const stackName = `StagingStack${app._appId}`; + const stackName = `StagingStack${this.appId}`; const stackId = 'StagingStack'; const stagingStack = app.node.tryFindChild(stackId) as IStagingStack ?? new DefaultStagingStack(app, stackId, { + appId: this.appId, env, stackName, fileAssetPublishingRole: this.fileAssetPublishingRole, diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.ts index b17129cbe52c3..a90a64fc74e38 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.ts +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.ts @@ -3,12 +3,10 @@ import * as lambda from '@aws-cdk/aws-lambda'; import { App, Stack } from '@aws-cdk/core'; import { AppScopedStagingSynthesizer } from '../lib'; -const app = new App({ - appId: 'app2', -}); +const app = new App(); const stack = new Stack(app, 'app-scoped-staging-test', { - synthesizer: new AppScopedStagingSynthesizer(), + synthesizer: new AppScopedStagingSynthesizer({ appId: 'app-id' }), env: { account: '489318732371', region: 'us-east-2', diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts index a569494a094aa..1e94ed0854ffe 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts @@ -21,7 +21,6 @@ describe(AppScopedStagingSynthesizer, () => { beforeEach(() => { app = new App({ - appId: APP_ID, defaultStackSynthesizer: new TestAppScopedStagingSynthesizer(), }); stack = new Stack(app, 'Stack', { @@ -69,7 +68,6 @@ describe(AppScopedStagingSynthesizer, () => { test('stack template is in the asset manifest - environment tokens', () => { const app2 = new App({ - appId: APP_ID, defaultStackSynthesizer: new TestAppScopedStagingSynthesizer(), }); const accountToken = Token.asString('111111111111'); @@ -251,8 +249,8 @@ describe(AppScopedStagingSynthesizer, () => { describe('Custom Roles on AppScopedStagingSynthesizer', () => { test('Can supply different roles', () => { const app = new App({ - appId: APP_ID, defaultStackSynthesizer: new AppScopedStagingSynthesizer({ + appId: APP_ID, bootstrapRoles: { cloudFormationExecutionRole: BootstrapRole.fromRoleArn(CLOUDFORMATION_EXECUTION_ROLE), lookupRole: BootstrapRole.fromRoleArn(LOOKUP_ROLE), @@ -304,6 +302,7 @@ function last
(xs?: A[]): A | undefined { class TestAppScopedStagingSynthesizer extends AppScopedStagingSynthesizer { public constructor(props: Partial = {}) { super({ + appId: props.appId ?? APP_ID, bootstrapRoles: { cloudFormationExecutionRole: BootstrapRole.fromRoleArn(CLOUDFORMATION_EXECUTION_ROLE), deploymentActionRole: BootstrapRole.fromRoleArn(DEPLOY_ACTION_ROLE), From 7d115cafb6850b577b570756525fef8ce3df1536 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 23 Mar 2023 10:02:29 -0400 Subject: [PATCH 041/120] massive changes to api but tests succeed --- .../lib/default-staging-stack.ts | 2 +- .../lib/synthesizer.ts | 140 ++++++++++++------ .../test/integ.synthesizer.ts | 2 +- .../test/synthesizer.test.ts | 56 ++----- 4 files changed, 111 insertions(+), 89 deletions(-) diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts index 7557154ff02a6..ff6ce15e1dbf3 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts @@ -1,7 +1,7 @@ import * as ecr from '@aws-cdk/aws-ecr'; import * as iam from '@aws-cdk/aws-iam'; -import * as s3 from '@aws-cdk/aws-s3'; import * as kms from '@aws-cdk/aws-kms'; +import * as s3 from '@aws-cdk/aws-s3'; import { App, Arn, ArnFormat, Aws, BootstraplessSynthesizer, DockerImageAssetSource, FileAssetSource, RemovalPolicy, Stack, StackProps } from '@aws-cdk/core'; import { IConstruct } from 'constructs'; import { BootstrapRole } from './synthesizer'; diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts index fecda3bce00ff..a792868e1bb4d 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts @@ -4,7 +4,6 @@ import { AssetManifestBuilder, DockerImageAssetLocation, DockerImageAssetSource, - Environment, FileAssetLocation, FileAssetSource, IBoundStackSynthesizer, @@ -50,28 +49,74 @@ export interface BootstrapRoles { readonly cloudFormationExecutionRole?: BootstrapRole; readonly deploymentActionRole?: BootstrapRole; readonly lookupRole?: BootstrapRole; +} + +export interface StagingRoles { readonly fileAssetPublishingRole?: BootstrapRole; readonly dockerAssetPublishingRole?: BootstrapRole; } /** - * New stack synthesizer properties + * Properties for stackPerEnv static method */ -export interface AppScopedStagingSynthesizerProps { +export interface StackPerEnvProps { /** * App identifier that is unique to the app and used in the resource names of the Staging Stack. */ readonly appId: string; /** - * Bring a custom staging stack into the app. + * Custom roles to bring into the staging stack. + * + * @default - no custom roles + */ + readonly stagingRoles?: StagingRoles; + + /** + * Custom bootstrap roles that have permissions to interact with CloudFormation + * on your behalf. * - * @default - default staging stack + * @default - no custom roles + */ + readonly bootstrapRoles?: BootstrapRoles; +} + +/** + * Staging Stack Factory interface. + * + * The function included in this class will be called by the synthesizer + * to create or reference an IStagingStack that has the necessary + * staging resources for the Stack. + */ +export interface IStagingStackFactory { + stagingStackFactory(boundStack: Stack): IStagingStack; +} + +/** + * Properties for customFactory static method + */ +export interface CustomFactoryProps extends AppScopedStagingSynthesizerProps { + /** + * Include rules that create a new Staging Stack per environment. + * + * @default true + */ + oncePerEnv: boolean; +} + +/** + * Internal properties for AppScopedStagingSynthesizer + */ +interface AppScopedStagingSynthesizerProps { + /** + * A factory method that creates an IStagingStack when given the stack the + * synthesizer is binding. */ - readonly stagingStack?: IStagingStack; + readonly stagingStackFactory: IStagingStackFactory; /** - * Custom roles + * Custom bootstrap roles that have permissions to interact with CloudFormation + * on your behalf. * * @default - no custom roles */ @@ -82,16 +127,14 @@ export interface AppScopedStagingSynthesizerProps { * App Scoped Staging Stack Synthesizer */ export class AppScopedStagingSynthesizer extends StackSynthesizer implements IReusableStackSynthesizer { - constructor(private readonly props: AppScopedStagingSynthesizerProps) { - super(); - + public static stackPerEnv(props: StackPerEnvProps) { for (const key in props) { if (props.hasOwnProperty(key)) { - validateNoToken(key as keyof AppScopedStagingSynthesizerProps); + validateNoToken(key as keyof StackPerEnvProps); } } - function validateNoToken(key: A) { + function validateNoToken(key: A) { const prop = props[key]; if (typeof prop === 'string' && Token.isUnresolved(prop)) { throw new Error(`AppScopedStagingSynthesizer property '${key}' cannot contain tokens; only the following placeholder strings are allowed: ` + [ @@ -102,6 +145,46 @@ export class AppScopedStagingSynthesizer extends StackSynthesizer implements IRe ].join(', ')); } } + + return new AppScopedStagingSynthesizer({ + bootstrapRoles: props.bootstrapRoles, + stagingStackFactory: { + stagingStackFactory(boundStack: Stack) { + const app = App.of(boundStack); + if (!App.isApp(app)) { + throw new Error(`Stack ${boundStack.stackName} must be part of an App`); + } + + let stackId = 'StagingStack'; + if (!Token.isUnresolved(boundStack.account) && !Token.isUnresolved(boundStack.region)) { + stackId = stackId + boundStack.account + boundStack.region; + } + + const stackName = `StagingStack${props.appId}`; + const stagingStack = app.node.tryFindChild(stackId) as IStagingStack ?? new DefaultStagingStack(app, stackId, { + appId: props.appId, + env: { + account: boundStack.account, + region: boundStack.region, + }, + stackName, + fileAssetPublishingRole: props.stagingRoles?.fileAssetPublishingRole, + dockerAssetPublishingRole: props.stagingRoles?.dockerAssetPublishingRole, + }); + boundStack.addDependency(stagingStack.dependencyStack, 'stack depends on the staging stack for staging resources'); + + return stagingStack; + }, + }, + }); + } + + public static customFactory(props: CustomFactoryProps) { + return new AppScopedStagingSynthesizer(props); + } + + private constructor(private readonly props: AppScopedStagingSynthesizerProps) { + super(); } /** @@ -146,45 +229,16 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta private readonly lookupRoleArn?: string; private readonly cloudFormationExecutionRoleArn?: string; private readonly deploymentActionRoleArn?: string; - private readonly fileAssetPublishingRole?: BootstrapRole; - private readonly dockerAssetPublishingRole?: BootstrapRole; - private readonly appId: string; - constructor(private readonly stack: Stack, props: AppScopedStagingSynthesizerProps) { + constructor(stack: Stack, props: AppScopedStagingSynthesizerProps) { super(); super.bind(stack); - this.appId = props.appId; this.lookupRoleArn = props.bootstrapRoles?.lookupRole?.roleArn; this.cloudFormationExecutionRoleArn = props.bootstrapRoles?.cloudFormationExecutionRole?.roleArn; this.deploymentActionRoleArn = props.bootstrapRoles?.deploymentActionRole?.roleArn; - this.fileAssetPublishingRole = props.bootstrapRoles?.fileAssetPublishingRole; - this.dockerAssetPublishingRole = props.bootstrapRoles?.dockerAssetPublishingRole; - - const app = App.of(stack); - if (!App.isApp(app)) { - throw new Error(`Stack ${stack.stackName} must be part of an App`); - } - - this.stagingStack = props.stagingStack ?? this.getCreateStagingStack(app, { - account: stack.account, - region: stack.region, - }); - } - - private getCreateStagingStack(app: App, env: Environment): IStagingStack { - const stackName = `StagingStack${this.appId}`; - const stackId = 'StagingStack'; - const stagingStack = app.node.tryFindChild(stackId) as IStagingStack ?? new DefaultStagingStack(app, stackId, { - appId: this.appId, - env, - stackName, - fileAssetPublishingRole: this.fileAssetPublishingRole, - dockerAssetPublishingRole: this.dockerAssetPublishingRole, - }); - this.stack.addDependency(stagingStack.dependencyStack, 'reason'); - return stagingStack; + this.stagingStack = props.stagingStackFactory.stagingStackFactory(stack); } public synthesize(session: ISynthesisSession): void { diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.ts index a90a64fc74e38..0368b754bcfcd 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.ts +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.ts @@ -6,7 +6,7 @@ import { AppScopedStagingSynthesizer } from '../lib'; const app = new App(); const stack = new Stack(app, 'app-scoped-staging-test', { - synthesizer: new AppScopedStagingSynthesizer({ appId: 'app-id' }), + synthesizer: AppScopedStagingSynthesizer.stackPerEnv({ appId: 'app-id' }), env: { account: '489318732371', region: 'us-east-2', diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts index 1e94ed0854ffe..2f9c7448f160f 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts +++ b/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts @@ -1,9 +1,9 @@ import * as fs from 'fs'; import * as cxschema from '@aws-cdk/cloud-assembly-schema'; -import { App, Stack, CfnResource, FileAssetPackaging, Aws, FileAssetSource, DockerImageAssetSource, Token } from '@aws-cdk/core'; +import { App, Stack, CfnResource, FileAssetPackaging, Token } from '@aws-cdk/core'; import { evaluateCFN } from '@aws-cdk/core/test/evaluate-cfn'; import * as cxapi from '@aws-cdk/cx-api'; -import { AppScopedStagingSynthesizer, AppScopedStagingSynthesizerProps, BootstrapRole, DockerAssetInfo, FileAssetInfo, IStagingStack } from '../lib'; +import { AppScopedStagingSynthesizer, BootstrapRole, StackPerEnvProps } from '../lib'; const CFN_CONTEXT = { 'AWS::Region': 'the_region', @@ -21,7 +21,7 @@ describe(AppScopedStagingSynthesizer, () => { beforeEach(() => { app = new App({ - defaultStackSynthesizer: new TestAppScopedStagingSynthesizer(), + defaultStackSynthesizer: TestAppScopedStagingSynthesizer.stackPerEnv(), }); stack = new Stack(app, 'Stack', { env: { @@ -60,7 +60,7 @@ describe(AppScopedStagingSynthesizer, () => { bucketName: `cdk-000000000000-us-east-1-${APP_ID.toLocaleLowerCase()}`, objectKey: templateObjectKey, region: 'us-east-1', - assumeRoleArn: `arn:${Aws.PARTITION}:iam::000000000000:role/cdk-file-publishing-role-us-east-1-${APP_ID}`, + assumeRoleArn: `arn:\${AWS::Partition}:iam::000000000000:role/cdk-file-publishing-role-us-east-1-${APP_ID}`, }, }, }); @@ -68,7 +68,7 @@ describe(AppScopedStagingSynthesizer, () => { test('stack template is in the asset manifest - environment tokens', () => { const app2 = new App({ - defaultStackSynthesizer: new TestAppScopedStagingSynthesizer(), + defaultStackSynthesizer: TestAppScopedStagingSynthesizer.stackPerEnv(), }); const accountToken = Token.asString('111111111111'); const regionToken = Token.asString('us-east-2'); @@ -107,7 +107,7 @@ describe(AppScopedStagingSynthesizer, () => { bucketName: `cdk-111111111111-us-east-2-${APP_ID.toLocaleLowerCase()}`, objectKey: templateObjectKey, region: 'us-east-2', - assumeRoleArn: `arn:${Aws.PARTITION}:iam::111111111111:role/cdk-file-publishing-role-us-east-2-${APP_ID}`, + assumeRoleArn: `arn:\${AWS::Partition}:iam::111111111111:role/cdk-file-publishing-role-us-east-2-${APP_ID}`, }, }, }); @@ -202,40 +202,6 @@ describe(AppScopedStagingSynthesizer, () => { expect(evalCFN(location1.repositoryName)).toEqual(evalCFN(location2.repositoryName)); }); - test('throws when App uses DefaultStagingStack and does not have appId', () => { - const app2 = new App({ - defaultStackSynthesizer: new TestAppScopedStagingSynthesizer(), - }); - expect(() => new Stack(app2, 'Stack')).toThrowError('DefaultStagingStack can only be used on Apps with a user-specified appId, but no appId found.'); - }); - - test('does not throw when App does not have appId and does not use DefaultStagingStack', () => { - class TestStagingStack extends Stack implements IStagingStack { - readonly appId = 'appId'; - readonly stagingRepos = {}; - readonly dependencyStack = this; - addFile(_asset: FileAssetSource): FileAssetInfo { - return { - assumeRoleArn: 'assumeRoleArn', - bucketName: 'bucketName', - }; - } - addDockerImage(_asset: DockerImageAssetSource): DockerAssetInfo { - return { - assumeRoleArn: 'assumeRoleArn', - repoName: 'repoName', - }; - } - } - - const app2 = new App({ - defaultStackSynthesizer: new TestAppScopedStagingSynthesizer({ - stagingStack: new TestStagingStack(), - }), - }); - expect(() => new Stack(app2, 'Stack')).not.toThrow(); - }); - /** * Evaluate a possibly string-containing value the same way CFN would do * @@ -249,12 +215,14 @@ describe(AppScopedStagingSynthesizer, () => { describe('Custom Roles on AppScopedStagingSynthesizer', () => { test('Can supply different roles', () => { const app = new App({ - defaultStackSynthesizer: new AppScopedStagingSynthesizer({ + defaultStackSynthesizer: AppScopedStagingSynthesizer.stackPerEnv({ appId: APP_ID, bootstrapRoles: { cloudFormationExecutionRole: BootstrapRole.fromRoleArn(CLOUDFORMATION_EXECUTION_ROLE), lookupRole: BootstrapRole.fromRoleArn(LOOKUP_ROLE), deploymentActionRole: BootstrapRole.fromRoleArn(DEPLOY_ACTION_ROLE), + }, + stagingRoles: { fileAssetPublishingRole: BootstrapRole.fromRoleArn('arn'), dockerAssetPublishingRole: BootstrapRole.fromRoleArn('arn'), }, @@ -299,9 +267,9 @@ function last(xs?: A[]): A | undefined { return xs ? xs[xs.length - 1] : undefined; } -class TestAppScopedStagingSynthesizer extends AppScopedStagingSynthesizer { - public constructor(props: Partial = {}) { - super({ +class TestAppScopedStagingSynthesizer { + public static stackPerEnv(props: Partial = {}): AppScopedStagingSynthesizer { + return AppScopedStagingSynthesizer.stackPerEnv({ appId: props.appId ?? APP_ID, bootstrapRoles: { cloudFormationExecutionRole: BootstrapRole.fromRoleArn(CLOUDFORMATION_EXECUTION_ROLE), From 57547122f6863056847a0ef61f6b4d87ba24781b Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 23 Mar 2023 12:29:33 -0400 Subject: [PATCH 042/120] rename folder --- .../.eslintrc.js | 0 .../.gitignore | 0 .../.npmignore | 0 .../LICENSE | 0 .../NOTICE | 0 .../README.md | 34 +++++++++---------- .../adr/resource-names.md | 0 .../jest.config.js | 0 .../lib/default-staging-stack.ts | 0 .../lib/index.ts | 0 .../lib/synthesizer.ts | 18 +++++----- .../package.json | 19 ++++++----- .../test/assets/index.py | 0 .../StagingStack.assets.json | 0 .../StagingStack.template.json | 0 .../app-scoped-staging-test.assets.json | 0 .../app-scoped-staging-test.template.json | 0 .../__entrypoint__.js | 0 .../index.js | 0 .../integ.synthesizer.js.snapshot/cdk.out | 0 .../integ.synthesizer.js.snapshot/integ.json | 0 .../manifest.json | 0 ...efaultTestDeployAssert208D3414.assets.json | 0 ...aultTestDeployAssert208D3414.template.json | 0 .../integ.synthesizer.js.snapshot/tree.json | 0 .../test/integ.synthesizer.ts | 4 +-- .../test/synthesizer.test.ts | 10 +++--- 27 files changed, 43 insertions(+), 42 deletions(-) rename packages/@aws-cdk/{core-app-scoped-staging-synthesizer => app-staging-synthesizer}/.eslintrc.js (100%) rename packages/@aws-cdk/{core-app-scoped-staging-synthesizer => app-staging-synthesizer}/.gitignore (100%) rename packages/@aws-cdk/{core-app-scoped-staging-synthesizer => app-staging-synthesizer}/.npmignore (100%) rename packages/@aws-cdk/{core-app-scoped-staging-synthesizer => app-staging-synthesizer}/LICENSE (100%) rename packages/@aws-cdk/{core-app-scoped-staging-synthesizer => app-staging-synthesizer}/NOTICE (100%) rename packages/@aws-cdk/{core-app-scoped-staging-synthesizer => app-staging-synthesizer}/README.md (81%) rename packages/@aws-cdk/{core-app-scoped-staging-synthesizer => app-staging-synthesizer}/adr/resource-names.md (100%) rename packages/@aws-cdk/{core-app-scoped-staging-synthesizer => app-staging-synthesizer}/jest.config.js (100%) rename packages/@aws-cdk/{core-app-scoped-staging-synthesizer => app-staging-synthesizer}/lib/default-staging-stack.ts (100%) rename packages/@aws-cdk/{core-app-scoped-staging-synthesizer => app-staging-synthesizer}/lib/index.ts (100%) rename packages/@aws-cdk/{core-app-scoped-staging-synthesizer => app-staging-synthesizer}/lib/synthesizer.ts (93%) rename packages/@aws-cdk/{core-app-scoped-staging-synthesizer => app-staging-synthesizer}/package.json (82%) rename packages/@aws-cdk/{core-app-scoped-staging-synthesizer => app-staging-synthesizer}/test/assets/index.py (100%) rename packages/@aws-cdk/{core-app-scoped-staging-synthesizer => app-staging-synthesizer}/test/integ.synthesizer.js.snapshot/StagingStack.assets.json (100%) rename packages/@aws-cdk/{core-app-scoped-staging-synthesizer => app-staging-synthesizer}/test/integ.synthesizer.js.snapshot/StagingStack.template.json (100%) rename packages/@aws-cdk/{core-app-scoped-staging-synthesizer => app-staging-synthesizer}/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json (100%) rename packages/@aws-cdk/{core-app-scoped-staging-synthesizer => app-staging-synthesizer}/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json (100%) rename packages/@aws-cdk/{core-app-scoped-staging-synthesizer => app-staging-synthesizer}/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js (100%) rename packages/@aws-cdk/{core-app-scoped-staging-synthesizer => app-staging-synthesizer}/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js (100%) rename packages/@aws-cdk/{core-app-scoped-staging-synthesizer => app-staging-synthesizer}/test/integ.synthesizer.js.snapshot/cdk.out (100%) rename packages/@aws-cdk/{core-app-scoped-staging-synthesizer => app-staging-synthesizer}/test/integ.synthesizer.js.snapshot/integ.json (100%) rename packages/@aws-cdk/{core-app-scoped-staging-synthesizer => app-staging-synthesizer}/test/integ.synthesizer.js.snapshot/manifest.json (100%) rename packages/@aws-cdk/{core-app-scoped-staging-synthesizer => app-staging-synthesizer}/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json (100%) rename packages/@aws-cdk/{core-app-scoped-staging-synthesizer => app-staging-synthesizer}/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json (100%) rename packages/@aws-cdk/{core-app-scoped-staging-synthesizer => app-staging-synthesizer}/test/integ.synthesizer.js.snapshot/tree.json (100%) rename packages/@aws-cdk/{core-app-scoped-staging-synthesizer => app-staging-synthesizer}/test/integ.synthesizer.ts (78%) rename packages/@aws-cdk/{core-app-scoped-staging-synthesizer => app-staging-synthesizer}/test/synthesizer.test.ts (97%) diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/.eslintrc.js b/packages/@aws-cdk/app-staging-synthesizer/.eslintrc.js similarity index 100% rename from packages/@aws-cdk/core-app-scoped-staging-synthesizer/.eslintrc.js rename to packages/@aws-cdk/app-staging-synthesizer/.eslintrc.js diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/.gitignore b/packages/@aws-cdk/app-staging-synthesizer/.gitignore similarity index 100% rename from packages/@aws-cdk/core-app-scoped-staging-synthesizer/.gitignore rename to packages/@aws-cdk/app-staging-synthesizer/.gitignore diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/.npmignore b/packages/@aws-cdk/app-staging-synthesizer/.npmignore similarity index 100% rename from packages/@aws-cdk/core-app-scoped-staging-synthesizer/.npmignore rename to packages/@aws-cdk/app-staging-synthesizer/.npmignore diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/LICENSE b/packages/@aws-cdk/app-staging-synthesizer/LICENSE similarity index 100% rename from packages/@aws-cdk/core-app-scoped-staging-synthesizer/LICENSE rename to packages/@aws-cdk/app-staging-synthesizer/LICENSE diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/NOTICE b/packages/@aws-cdk/app-staging-synthesizer/NOTICE similarity index 100% rename from packages/@aws-cdk/core-app-scoped-staging-synthesizer/NOTICE rename to packages/@aws-cdk/app-staging-synthesizer/NOTICE diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/README.md b/packages/@aws-cdk/app-staging-synthesizer/README.md similarity index 81% rename from packages/@aws-cdk/core-app-scoped-staging-synthesizer/README.md rename to packages/@aws-cdk/app-staging-synthesizer/README.md index 30ee6cb4f27d4..f8fc8a7818d7d 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/README.md +++ b/packages/@aws-cdk/app-staging-synthesizer/README.md @@ -19,7 +19,7 @@ This library includes constructs aimed at replacing the current model of bootstr greater control of the bootstrap experience to the CDK user. The important constructs in this library are the `IStagingStack`, a framework for an app-level bootstrap stack that handles file assets and docker assets and the `DefaultStagingStack`, which is a works-out-of-the-box implementation of the -interface. Additionally, there is an `AppScopedStagingSynthesizer` that will synthesize CDK applications +interface. Additionally, there is an `AppStagingSynthesizer` that will synthesize CDK applications built with this new model of bootstrapping. ## Bootstrap Model @@ -74,24 +74,24 @@ benefits: ## Synthesizer -To use this library, supply the `AppScopedStagingSynthesizer` in as the default synthesizer to the app. +To use this library, supply the `AppStagingSynthesizer` in as the default synthesizer to the app. This will ensure that a Staging Stack will be created next to the CDK App to hold the staging resources. -`AppScopedStagingSynthesizer` comes with static methods covering the use-cases for the synthesizer. +`AppStagingSynthesizer` comes with static methods covering the use-cases for the synthesizer. ### Using the Default Staging Stack per Environment The most common use case will be to use the built-in `DefaultStagingStack` on a per-environment basis. That means that in each environment the CDK App is deployed to, the synthesizer will create a Staging Stack to store its resources. To use this kind of synthesizer, use -`AppScopedStagingSynthesizer.stackPerEnv()`. +`AppStagingSynthesizer.stackPerEnv()`. ```ts import { App } from 'aws-cdk-lib'; -import { AppScopedStagingSynthesizer } from '@aws-cdk/core-app-scoped-staging-synthesizer'; +import { AppStagingSynthesizer } from '@aws-cdk/core-app-scoped-staging-synthesizer'; const app = new App({ - defaultSynthesizer: AppScopedStagingSynthesizer.stackPerEnv({ + defaultSynthesizer: AppStagingSynthesizer.stackPerEnv({ appId: 'my-app-id', }), }); @@ -99,16 +99,16 @@ const app = new App({ ### Using a Custom Staging Stack per Environment -To use a custom stack, but still on a per-environment basis, use `AppScopedStagingSynthesizer.customFactory()`. +To use a custom stack, but still on a per-environment basis, use `AppStagingSynthesizer.customFactory()`. This has the benefit of providing a custom Staging Stack that can be created in every environment the CDK App is deployed to. ```ts import { App } from 'aws-cdk-lib'; -import { AppScopedStagingSynthesizer } from '@aws-cdk/core-app-scoped-staging-synthesizer'; +import { AppStagingSynthesizer } from '@aws-cdk/core-app-scoped-staging-synthesizer'; const app = new App({ - defaultSynthesizer: AppScopedStagingSynthesizer.customFactory({ + defaultSynthesizer: AppStagingSynthesizer.customFactory({ createStack() { return new CustomStagingStack({ appId: 'my-app-id' }), }, @@ -119,19 +119,19 @@ const app = new App({ ### Using an Existing Staging Stack If you need to pass in an existing stack as the Staging Stack to the CDK App, use -`AppScopedStagingSynthesizer.customStack()`. Make sure that the custom stack you provide implements +`AppStagingSynthesizer.customStack()`. Make sure that the custom stack you provide implements `IStagingStack`. ```ts import { App, Stack } from 'aws-cdk-lib'; -import { AppScopedStagingSynthesizer, IStagingStack } from '@aws-cdk/core-app-scoped-staging-synthesizer'; +import { AppStagingSynthesizer, IStagingStack } from '@aws-cdk/core-app-scoped-staging-synthesizer'; class CustomStagingStack implements IStagingStack { // ... } const app = new App({ - defaultSynthesizer: AppScopedStagingSynthesizer.customStack(new CustomStagingStack(this, 'StagingStack')), + defaultSynthesizer: AppStagingSynthesizer.customStack(new CustomStagingStack(this, 'StagingStack')), }); ``` @@ -140,7 +140,7 @@ const app = new App({ > The Default Staging Stack is being actively worked on right now and currently does not support image assets. The default Staging Stack includes all the staging resources necessary for CDK Assets. The below example -is of a CDK App using the `AppScopedStagingSynthesizer` and creating a file asset for the Lambda Function +is of a CDK App using the `AppStagingSynthesizer` and creating a file asset for the Lambda Function source code. As part of the `DefaultStagingStack`, an s3 bucket and iam role will be created that will be used to upload the asset to s3. @@ -148,10 +148,10 @@ used to upload the asset to s3. import * as path from 'path'; import * as lambda from 'aws-cdk-lib/aws-lambda'; import { App, Stack } from 'aws-cdk-lib'; -import { AppScopedStagingSynthesizer } from 'aws-cdk-lib/core-app-scoped-staging-synthesizer'; +import { AppStagingSynthesizer } from 'aws-cdk-lib/core-app-scoped-staging-synthesizer'; const app = new App({ - defaultSynthesizer: new AppScopedStagingSynthesizer.stackPerEnv({appId: 'my-app-id'}), + defaultSynthesizer: new AppStagingSynthesizer.stackPerEnv({appId: 'my-app-id'}), }); const stack = new Stack(app, 'my-stack'); @@ -176,10 +176,10 @@ if all you need is to supply custom roles (and not change anything else in the ` ```ts import { App } from 'aws-cdk-lib'; -import { AppScopedStagingSynthesizer, BootstrapRole } from 'aws-cdk-lib/core-app-scoped-staging-synthesizer'; +import { AppStagingSynthesizer, BootstrapRole } from 'aws-cdk-lib/core-app-scoped-staging-synthesizer'; const app = new App({ - defaultSynthesizer: new AppScopedStagingSynthesizer.stackFromEnv({ + defaultSynthesizer: new AppStagingSynthesizer.stackFromEnv({ appId: 'my-app-id', roles: { cloudFormationExecutionRole: BoostrapRole.fromRoleArn('arn'), diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/adr/resource-names.md b/packages/@aws-cdk/app-staging-synthesizer/adr/resource-names.md similarity index 100% rename from packages/@aws-cdk/core-app-scoped-staging-synthesizer/adr/resource-names.md rename to packages/@aws-cdk/app-staging-synthesizer/adr/resource-names.md diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/jest.config.js b/packages/@aws-cdk/app-staging-synthesizer/jest.config.js similarity index 100% rename from packages/@aws-cdk/core-app-scoped-staging-synthesizer/jest.config.js rename to packages/@aws-cdk/app-staging-synthesizer/jest.config.js diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts similarity index 100% rename from packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/default-staging-stack.ts rename to packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/index.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/index.ts similarity index 100% rename from packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/index.ts rename to packages/@aws-cdk/app-staging-synthesizer/lib/index.ts diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/synthesizer.ts similarity index 93% rename from packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts rename to packages/@aws-cdk/app-staging-synthesizer/lib/synthesizer.ts index a792868e1bb4d..aa20b1115b00d 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/lib/synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/synthesizer.ts @@ -95,7 +95,7 @@ export interface IStagingStackFactory { /** * Properties for customFactory static method */ -export interface CustomFactoryProps extends AppScopedStagingSynthesizerProps { +export interface CustomFactoryProps extends AppStagingSynthesizerProps { /** * Include rules that create a new Staging Stack per environment. * @@ -105,9 +105,9 @@ export interface CustomFactoryProps extends AppScopedStagingSynthesizerProps { } /** - * Internal properties for AppScopedStagingSynthesizer + * Internal properties for AppStagingSynthesizer */ -interface AppScopedStagingSynthesizerProps { +interface AppStagingSynthesizerProps { /** * A factory method that creates an IStagingStack when given the stack the * synthesizer is binding. @@ -124,9 +124,9 @@ interface AppScopedStagingSynthesizerProps { } /** - * App Scoped Staging Stack Synthesizer + * App Staging Synthesizer */ -export class AppScopedStagingSynthesizer extends StackSynthesizer implements IReusableStackSynthesizer { +export class AppStagingSynthesizer extends StackSynthesizer implements IReusableStackSynthesizer { public static stackPerEnv(props: StackPerEnvProps) { for (const key in props) { if (props.hasOwnProperty(key)) { @@ -146,7 +146,7 @@ export class AppScopedStagingSynthesizer extends StackSynthesizer implements IRe } } - return new AppScopedStagingSynthesizer({ + return new AppStagingSynthesizer({ bootstrapRoles: props.bootstrapRoles, stagingStackFactory: { stagingStackFactory(boundStack: Stack) { @@ -180,10 +180,10 @@ export class AppScopedStagingSynthesizer extends StackSynthesizer implements IRe } public static customFactory(props: CustomFactoryProps) { - return new AppScopedStagingSynthesizer(props); + return new AppStagingSynthesizer(props); } - private constructor(private readonly props: AppScopedStagingSynthesizerProps) { + private constructor(private readonly props: AppStagingSynthesizerProps) { super(); } @@ -230,7 +230,7 @@ class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundSta private readonly cloudFormationExecutionRoleArn?: string; private readonly deploymentActionRoleArn?: string; - constructor(stack: Stack, props: AppScopedStagingSynthesizerProps) { + constructor(stack: Stack, props: AppStagingSynthesizerProps) { super(); super.bind(stack); diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/package.json b/packages/@aws-cdk/app-staging-synthesizer/package.json similarity index 82% rename from packages/@aws-cdk/core-app-scoped-staging-synthesizer/package.json rename to packages/@aws-cdk/app-staging-synthesizer/package.json index 43290ec939dab..ad8b9bbc5805f 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/package.json +++ b/packages/@aws-cdk/app-staging-synthesizer/package.json @@ -1,8 +1,8 @@ { - "name": "@aws-cdk/core-app-scoped-staging-synthesizer", + "name": "@aws-cdk/app-staging-synthesizer", "private": true, "version": "0.0.0", - "description": "Cdk synthesizer for app-scoped bootstrap", + "description": "Cdk synthesizer for with app-scoped staging stack", "main": "lib/index.js", "types": "lib/index.d.ts", "jsii": { @@ -19,20 +19,20 @@ "java": { "maven": { "groupId": "software.amazon.awscdk", - "artifactId": "cdk-core-app-scoped-staging-synthesizer" + "artifactId": "cdk-app-staging-synthesizer" }, - "package": "software.amazon.awscdk.core.app.scoped.staging.synthesizer" + "package": "software.amazon.awscdk.app.staging.synthesizer" }, "python": { - "distName": "aws-cdk.core-app-scoped-staging-synthesizer", - "module": "aws_cdk.core_app_scoped_staging_synthesizer", + "distName": "aws-cdk.app-staging-synthesizer", + "module": "aws_cdk.app_staging_synthesizer", "classifiers": [ "Framework :: AWS CDK", "Framework :: AWS CDK :: 2" ] }, "dotnet": { - "namespace": "Amazon.CDK.Core.App.Scoped.Staging.Synthesizer", + "namespace": "Amazon.CDK.App.Staging.Synthesizer", "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/main/logo/default-256-dark.png" } } @@ -40,7 +40,7 @@ "repository": { "type": "git", "url": "https://github.com/aws/aws-cdk.git", - "directory": "packages/@aws-cdk/core-app-scoped-staging-synthesizer" + "directory": "packages/@aws-cdk/app-staging-synthesizer" }, "scripts": { "build": "cdk-build", @@ -111,6 +111,7 @@ "constructs": "^10.0.0", "@aws-cdk/aws-s3": "0.0.0", "@aws-cdk/aws-ecr": "0.0.0", - "@aws-cdk/aws-iam": "0.0.0" + "@aws-cdk/aws-iam": "0.0.0", + "@aws-cdk/aws-kms": "0.0.0" } } diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/assets/index.py b/packages/@aws-cdk/app-staging-synthesizer/test/assets/index.py similarity index 100% rename from packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/assets/index.py rename to packages/@aws-cdk/app-staging-synthesizer/test/assets/index.py diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.assets.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.assets.json similarity index 100% rename from packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.assets.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.assets.json diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.template.json similarity index 100% rename from packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.template.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.template.json diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json similarity index 100% rename from packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json similarity index 100% rename from packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js similarity index 100% rename from packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js similarity index 100% rename from packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/cdk.out b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/cdk.out similarity index 100% rename from packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/cdk.out rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/cdk.out diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/integ.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integ.json similarity index 100% rename from packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/integ.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integ.json diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json similarity index 100% rename from packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json similarity index 100% rename from packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json similarity index 100% rename from packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json similarity index 100% rename from packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts similarity index 78% rename from packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.ts rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts index 0368b754bcfcd..0ba12074ba217 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/integ.synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts @@ -1,12 +1,12 @@ import * as path from 'path'; import * as lambda from '@aws-cdk/aws-lambda'; import { App, Stack } from '@aws-cdk/core'; -import { AppScopedStagingSynthesizer } from '../lib'; +import { AppStagingSynthesizer } from '../lib'; const app = new App(); const stack = new Stack(app, 'app-scoped-staging-test', { - synthesizer: AppScopedStagingSynthesizer.stackPerEnv({ appId: 'app-id' }), + synthesizer: AppStagingSynthesizer.stackPerEnv({ appId: 'app-id' }), env: { account: '489318732371', region: 'us-east-2', diff --git a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/synthesizer.test.ts similarity index 97% rename from packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts rename to packages/@aws-cdk/app-staging-synthesizer/test/synthesizer.test.ts index 2f9c7448f160f..6886ca88fed75 100644 --- a/packages/@aws-cdk/core-app-scoped-staging-synthesizer/test/synthesizer.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/synthesizer.test.ts @@ -3,7 +3,7 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import { App, Stack, CfnResource, FileAssetPackaging, Token } from '@aws-cdk/core'; import { evaluateCFN } from '@aws-cdk/core/test/evaluate-cfn'; import * as cxapi from '@aws-cdk/cx-api'; -import { AppScopedStagingSynthesizer, BootstrapRole, StackPerEnvProps } from '../lib'; +import { AppStagingSynthesizer, BootstrapRole, StackPerEnvProps } from '../lib'; const CFN_CONTEXT = { 'AWS::Region': 'the_region', @@ -15,7 +15,7 @@ const CLOUDFORMATION_EXECUTION_ROLE = 'role'; const DEPLOY_ACTION_ROLE = 'role'; const LOOKUP_ROLE = 'role'; -describe(AppScopedStagingSynthesizer, () => { +describe(AppStagingSynthesizer, () => { let app: App; let stack: Stack; @@ -215,7 +215,7 @@ describe(AppScopedStagingSynthesizer, () => { describe('Custom Roles on AppScopedStagingSynthesizer', () => { test('Can supply different roles', () => { const app = new App({ - defaultStackSynthesizer: AppScopedStagingSynthesizer.stackPerEnv({ + defaultStackSynthesizer: AppStagingSynthesizer.stackPerEnv({ appId: APP_ID, bootstrapRoles: { cloudFormationExecutionRole: BootstrapRole.fromRoleArn(CLOUDFORMATION_EXECUTION_ROLE), @@ -268,8 +268,8 @@ function last(xs?: A[]): A | undefined { } class TestAppScopedStagingSynthesizer { - public static stackPerEnv(props: Partial = {}): AppScopedStagingSynthesizer { - return AppScopedStagingSynthesizer.stackPerEnv({ + public static stackPerEnv(props: Partial = {}): AppStagingSynthesizer { + return AppStagingSynthesizer.stackPerEnv({ appId: props.appId ?? APP_ID, bootstrapRoles: { cloudFormationExecutionRole: BootstrapRole.fromRoleArn(CLOUDFORMATION_EXECUTION_ROLE), From 46da1cc3eee13dfc12dfe9bc5106cadccef99d96 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 23 Mar 2023 13:39:00 -0400 Subject: [PATCH 043/120] add roles stuff without testing yet --- .../app-staging-synthesizer/README.md | 21 ++-- .../lib/default-staging-stack.ts | 13 ++- .../lib/synthesizer.ts | 96 +++++++++++++------ 3 files changed, 93 insertions(+), 37 deletions(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer/README.md b/packages/@aws-cdk/app-staging-synthesizer/README.md index f8fc8a7818d7d..3a6ba7bca4ab3 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/README.md +++ b/packages/@aws-cdk/app-staging-synthesizer/README.md @@ -88,7 +88,7 @@ Staging Stack to store its resources. To use this kind of synthesizer, use ```ts import { App } from 'aws-cdk-lib'; -import { AppStagingSynthesizer } from '@aws-cdk/core-app-scoped-staging-synthesizer'; +import { AppStagingSynthesizer } from '@aws-cdk/app-staging-synthesizer'; const app = new App({ defaultSynthesizer: AppStagingSynthesizer.stackPerEnv({ @@ -105,13 +105,20 @@ is deployed to. ```ts import { App } from 'aws-cdk-lib'; -import { AppStagingSynthesizer } from '@aws-cdk/core-app-scoped-staging-synthesizer'; +import { AppStagingSynthesizer } from '@aws-cdk/app-staging-synthesizer'; const app = new App({ defaultSynthesizer: AppStagingSynthesizer.customFactory({ - createStack() { - return new CustomStagingStack({ appId: 'my-app-id' }), + stagingStackFactory: { + stagingStackFactory(boundStack: Stack) { + const app = App.of(boundStack); + if (!App.isApp(app)) { + throw new Error(`Stack ${boundStack.stackName} must be part of an App`); + } + return new CustomStagingStack(app, 'StagingStack', { appId: 'my-app-id' }), + }, }, + oncePerEnv: true, // by default }), }); ``` @@ -124,7 +131,7 @@ If you need to pass in an existing stack as the Staging Stack to the CDK App, us ```ts import { App, Stack } from 'aws-cdk-lib'; -import { AppStagingSynthesizer, IStagingStack } from '@aws-cdk/core-app-scoped-staging-synthesizer'; +import { AppStagingSynthesizer, IStagingStack } from '@aws-cdk/app-staging-synthesizer'; class CustomStagingStack implements IStagingStack { // ... @@ -148,7 +155,7 @@ used to upload the asset to s3. import * as path from 'path'; import * as lambda from 'aws-cdk-lib/aws-lambda'; import { App, Stack } from 'aws-cdk-lib'; -import { AppStagingSynthesizer } from 'aws-cdk-lib/core-app-scoped-staging-synthesizer'; +import { AppStagingSynthesizer } from 'aws-cdk-lib/app-staging-synthesizer'; const app = new App({ defaultSynthesizer: new AppStagingSynthesizer.stackPerEnv({appId: 'my-app-id'}), @@ -176,7 +183,7 @@ if all you need is to supply custom roles (and not change anything else in the ` ```ts import { App } from 'aws-cdk-lib'; -import { AppStagingSynthesizer, BootstrapRole } from 'aws-cdk-lib/core-app-scoped-staging-synthesizer'; +import { AppStagingSynthesizer, BootstrapRole } from 'aws-cdk-lib/app-staging-synthesizer'; const app = new App({ defaultSynthesizer: new AppStagingSynthesizer.stackFromEnv({ diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts index ff6ce15e1dbf3..02ba1545e6e57 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts @@ -2,7 +2,18 @@ import * as ecr from '@aws-cdk/aws-ecr'; import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; import * as s3 from '@aws-cdk/aws-s3'; -import { App, Arn, ArnFormat, Aws, BootstraplessSynthesizer, DockerImageAssetSource, FileAssetSource, RemovalPolicy, Stack, StackProps } from '@aws-cdk/core'; +import { + App, + Arn, + ArnFormat, + Aws, + BootstraplessSynthesizer, + DockerImageAssetSource, + FileAssetSource, + RemovalPolicy, + Stack, + StackProps, +} from '@aws-cdk/core'; import { IConstruct } from 'constructs'; import { BootstrapRole } from './synthesizer'; diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/synthesizer.ts index aa20b1115b00d..db45b453d7d52 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/synthesizer.ts @@ -1,12 +1,12 @@ import { App, - Arn, AssetManifestBuilder, + BOOTSTRAP_QUALIFIER_CONTEXT, DockerImageAssetLocation, DockerImageAssetSource, FileAssetLocation, FileAssetSource, - IBoundStackSynthesizer, + IBoundStackSynthesizer as IBoundAppStagingSynthesizer, IReusableStackSynthesizer, ISynthesisSession, Stack, @@ -17,32 +17,15 @@ import * as cxapi from '@aws-cdk/cx-api'; import { IStagingStack as IStagingStack, DefaultStagingStack } from './default-staging-stack'; export class BootstrapRole { - // public abstract arn(stack: Stack) { - // // stack.formatArn - // } - public static default() { - return new BootstrapRole('default'); + public static CLI_CREDENTIALS() { + return new BootstrapRole(undefined); } public static fromRoleArn(arn: string) { return new BootstrapRole(arn); } - public static fromRoleName(name: string) { - // wont work - const arn = Arn.format({ - resource: 'role', - service: 'iam', - resourceName: name, - }); - return new BootstrapRole(arn); - } - - private constructor(public readonly roleArn: string) {} - - public get roleName() { - return this.roleArn.split('/')[1]; - } + private constructor(public readonly roleArn: string | undefined) {} } export interface BootstrapRoles { @@ -79,6 +62,16 @@ export interface StackPerEnvProps { * @default - no custom roles */ readonly bootstrapRoles?: BootstrapRoles; + + /** + * Qualifier to disambiguate multiple environments in the same account + * + * You can use this and leave the other naming properties empty if you have deployed + * the bootstrap environment with standard names but only different qualifiers. + * + * @default - Value of context key '@aws-cdk/core:bootstrapQualifier' if set, otherwise `DEFAULT_QUALIFIER` + */ + readonly qualifier?: string; } /** @@ -121,6 +114,16 @@ interface AppStagingSynthesizerProps { * @default - no custom roles */ readonly bootstrapRoles?: BootstrapRoles; + + /** + * Qualifier to disambiguate multiple environments in the same account + * + * You can use this and leave the other naming properties empty if you have deployed + * the bootstrap environment with standard names but only different qualifiers. + * + * @default - Value of context key '@aws-cdk/core:bootstrapQualifier' if set, otherwise `DEFAULT_QUALIFIER` + */ + readonly qualifier?: string; } /** @@ -137,7 +140,7 @@ export class AppStagingSynthesizer extends StackSynthesizer implements IReusable function validateNoToken(key: A) { const prop = props[key]; if (typeof prop === 'string' && Token.isUnresolved(prop)) { - throw new Error(`AppScopedStagingSynthesizer property '${key}' cannot contain tokens; only the following placeholder strings are allowed: ` + [ + throw new Error(`AppStagingSynthesizer property '${key}' cannot contain tokens; only the following placeholder strings are allowed: ` + [ '${Qualifier}', cxapi.EnvironmentPlaceholders.CURRENT_REGION, cxapi.EnvironmentPlaceholders.CURRENT_ACCOUNT, @@ -190,8 +193,8 @@ export class AppStagingSynthesizer extends StackSynthesizer implements IReusable /** * Returns a version of the synthesizer bound to a stack. */ - public reusableBind(stack: Stack): IBoundStackSynthesizer { - return new BoundStagingStackSynthesizer(stack, this.props); + public reusableBind(stack: Stack): IBoundAppStagingSynthesizer { + return new BoundAppStagingSynthesizer(stack, this.props); } /** @@ -223,24 +226,59 @@ export class AppStagingSynthesizer extends StackSynthesizer implements IReusable } } -class BoundStagingStackSynthesizer extends StackSynthesizer implements IBoundStackSynthesizer { +class BoundAppStagingSynthesizer extends StackSynthesizer implements IBoundAppStagingSynthesizer { + /** + * Default ARN qualifier + */ + public static readonly DEFAULT_QUALIFIER = 'hnb659fds'; + + /** + * Default CloudFormation role ARN. + */ + public static readonly DEFAULT_CLOUDFORMATION_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-cfn-exec-role-${AWS::AccountId}-${AWS::Region}'; + + /** + * Default deploy role ARN. + */ + public static readonly DEFAULT_DEPLOY_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-deploy-role-${AWS::AccountId}-${AWS::Region}'; + + /** + * Default lookup role ARN for missing values. + */ + public static readonly DEFAULT_LOOKUP_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-lookup-role-${AWS::AccountId}-${AWS::Region}'; + private stagingStack: IStagingStack; private assetManifest = new AssetManifestBuilder(); private readonly lookupRoleArn?: string; private readonly cloudFormationExecutionRoleArn?: string; private readonly deploymentActionRoleArn?: string; + private readonly qualifier: string; constructor(stack: Stack, props: AppStagingSynthesizerProps) { super(); super.bind(stack); - this.lookupRoleArn = props.bootstrapRoles?.lookupRole?.roleArn; - this.cloudFormationExecutionRoleArn = props.bootstrapRoles?.cloudFormationExecutionRole?.roleArn; - this.deploymentActionRoleArn = props.bootstrapRoles?.deploymentActionRole?.roleArn; + this.qualifier = props.qualifier ?? stack.node.tryGetContext(BOOTSTRAP_QUALIFIER_CONTEXT) ?? BoundAppStagingSynthesizer.DEFAULT_QUALIFIER; + + // Roles are implemented this way because roleArn could be undefined, signifying that we are + // to use cli credentials instead. + this.lookupRoleArn = props.bootstrapRoles?.lookupRole ? + props.bootstrapRoles.lookupRole.roleArn : BoundAppStagingSynthesizer.DEFAULT_LOOKUP_ROLE_ARN; + this.cloudFormationExecutionRoleArn = props.bootstrapRoles?.cloudFormationExecutionRole ? + props.bootstrapRoles.cloudFormationExecutionRole.roleArn : BoundAppStagingSynthesizer.DEFAULT_CLOUDFORMATION_ROLE_ARN; + this.deploymentActionRoleArn = props.bootstrapRoles?.deploymentActionRole ? + props.bootstrapRoles.deploymentActionRole.roleArn : BoundAppStagingSynthesizer.DEFAULT_DEPLOY_ROLE_ARN; this.stagingStack = props.stagingStackFactory.stagingStackFactory(stack); } + /** + * The qualifier used to bootstrap this stack + */ + public get bootstrapQualifier(): string | undefined { + return this.qualifier; + } + public synthesize(session: ISynthesisSession): void { const templateAssetSource = this.synthesizeTemplate(session, this.lookupRoleArn); const templateAsset = this.addFileAsset(templateAssetSource); From 20c8032aec3d6056543efc7b1a95a2feb9ae435e Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Fri, 24 Mar 2023 03:17:08 -0400 Subject: [PATCH 044/120] minor cleanups --- .../lib/default-staging-stack.ts | 24 ++++++++---- .../lib/synthesizer.ts | 4 +- .../test/synthesizer.test.ts | 39 ++++++++++++++----- 3 files changed, 48 insertions(+), 19 deletions(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts index 02ba1545e6e57..5df9d83e6acec 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts @@ -131,8 +131,8 @@ export class DefaultStagingStack extends Stack implements IStagingStack { public readonly dependencyStack: Stack; public readonly appId: string; private readonly stagingBucketName?: string; - private fileAssetPublishingRole?: BootstrapRole; - private dockerAssetPublishingRole?: BootstrapRole; + private fileAssetPublishingRoleArn?: string; + private dockerAssetPublishingRoleArn?: string; private readonly repositoryLifecycleRules: Record; constructor(scope: App, id: string, props: DefaultStagingStackProps) { @@ -145,12 +145,20 @@ export class DefaultStagingStack extends Stack implements IStagingStack { this.dependencyStack = this; this.stagingBucketName = props.stagingBucketName; - this.fileAssetPublishingRole = props.fileAssetPublishingRole; - this.dockerAssetPublishingRole = props.dockerAssetPublishingRole; + this.fileAssetPublishingRoleArn = props.fileAssetPublishingRole ? this.validateStagingRole(props.fileAssetPublishingRole).roleArn : undefined; + this.dockerAssetPublishingRoleArn = props.dockerAssetPublishingRole ? + this.validateStagingRole(props.dockerAssetPublishingRole).roleArn : undefined; this.repositoryLifecycleRules = this.processLifecycleRules(props.repositoryLifecycleRules ?? []); this.stagingRepos = {}; } + private validateStagingRole(stagingRole: BootstrapRole) { + if (stagingRole.roleArn === undefined) { + throw new Error('fileAssetPublishingRole and dockerAssetPublishingRole cannot be specified as cliCredentials(). Please supply an arn to reference an existing IAM role.'); + } + return stagingRole; + } + private processLifecycleRules(rules: StagingRepoLifecycleRule[]) { const ruleMap: Record = {}; for (const rule of rules) { @@ -168,8 +176,8 @@ export class DefaultStagingStack extends Stack implements IStagingStack { * Returns the file publishing role arn */ private getFilePublishingRoleArn(): string { - if (this.fileAssetPublishingRole) { - return this.fileAssetPublishingRole.roleArn; + if (this.fileAssetPublishingRoleArn) { + return this.fileAssetPublishingRoleArn; } const role = this.node.tryFindChild('CdkFilePublishingRole') as iam.Role; if (role === undefined) { @@ -201,8 +209,8 @@ export class DefaultStagingStack extends Stack implements IStagingStack { * Returns the well-known name of the image publishing role */ private getCreateImagePublishingRole() { - if (this.dockerAssetPublishingRole) { - return this.dockerAssetPublishingRole.roleArn; + if (this.dockerAssetPublishingRoleArn) { + return this.dockerAssetPublishingRoleArn; } const roleId = 'CdkDockerAssetPublishingRole'; diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/synthesizer.ts index db45b453d7d52..0d2f7136ee83a 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/synthesizer.ts @@ -17,7 +17,7 @@ import * as cxapi from '@aws-cdk/cx-api'; import { IStagingStack as IStagingStack, DefaultStagingStack } from './default-staging-stack'; export class BootstrapRole { - public static CLI_CREDENTIALS() { + public static cliCredentials() { return new BootstrapRole(undefined); } @@ -94,7 +94,7 @@ export interface CustomFactoryProps extends AppStagingSynthesizerProps { * * @default true */ - oncePerEnv: boolean; + // oncePerEnv: boolean; } /** diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/synthesizer.test.ts index 6886ca88fed75..f25028183ae92 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/synthesizer.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/synthesizer.test.ts @@ -212,8 +212,9 @@ describe(AppStagingSynthesizer, () => { } }); -describe('Custom Roles on AppScopedStagingSynthesizer', () => { - test('Can supply different roles', () => { +describe('Boostrap Roles', () => { + test('Can supply existing arns for bootstrapped roles', () => { + // GIVEN const app = new App({ defaultStackSynthesizer: AppStagingSynthesizer.stackPerEnv({ appId: APP_ID, @@ -222,13 +223,8 @@ describe('Custom Roles on AppScopedStagingSynthesizer', () => { lookupRole: BootstrapRole.fromRoleArn(LOOKUP_ROLE), deploymentActionRole: BootstrapRole.fromRoleArn(DEPLOY_ACTION_ROLE), }, - stagingRoles: { - fileAssetPublishingRole: BootstrapRole.fromRoleArn('arn'), - dockerAssetPublishingRole: BootstrapRole.fromRoleArn('arn'), - }, }), }); - const stack = new Stack(app, 'Stack', { env: { account: '000000000000', @@ -245,16 +241,41 @@ describe('Custom Roles on AppScopedStagingSynthesizer', () => { // THEN const stackArtifact = asm.getStackArtifact('Stack'); - // CloudFormation roles are as advertised + // Bootstrapped roles are as advertised expect(stackArtifact.cloudFormationExecutionRoleArn).toEqual(CLOUDFORMATION_EXECUTION_ROLE); expect(stackArtifact.lookupRole).toEqual({ arn: LOOKUP_ROLE }); expect(stackArtifact.assumeRoleArn).toEqual(DEPLOY_ACTION_ROLE); + }); + + test('can supply existing arns for staging roles', () => { + // GIVEN + const app = new App({ + defaultStackSynthesizer: AppStagingSynthesizer.stackPerEnv({ + appId: APP_ID, + stagingRoles: { + fileAssetPublishingRole: BootstrapRole.fromRoleArn('arn'), + }, + }), + }); + const stack = new Stack(app, 'Stack', { + env: { + account: '000000000000', + region: 'us-east-1', + }, + }); + new CfnResource(stack, 'Resource', { + type: 'Some::Resource', + }); + // WHEN + const asm = app.synth(); + + // THEN + // Staging roles are as advertised const manifestArtifact = asm.artifacts.filter(isAssetManifest)[0]; expect(manifestArtifact).toBeDefined(); const manifest: cxschema.AssetManifest = JSON.parse(fs.readFileSync(manifestArtifact.file, { encoding: 'utf-8' })); const firstFile: any = (manifest.files ? manifest.files[Object.keys(manifest.files)[0]] : undefined) ?? {}; - expect(firstFile.destinations['000000000000-us-east-1'].assumeRoleArn).toEqual('arn'); }); }); From 3b55d82c21c5bd0d65004053bfe2923b54775970 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Fri, 24 Mar 2023 12:20:50 -0400 Subject: [PATCH 045/120] error when mixing agnostic/non-agnostic stacks in an app --- .../lib/synthesizer.ts | 25 ++++++++++++++++++- .../test/synthesizer.test.ts | 22 ++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/synthesizer.ts index 0d2f7136ee83a..78fa071dc4590 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/synthesizer.ts @@ -94,7 +94,7 @@ export interface CustomFactoryProps extends AppStagingSynthesizerProps { * * @default true */ - // oncePerEnv: boolean; + oncePerEnv: boolean; } /** @@ -159,8 +159,31 @@ export class AppStagingSynthesizer extends StackSynthesizer implements IReusable } let stackId = 'StagingStack'; + // Ensure we do not have a scenario where the App includes BOTH + // environment-agnostic stacks and set environment stacks. + const incompatibleEnvErrorMessage = [ + 'AppStagingSynthesizer cannot synthesize CDK Apps with BOTH environment-agnostic stacks and set environment stacks.', + 'Please either specify environments for all stacks or no stacks in the CDK App.', + ].join('\n'); + const addEnvMetadata = (agnostic: boolean) => { + if (app.node.metadata.filter((m) => m.type === 'cdk-env').length === 0) { + app.node.addMetadata('cdk-env', agnostic ? 'agnostic': 'non-agnostic'); + } + }; + if (!Token.isUnresolved(boundStack.account) && !Token.isUnresolved(boundStack.region)) { + // Stack has specified account and region stackId = stackId + boundStack.account + boundStack.region; + if (app.node.metadata.filter((m) => m.type === 'cdk-env' && m.data === 'agnostic').length >= 1) { + throw new Error(incompatibleEnvErrorMessage); + } + addEnvMetadata(false); + } else { + // Stack is environment agnostic + if (app.node.metadata.filter((m) => m.type === 'cdk-env' && m.data === 'non-agnostic').length >= 1) { + throw new Error(incompatibleEnvErrorMessage); + } + addEnvMetadata(true); } const stackName = `StagingStack${props.appId}`; diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/synthesizer.test.ts index f25028183ae92..ae280c03e0b15 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/synthesizer.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/synthesizer.test.ts @@ -202,6 +202,28 @@ describe(AppStagingSynthesizer, () => { expect(evalCFN(location1.repositoryName)).toEqual(evalCFN(location2.repositoryName)); }); + describe('environment specifics', () => { + test('throws if App includes env-agnostic and specific env stacks', () => { + // GIVEN - App with Stack with specific environment + + // THEN - Expect environment agnostic stack to fail + expect(() => new Stack(app, 'NoEnvStack')).toThrowError('AppStagingSynthesizer cannot synthesize CDK Apps with BOTH environment-agnostic stacks and set environment stacks.\nPlease either specify environments for all stacks or no stacks in the CDK App.'); + }); + + test('metadata for cdk-env is only added once on the app', () => { + // GIVEN - Additional App with specific environment + new Stack(app, 'EnvStack', { + env: { + account: '000000000000', + region: 'us-west-2', + }, + }); + + // THEN - We only add the env metadata once + expect(app.node.metadata.filter((m) => m.type === 'cdk-env').length).toEqual(1); + }); + }); + /** * Evaluate a possibly string-containing value the same way CFN would do * From e9b1030a67d0f173287824c56e866cb5935cfb6c Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Fri, 24 Mar 2023 12:35:25 -0400 Subject: [PATCH 046/120] additional bootstrap role test --- .../test/synthesizer.test.ts | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/synthesizer.test.ts index ae280c03e0b15..0d208c01c6567 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/synthesizer.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/synthesizer.test.ts @@ -300,6 +300,40 @@ describe('Boostrap Roles', () => { const firstFile: any = (manifest.files ? manifest.files[Object.keys(manifest.files)[0]] : undefined) ?? {}; expect(firstFile.destinations['000000000000-us-east-1'].assumeRoleArn).toEqual('arn'); }); + + test('can specify using current cli credentials instead', () => { + // GIVEN + const app = new App({ + defaultStackSynthesizer: AppStagingSynthesizer.stackPerEnv({ + appId: APP_ID, + bootstrapRoles: { + cloudFormationExecutionRole: BootstrapRole.cliCredentials(), + lookupRole: BootstrapRole.cliCredentials(), + deploymentActionRole: BootstrapRole.cliCredentials(), + }, + }), + }); + const stack = new Stack(app, 'Stack', { + env: { + account: '000000000000', + region: 'us-east-1', + }, + }); + new CfnResource(stack, 'Resource', { + type: 'Some::Resource', + }); + + // WHEN + const asm = app.synth(); + + // THEN + const stackArtifact = asm.getStackArtifact('Stack'); + + // Bootstrapped roles are undefined, which means current credentials are used + expect(stackArtifact.cloudFormationExecutionRoleArn).toBeUndefined(); + expect(stackArtifact.lookupRole).toBeUndefined(); + expect(stackArtifact.assumeRoleArn).toBeUndefined(); + }); }); function isAssetManifest(x: cxapi.CloudArtifact): x is cxapi.AssetManifestArtifact { From b1683084fc35e661b0ebd804851e1c84bef92424 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Fri, 24 Mar 2023 12:39:21 -0400 Subject: [PATCH 047/120] rename to app-staging-synthesizer --- .../lib/{synthesizer.ts => app-staging-synthesizer.ts} | 0 .../app-staging-synthesizer/lib/default-staging-stack.ts | 2 +- packages/@aws-cdk/app-staging-synthesizer/lib/index.ts | 2 +- .../{synthesizer.test.ts => app-staging-synthesizer.test.ts} | 0 4 files changed, 2 insertions(+), 2 deletions(-) rename packages/@aws-cdk/app-staging-synthesizer/lib/{synthesizer.ts => app-staging-synthesizer.ts} (100%) rename packages/@aws-cdk/app-staging-synthesizer/test/{synthesizer.test.ts => app-staging-synthesizer.test.ts} (100%) diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/lib/synthesizer.ts rename to packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts index 5df9d83e6acec..74bf52f261ebc 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts @@ -15,7 +15,7 @@ import { StackProps, } from '@aws-cdk/core'; import { IConstruct } from 'constructs'; -import { BootstrapRole } from './synthesizer'; +import { BootstrapRole } from './app-staging-synthesizer'; export interface FileAssetInfo { readonly bucketName: string; diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/index.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/index.ts index e8df9691bee4d..afe13d9c34942 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/index.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/index.ts @@ -1,2 +1,2 @@ export * from './default-staging-stack'; -export * from './synthesizer'; +export * from './app-staging-synthesizer'; diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/synthesizer.test.ts rename to packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts From 3eee18990c97dad9ca3b81e17209381ffd58c964 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Fri, 24 Mar 2023 13:56:06 -0400 Subject: [PATCH 048/120] add documentation everywhere and comment out docker code --- .../lib/app-staging-synthesizer.ts | 46 ++--- .../lib/bootstrap-roles.ts | 57 ++++++ .../lib/default-staging-stack.ts | 176 ++++++++++-------- .../app-staging-synthesizer/lib/index.ts | 1 + .../test/app-staging-synthesizer.test.ts | 117 ++++++------ 5 files changed, 226 insertions(+), 171 deletions(-) create mode 100644 packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts index 78fa071dc4590..8b324c51deab5 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts @@ -14,31 +14,9 @@ import { Token, } from '@aws-cdk/core'; import * as cxapi from '@aws-cdk/cx-api'; +import { BootstrapRoles, StagingRoles } from './bootstrap-roles'; import { IStagingStack as IStagingStack, DefaultStagingStack } from './default-staging-stack'; -export class BootstrapRole { - public static cliCredentials() { - return new BootstrapRole(undefined); - } - - public static fromRoleArn(arn: string) { - return new BootstrapRole(arn); - } - - private constructor(public readonly roleArn: string | undefined) {} -} - -export interface BootstrapRoles { - readonly cloudFormationExecutionRole?: BootstrapRole; - readonly deploymentActionRole?: BootstrapRole; - readonly lookupRole?: BootstrapRole; -} - -export interface StagingRoles { - readonly fileAssetPublishingRole?: BootstrapRole; - readonly dockerAssetPublishingRole?: BootstrapRole; -} - /** * Properties for stackPerEnv static method */ @@ -195,7 +173,7 @@ export class AppStagingSynthesizer extends StackSynthesizer implements IReusable }, stackName, fileAssetPublishingRole: props.stagingRoles?.fileAssetPublishingRole, - dockerAssetPublishingRole: props.stagingRoles?.dockerAssetPublishingRole, + imageAssetPublishingRole: props.stagingRoles?.dockerAssetPublishingRole, }); boundStack.addDependency(stagingStack.dependencyStack, 'stack depends on the staging stack for staging resources'); @@ -337,14 +315,16 @@ class BoundAppStagingSynthesizer extends StackSynthesizer implements IBoundAppSt /** * Add a docker image asset to the manifest. */ - public addDockerImageAsset(asset: DockerImageAssetSource): DockerImageAssetLocation { - const { repoName, assumeRoleArn } = this.stagingStack.addDockerImage(asset); - - const location = this.assetManifest.defaultAddDockerImageAsset(this.boundStack, asset, { - repositoryName: repoName, - role: { assumeRoleArn }, - // TODO: more props - }); - return this.cloudFormationLocationFromDockerImageAsset(location); + public addDockerImageAsset(_asset: DockerImageAssetSource): DockerImageAssetLocation { + // TODO: implement + throw new Error('Support for Docker Image Assets in AppStagingSynthesizer is not yet implemented. This construct is being actively worked on.'); + // const { repoName, assumeRoleArn } = this.stagingStack.addDockerImage(asset); + + // const location = this.assetManifest.defaultAddDockerImageAsset(this.boundStack, asset, { + // repositoryName: repoName, + // role: { assumeRoleArn }, + // // TODO: more props + // }); + // return this.cloudFormationLocationFromDockerImageAsset(location); } } diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts new file mode 100644 index 0000000000000..d0f57af0ac18d --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts @@ -0,0 +1,57 @@ +/** + * Bootstrapped role specifier. These roles must exist already. + * This class does not create new IAM Roles. + */ +export class BootstrapRole { + /** + * Use the currently assumed role/credentials + */ + public static cliCredentials() { + return new BootstrapRole(undefined); + } + + /** + * Specify an existing IAM Role to assume + */ + public static fromRoleArn(arn: string) { + return new BootstrapRole(arn); + } + + private constructor(public readonly roleArn: string | undefined) {} +} + +/** + * Roles that are bootstrapped to your account. + */ +export interface BootstrapRoles { + /** + * CloudFormation Execution Role + */ + readonly cloudFormationExecutionRole?: BootstrapRole; + + /** + * Deployment Action Role + */ + readonly deploymentActionRole?: BootstrapRole; + + /** + * Lookup Role + */ + readonly lookupRole?: BootstrapRole; +} + +/** + * Roles that are included in the Staging Stack + * (for access to Staging Resources) + */ +export interface StagingRoles { + /** + * File Asset Publishing Role + */ + readonly fileAssetPublishingRole?: BootstrapRole; + + /** + * Docker Asset Publishing Role + */ + readonly dockerAssetPublishingRole?: BootstrapRole; +} diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts index 74bf52f261ebc..8b46fdb6e39bd 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts @@ -4,9 +4,7 @@ import * as kms from '@aws-cdk/aws-kms'; import * as s3 from '@aws-cdk/aws-s3'; import { App, - Arn, ArnFormat, - Aws, BootstraplessSynthesizer, DockerImageAssetSource, FileAssetSource, @@ -15,26 +13,42 @@ import { StackProps, } from '@aws-cdk/core'; import { IConstruct } from 'constructs'; -import { BootstrapRole } from './app-staging-synthesizer'; +import { BootstrapRole } from './bootstrap-roles'; +/** + * Information returned by the Staging Stack for each file asset. + */ export interface FileAssetInfo { + /** + * The name of the staging bucket + */ readonly bucketName: string; + + /** + * The arn to assume (fileAssetPublishingRole) + */ readonly assumeRoleArn: string; } -export interface DockerAssetInfo { +/** + * Information returned by the Staging Stack for each image asset + */ +export interface ImageAssetInfo { + /** + * The name of the staging repository + */ readonly repoName: string; + + /** + * The arn to assume (imageAssetPublishingRole) + */ readonly assumeRoleArn: string; } + /** * Information on how a Staging Stack should look. */ export interface IStagingStack extends IConstruct { - /** - * App level unique identifier - */ - readonly appId: string; - /** * The app-scoped, environment-keyed bucket created in this staging stack. */ @@ -46,17 +60,20 @@ export interface IStagingStack extends IConstruct { */ readonly stagingRepos: Record; + /** + * The stack to add dependencies to. + */ readonly dependencyStack: Stack; /** - * // TODO + * Return staging resource information for a file asset. */ addFile(asset: FileAssetSource): FileAssetInfo; /** - * // TODO + * Return staging resource information for a docker asset. */ - addDockerImage(asset: DockerImageAssetSource): DockerAssetInfo; + addDockerImage(asset: DockerImageAssetSource): ImageAssetInfo; } /** @@ -64,7 +81,7 @@ export interface IStagingStack extends IConstruct { */ export interface DefaultStagingStackProps extends StackProps { /** - * App id + * The unique id of the app that the staging stack is scoped to. */ readonly appId: string; @@ -87,18 +104,18 @@ export interface DefaultStagingStackProps extends StackProps { * * @default - a well-known name unique to this app/env. */ - readonly dockerAssetPublishingRole?: BootstrapRole; + readonly imageAssetPublishingRole?: BootstrapRole; /** * Repository lifecycle rules (not fully implemented) */ - readonly repositoryLifecycleRules?: StagingRepoLifecycleRule[]; + // readonly repositoryLifecycleRules?: StagingRepoLifecycleRule[]; } -export interface StagingRepoLifecycleRule { - readonly lifecycleRules: ecr.LifecycleRule[]; - readonly assets: string[]; -} +// export interface StagingRepoLifecycleRule { +// readonly lifecycleRules: ecr.LifecycleRule[]; +// readonly assets: string[]; +// } /** * A default Staging Stack @@ -114,7 +131,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { /** * Default asset publishing role name for docker (ECR) assets. */ - private get DEFAULT_DOCKER_ASSET_PUBISHING_ROLE_NAME() { + private get DEFAULT_IMAGE_ASSET_PUBISHING_ROLE_NAME() { return `cdk-asset-publishing-role-${this.region}-${this.appId}`.slice(0, 63); } @@ -128,12 +145,18 @@ export class DefaultStagingStack extends Stack implements IStagingStack { */ public readonly stagingRepos: Record; + /** + * The stack to add dependencies to. + */ public readonly dependencyStack: Stack; - public readonly appId: string; + + private readonly appId: string; private readonly stagingBucketName?: string; - private fileAssetPublishingRoleArn?: string; - private dockerAssetPublishingRoleArn?: string; - private readonly repositoryLifecycleRules: Record; + private readonly fileAssetPublishingRoleArn?: string; + private readonly fileAssetPublishingRoleId = 'CdkFilePublishingRole'; + private readonly imageAssetPublishingRoleArn?: string; + private readonly imageAssetPublishingRoleId = 'CdkImagePublishingRole'; + // private readonly repositoryLifecycleRules: Record; constructor(scope: App, id: string, props: DefaultStagingStackProps) { super(scope, id, { @@ -146,9 +169,9 @@ export class DefaultStagingStack extends Stack implements IStagingStack { this.stagingBucketName = props.stagingBucketName; this.fileAssetPublishingRoleArn = props.fileAssetPublishingRole ? this.validateStagingRole(props.fileAssetPublishingRole).roleArn : undefined; - this.dockerAssetPublishingRoleArn = props.dockerAssetPublishingRole ? - this.validateStagingRole(props.dockerAssetPublishingRole).roleArn : undefined; - this.repositoryLifecycleRules = this.processLifecycleRules(props.repositoryLifecycleRules ?? []); + this.imageAssetPublishingRoleArn = props.imageAssetPublishingRole ? + this.validateStagingRole(props.imageAssetPublishingRole).roleArn : undefined; + // this.repositoryLifecycleRules = this.processLifecycleRules(props.repositoryLifecycleRules ?? []); this.stagingRepos = {}; } @@ -159,32 +182,29 @@ export class DefaultStagingStack extends Stack implements IStagingStack { return stagingRole; } - private processLifecycleRules(rules: StagingRepoLifecycleRule[]) { - const ruleMap: Record = {}; - for (const rule of rules) { - for (const asset of rule.assets) { - if (ruleMap[asset] === undefined) { - ruleMap[asset] = []; - } - ruleMap[asset].push(...rule.lifecycleRules); - } - } - return ruleMap; - } + // private processLifecycleRules(rules: StagingRepoLifecycleRule[]) { + // const ruleMap: Record = {}; + // for (const rule of rules) { + // for (const asset of rule.assets) { + // if (ruleMap[asset] === undefined) { + // ruleMap[asset] = []; + // } + // ruleMap[asset].push(...rule.lifecycleRules); + // } + // } + // return ruleMap; + // } - /** - * Returns the file publishing role arn - */ private getFilePublishingRoleArn(): string { if (this.fileAssetPublishingRoleArn) { return this.fileAssetPublishingRoleArn; } - const role = this.node.tryFindChild('CdkFilePublishingRole') as iam.Role; + const role = this.node.tryFindChild(this.fileAssetPublishingRoleId) as iam.Role; if (role === undefined) { throw new Error('Cannot call getFilePublishingRoleArn before createFilePublishingRole'); } return Stack.of(this).formatArn({ - partition: '${AWS::Partition}', // TODO: token partition doesn't work + partition: '${AWS::Partition}', region: '', // iam is global service: 'iam', resource: 'role', @@ -193,55 +213,46 @@ export class DefaultStagingStack extends Stack implements IStagingStack { }); } - /** - * Creates the file publishing role - */ private createFilePublishingRole() { const roleName = this.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_NAME; - const role = new iam.Role(this, 'CdkFilePublishingRole', { - roleName: roleName, + const role = new iam.Role(this, this.fileAssetPublishingRoleId, { + roleName, assumedBy: new iam.AccountPrincipal(this.account), }); return role; } - /** - * Returns the well-known name of the image publishing role - */ - private getCreateImagePublishingRole() { - if (this.dockerAssetPublishingRoleArn) { - return this.dockerAssetPublishingRoleArn; + private getImagePublishingRoleArn(): string { + if (this.imageAssetPublishingRoleArn) { + return this.imageAssetPublishingRoleArn; } - - const roleId = 'CdkDockerAssetPublishingRole'; - const roleName = this.DEFAULT_DOCKER_ASSET_PUBISHING_ROLE_NAME; - - const createIamRole = () => { - const role = new iam.Role(this, roleId, { - roleName: roleName, - assumedBy: new iam.ServicePrincipal('sts.amazonaws.com'), - }); - role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('AdministratorAccess')); - return role; - }; - - this.node.tryFindChild(roleId) as iam.Role ?? createIamRole(); - - return Arn.format({ - partition: this.partition ?? Aws.PARTITION, - account: this.account ?? Aws.ACCOUNT_ID, - region: this.region ?? Aws.REGION, + const role = this.node.tryFindChild(this.imageAssetPublishingRoleId) as iam.Role; + if (role === undefined) { + throw new Error('Cannot call getImagePublishingRoleArn before createImagePublishingRole'); + } + return Stack.of(this).formatArn({ + partition: '${AWS::{artition}', + region: '', // iam is global service: 'iam', resource: 'role', - resourceName: roleName, - arnFormat: ArnFormat.COLON_RESOURCE_NAME, + resourceName: this.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_NAME, + arnFormat: ArnFormat.SLASH_RESOURCE_NAME, }); } + private createImagePublishingRole() { + const roleName = this.DEFAULT_IMAGE_ASSET_PUBISHING_ROLE_NAME; + const role = new iam.Role(this, this.imageAssetPublishingRoleId, { + roleName, + assumedBy: new iam.AccountPrincipal(this.account), + }); + return role; + } + private createBucketKey(): kms.IKey { const bucketKeyId = 'BucketKey'; const key = this.node.tryFindChild(bucketKeyId) as kms.IKey ?? new kms.Key(this, bucketKeyId, { - // TODO add alias + alias: `CdkStagingBucketKey${this.account}-${this.region}-${this.appId}`, admins: [new iam.AccountPrincipal(this.account)], }); return key; @@ -280,11 +291,16 @@ export class DefaultStagingStack extends Stack implements IStagingStack { throw new Error('Assets synthesized with AppScopedStagingSynthesizer must include a \'uniqueId\' in the asset source definition.'); } + // Create image publishing role if it doesn't exist + this.node.tryFindChild(this.imageAssetPublishingRoleId) as iam.Role ?? this.createImagePublishingRole(); + + // TODO: grant permissions to the role + const repoName = `${asset.assetName}`.replace('.', '-'); // TODO: actually sanitize if (this.stagingRepos[asset.assetName] === undefined) { this.stagingRepos[asset.assetName] = new ecr.Repository(this, repoName, { repositoryName: repoName, - lifecycleRules: this.repositoryLifecycleRules[asset.assetName], + // lifecycleRules: this.repositoryLifecycleRules[asset.assetName], }); } return repoName; @@ -297,10 +313,10 @@ export class DefaultStagingStack extends Stack implements IStagingStack { }; } - public addDockerImage(asset: DockerImageAssetSource): DockerAssetInfo { + public addDockerImage(asset: DockerImageAssetSource): ImageAssetInfo { return { repoName: this.getCreateRepo(asset), - assumeRoleArn: this.getCreateImagePublishingRole(), + assumeRoleArn: this.getImagePublishingRoleArn(), }; } } diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/index.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/index.ts index afe13d9c34942..8c658ccabed61 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/index.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/index.ts @@ -1,2 +1,3 @@ export * from './default-staging-stack'; export * from './app-staging-synthesizer'; +export * from './bootstrap-roles'; \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts index 0d208c01c6567..bb22949f624d0 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable jest/no-commented-out-tests */ import * as fs from 'fs'; import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import { App, Stack, CfnResource, FileAssetPackaging, Token } from '@aws-cdk/core'; @@ -143,64 +144,64 @@ describe(AppStagingSynthesizer, () => { expect(location.objectKey.indexOf('abcdef')).toBeGreaterThan(-1); }); - test('add docker image asset', () => { - // WHEN - const location = stack.synthesizer.addDockerImageAsset({ - directoryName: '.', - sourceHash: 'abcdef', - assetName: 'abcdef', - }); - - // THEN - we have a fixed asset location - const repo = 'abcdef'; - expect(evalCFN(location.repositoryName)).toEqual(repo); - expect(evalCFN(location.imageUri)).toEqual(`000000000000.dkr.ecr.us-east-1.domain.aws/${repo}:abcdef`); - }); - - test('throws with docker image asset without uniqueId', () => { - expect(() => stack.synthesizer.addDockerImageAsset({ - directoryName: '.', - sourceHash: 'abcdef', - })).toThrowError('Assets synthesized with AppScopedStagingSynthesizer must include a \'uniqueId\' in the asset source definition.'); - }); - - test('separate docker image assets have separate repos', () => { - // WHEN - const location1 = stack.synthesizer.addDockerImageAsset({ - directoryName: '.', - sourceHash: 'abcdef', - assetName: 'abcdef', - }); - - const location2 = stack.synthesizer.addDockerImageAsset({ - directoryName: './hello', - sourceHash: 'abcdefg', - assetName: 'abcdefg', - }); - - // THEN - images have different asset locations - expect(evalCFN(location1.repositoryName)).not.toEqual(evalCFN(location2.repositoryName)); - }); - - test('docker image assets with same unique id have same repos', () => { - // WHEN - const location1 = stack.synthesizer.addDockerImageAsset({ - directoryName: '.', - sourceHash: 'abcdef', - assetName: 'abcdef', - }); - - const location2 = stack.synthesizer.addDockerImageAsset({ - directoryName: './hello', - sourceHash: 'abcdefg', - assetName: 'abcdef', - }); - - // THEN - images share same ecr repo - const repo = 'abcdef'; - expect(evalCFN(location1.repositoryName)).toEqual(repo); - expect(evalCFN(location1.repositoryName)).toEqual(evalCFN(location2.repositoryName)); - }); + // test('add docker image asset', () => { + // // WHEN + // const location = stack.synthesizer.addDockerImageAsset({ + // directoryName: '.', + // sourceHash: 'abcdef', + // assetName: 'abcdef', + // }); + + // // THEN - we have a fixed asset location + // const repo = 'abcdef'; + // expect(evalCFN(location.repositoryName)).toEqual(repo); + // expect(evalCFN(location.imageUri)).toEqual(`000000000000.dkr.ecr.us-east-1.domain.aws/${repo}:abcdef`); + // }); + + // test('throws with docker image asset without uniqueId', () => { + // expect(() => stack.synthesizer.addDockerImageAsset({ + // directoryName: '.', + // sourceHash: 'abcdef', + // })).toThrowError('Assets synthesized with AppScopedStagingSynthesizer must include a \'uniqueId\' in the asset source definition.'); + // }); + + // test('separate docker image assets have separate repos', () => { + // // WHEN + // const location1 = stack.synthesizer.addDockerImageAsset({ + // directoryName: '.', + // sourceHash: 'abcdef', + // assetName: 'abcdef', + // }); + + // const location2 = stack.synthesizer.addDockerImageAsset({ + // directoryName: './hello', + // sourceHash: 'abcdefg', + // assetName: 'abcdefg', + // }); + + // // THEN - images have different asset locations + // expect(evalCFN(location1.repositoryName)).not.toEqual(evalCFN(location2.repositoryName)); + // }); + + // test('docker image assets with same unique id have same repos', () => { + // // WHEN + // const location1 = stack.synthesizer.addDockerImageAsset({ + // directoryName: '.', + // sourceHash: 'abcdef', + // assetName: 'abcdef', + // }); + + // const location2 = stack.synthesizer.addDockerImageAsset({ + // directoryName: './hello', + // sourceHash: 'abcdefg', + // assetName: 'abcdef', + // }); + + // // THEN - images share same ecr repo + // const repo = 'abcdef'; + // expect(evalCFN(location1.repositoryName)).toEqual(repo); + // expect(evalCFN(location1.repositoryName)).toEqual(evalCFN(location2.repositoryName)); + // }); describe('environment specifics', () => { test('throws if App includes env-agnostic and specific env stacks', () => { From 4fbae1b17257b401267bd0e70857297a897bb9f6 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Fri, 24 Mar 2023 14:30:56 -0400 Subject: [PATCH 049/120] more docs --- .../lib/app-staging-synthesizer.ts | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts index 8b324c51deab5..8feda758418c0 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts @@ -72,7 +72,7 @@ export interface CustomFactoryProps extends AppStagingSynthesizerProps { * * @default true */ - oncePerEnv: boolean; + // oncePerEnv: boolean; } /** @@ -108,6 +108,10 @@ interface AppStagingSynthesizerProps { * App Staging Synthesizer */ export class AppStagingSynthesizer extends StackSynthesizer implements IReusableStackSynthesizer { + /** + * Use the Default Staging Stack as the Staging Stack for this Synthesizer, and + * create a new Staging Stack per environment this App is deployed in. + */ public static stackPerEnv(props: StackPerEnvProps) { for (const key in props) { if (props.hasOwnProperty(key)) { @@ -183,10 +187,30 @@ export class AppStagingSynthesizer extends StackSynthesizer implements IReusable }); } + /** + * Supply your own stagingStackFactory method for creating an IStagingStack when + * a stack is bound to the synthesizer. + * + * By default, `oncePerEnv = true`, which means that a new instance of the IStagingStack + * will be created in new environments. Set `oncePerEnv = false` to turn off that behavior. + */ public static customFactory(props: CustomFactoryProps) { return new AppStagingSynthesizer(props); } + /** + * Supply a specific stack to be used as the Staging Stack for this App. + */ + public static customDecider(stack: IStagingStack) { + return new AppStagingSynthesizer({ + stagingStackFactory: { + stagingStackFactory(_boundStack: Stack) { + return stack; + }, + }, + }); + } + private constructor(private readonly props: AppStagingSynthesizerProps) { super(); } From d3ec8a77727b37238ce1eadb11e1d383f54800e1 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Fri, 24 Mar 2023 14:59:42 -0400 Subject: [PATCH 050/120] couple more tests --- .../app-staging-synthesizer/jest.config.js | 10 +++- .../test/app-staging-synthesizer.test.ts | 46 +++++++++++++++++-- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer/jest.config.js b/packages/@aws-cdk/app-staging-synthesizer/jest.config.js index 3a2fd93a1228a..15b6600b559c2 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/jest.config.js +++ b/packages/@aws-cdk/app-staging-synthesizer/jest.config.js @@ -1,2 +1,10 @@ const baseConfig = require('@aws-cdk/cdk-build-tools/config/jest.config'); -module.exports = baseConfig; +module.exports = { + ...baseConfig, + coverageThreshold: { + global: { + branches: 75, + statements: 75, + } + } +}; diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts index bb22949f624d0..225b4f0ddb968 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts @@ -1,10 +1,12 @@ /* eslint-disable jest/no-commented-out-tests */ import * as fs from 'fs'; import * as cxschema from '@aws-cdk/cloud-assembly-schema'; -import { App, Stack, CfnResource, FileAssetPackaging, Token } from '@aws-cdk/core'; +import { App, Stack, CfnResource, FileAssetPackaging, Token, Lazy, FileAssetSource, DockerImageAssetSource } from '@aws-cdk/core'; import { evaluateCFN } from '@aws-cdk/core/test/evaluate-cfn'; import * as cxapi from '@aws-cdk/cx-api'; -import { AppStagingSynthesizer, BootstrapRole, StackPerEnvProps } from '../lib'; +import { AppStagingSynthesizer, BootstrapRole, FileAssetInfo, ImageAssetInfo, IStagingStack, StackPerEnvProps } from '../lib'; +import { Repository } from '@aws-cdk/aws-ecr'; +import { Bucket } from '@aws-cdk/aws-s3'; const CFN_CONTEXT = { 'AWS::Region': 'the_region', @@ -144,6 +146,23 @@ describe(AppStagingSynthesizer, () => { expect(location.objectKey.indexOf('abcdef')).toBeGreaterThan(-1); }); + test('adding multiple files only creates one bucket', () => { + // WHEN + const location1 = stack.synthesizer.addFileAsset({ + fileName: __filename, + packaging: FileAssetPackaging.FILE, + sourceHash: 'abcdef', + }); + const location2 = stack.synthesizer.addFileAsset({ + fileName: __filename, + packaging: FileAssetPackaging.FILE, + sourceHash: 'zyxwvu', + }); + + // THEN - assets have the same location + expect(evalCFN(location1.bucketName)).toEqual(evalCFN(location2.bucketName)); + }); + // test('add docker image asset', () => { // // WHEN // const location = stack.synthesizer.addDockerImageAsset({ @@ -225,6 +244,14 @@ describe(AppStagingSynthesizer, () => { }); }); + test('throws if synthesizer props have tokens', () => { + expect(() => new App({ + defaultStackSynthesizer: TestAppScopedStagingSynthesizer.stackPerEnv({ + appId: Lazy.string({ produce: () => 'appId' }), + }), + })).toThrowError(/AppStagingSynthesizer property 'appId' cannot contain tokens;/); + }); + /** * Evaluate a possibly string-containing value the same way CFN would do * @@ -302,7 +329,7 @@ describe('Boostrap Roles', () => { expect(firstFile.destinations['000000000000-us-east-1'].assumeRoleArn).toEqual('arn'); }); - test('can specify using current cli credentials instead', () => { + test('bootstrap roles can be specified as current cli credentials instead', () => { // GIVEN const app = new App({ defaultStackSynthesizer: AppStagingSynthesizer.stackPerEnv({ @@ -335,6 +362,19 @@ describe('Boostrap Roles', () => { expect(stackArtifact.lookupRole).toBeUndefined(); expect(stackArtifact.assumeRoleArn).toBeUndefined(); }); + + test('staging roles cannot be specified as cli credentials', () => { + const app = new App({ + defaultStackSynthesizer: AppStagingSynthesizer.stackPerEnv({ + appId: APP_ID, + stagingRoles: { + fileAssetPublishingRole: BootstrapRole.cliCredentials(), + }, + }), + }); + + expect(() => new Stack(app, 'Stack')).toThrowError('fileAssetPublishingRole and dockerAssetPublishingRole cannot be specified as cliCredentials(). Please supply an arn to reference an existing IAM role.'); + }); }); function isAssetManifest(x: cxapi.CloudArtifact): x is cxapi.AssetManifestArtifact { From bdd8a7131f782461c0f0dbda36278dd28d6f6686 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Tue, 4 Apr 2023 17:14:37 -0400 Subject: [PATCH 051/120] get package to work under new repo restructure --- .../lib/app-staging-synthesizer.ts | 12 ++++++-- .../lib/bootstrap-roles.ts | 12 +++++++- .../lib/default-staging-stack.ts | 10 +++---- .../app-staging-synthesizer/package.json | 30 +++++-------------- .../test/app-staging-synthesizer.test.ts | 14 ++++----- .../test/integ.synthesizer.ts | 4 +-- 6 files changed, 42 insertions(+), 40 deletions(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts index 8feda758418c0..7bd0b32f6ed1a 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts @@ -12,8 +12,8 @@ import { Stack, StackSynthesizer, Token, -} from '@aws-cdk/core'; -import * as cxapi from '@aws-cdk/cx-api'; +} from 'aws-cdk-lib'; +import * as cxapi from 'aws-cdk-lib/cx-api'; import { BootstrapRoles, StagingRoles } from './bootstrap-roles'; import { IStagingStack as IStagingStack, DefaultStagingStack } from './default-staging-stack'; @@ -60,6 +60,14 @@ export interface StackPerEnvProps { * staging resources for the Stack. */ export interface IStagingStackFactory { + /** + * Factory method to be called when binding stack to synthesizer. + * This method produces (either by creating or referencing an existing + * stack) the StagingStack that holds staging resources + * necessary for the bound stack. + * + * @param boundStack - stack to bind the synthesizer to + */ stagingStackFactory(boundStack: Stack): IStagingStack; } diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts index d0f57af0ac18d..4b54a0e089604 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts @@ -17,7 +17,7 @@ export class BootstrapRole { return new BootstrapRole(arn); } - private constructor(public readonly roleArn: string | undefined) {} + private constructor(/** Bootstrap role arn */ public readonly roleArn: string | undefined) {} } /** @@ -26,16 +26,22 @@ export class BootstrapRole { export interface BootstrapRoles { /** * CloudFormation Execution Role + * + * @default - use bootstrapped role */ readonly cloudFormationExecutionRole?: BootstrapRole; /** * Deployment Action Role + * + * @default - use boostrapped role */ readonly deploymentActionRole?: BootstrapRole; /** * Lookup Role + * + * @default - use bootstrapped role */ readonly lookupRole?: BootstrapRole; } @@ -47,11 +53,15 @@ export interface BootstrapRoles { export interface StagingRoles { /** * File Asset Publishing Role + * + * @default - staging stack creates a file asset publishing role */ readonly fileAssetPublishingRole?: BootstrapRole; /** * Docker Asset Publishing Role + * + * @default - staging stack creates a docker asset publishing role */ readonly dockerAssetPublishingRole?: BootstrapRole; } diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts index 8b46fdb6e39bd..10ef738ebfcfb 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts @@ -1,7 +1,3 @@ -import * as ecr from '@aws-cdk/aws-ecr'; -import * as iam from '@aws-cdk/aws-iam'; -import * as kms from '@aws-cdk/aws-kms'; -import * as s3 from '@aws-cdk/aws-s3'; import { App, ArnFormat, @@ -11,7 +7,11 @@ import { RemovalPolicy, Stack, StackProps, -} from '@aws-cdk/core'; +} from 'aws-cdk-lib'; +import * as ecr from 'aws-cdk-lib/aws-ecr'; +import * as iam from 'aws-cdk-lib/aws-iam'; +import * as kms from 'aws-cdk-lib/aws-kms'; +import * as s3 from 'aws-cdk-lib/aws-s3'; import { IConstruct } from 'constructs'; import { BootstrapRole } from './bootstrap-roles'; diff --git a/packages/@aws-cdk/app-staging-synthesizer/package.json b/packages/@aws-cdk/app-staging-synthesizer/package.json index ad8b9bbc5805f..a6262c9fd3186 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/package.json +++ b/packages/@aws-cdk/app-staging-synthesizer/package.json @@ -83,35 +83,19 @@ } }, "dependencies": { - "@aws-cdk/core": "0.0.0", - "@aws-cdk/cx-api": "0.0.0", - "constructs": "^10.0.0", - "@aws-cdk/aws-s3": "0.0.0", - "@aws-cdk/aws-kms": "0.0.0", - "@aws-cdk/aws-ecr": "0.0.0", - "@aws-cdk/aws-iam": "0.0.0" + "aws-cdk-lib": "0.0.0", + "constructs": "^10.0.0" }, "devDependencies": { - "@aws-cdk/core": "0.0.0", - "@aws-cdk/cloud-assembly-schema": "0.0.0", + "aws-cdk-lib": "0.0.0", "@aws-cdk/integ-runner": "0.0.0", - "@aws-cdk/integ-tests": "0.0.0", - "@aws-cdk/aws-lambda": "0.0.0", + "@aws-cdk/integ-tests-alpha": "0.0.0", "constructs": "^10.0.0", - "@aws-cdk/aws-s3": "0.0.0", - "@aws-cdk/aws-kms": "0.0.0", - "@aws-cdk/aws-ecr": "0.0.0", - "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", - "@aws-cdk/pkglint": "0.0.0", - "jsii": "v4.9-next" + "@aws-cdk/pkglint": "0.0.0" }, "peerDependencies": { - "@aws-cdk/core": "0.0.0", - "constructs": "^10.0.0", - "@aws-cdk/aws-s3": "0.0.0", - "@aws-cdk/aws-ecr": "0.0.0", - "@aws-cdk/aws-iam": "0.0.0", - "@aws-cdk/aws-kms": "0.0.0" + "aws-cdk-lib": "0.0.0", + "constructs": "^10.0.0" } } diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts index 225b4f0ddb968..0f6ce54fd414e 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts @@ -1,12 +1,12 @@ /* eslint-disable jest/no-commented-out-tests */ import * as fs from 'fs'; -import * as cxschema from '@aws-cdk/cloud-assembly-schema'; -import { App, Stack, CfnResource, FileAssetPackaging, Token, Lazy, FileAssetSource, DockerImageAssetSource } from '@aws-cdk/core'; -import { evaluateCFN } from '@aws-cdk/core/test/evaluate-cfn'; -import * as cxapi from '@aws-cdk/cx-api'; -import { AppStagingSynthesizer, BootstrapRole, FileAssetInfo, ImageAssetInfo, IStagingStack, StackPerEnvProps } from '../lib'; -import { Repository } from '@aws-cdk/aws-ecr'; -import { Bucket } from '@aws-cdk/aws-s3'; +import { App, Stack, CfnResource, FileAssetPackaging, Token, Lazy } from 'aws-cdk-lib'; +import * as cxschema from 'aws-cdk-lib/cloud-assembly-schema'; +import { evaluateCFN } from 'aws-cdk-lib/core/test/evaluate-cfn'; +import * as cxapi from 'aws-cdk-lib/cx-api'; +import { AppStagingSynthesizer, BootstrapRole, StackPerEnvProps } from '../lib'; +// import { Repository } from 'aws-cdk-lib/aws-ecr'; +// import { Bucket } from 'aws-cdk-lib/aws-s3'; const CFN_CONTEXT = { 'AWS::Region': 'the_region', diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts index 0ba12074ba217..52c03275c2057 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts @@ -1,6 +1,6 @@ import * as path from 'path'; -import * as lambda from '@aws-cdk/aws-lambda'; -import { App, Stack } from '@aws-cdk/core'; +import { App, Stack } from 'aws-cdk-lib'; +import * as lambda from 'aws-cdk-lib/aws-lambda'; import { AppStagingSynthesizer } from '../lib'; const app = new App(); From 806221e4d6203266cb51525f625b2711a425a74f Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 5 Apr 2023 09:51:21 -0400 Subject: [PATCH 052/120] rename to -alpha --- .../.eslintrc.js | 0 .../.gitignore | 0 .../.npmignore | 0 .../LICENSE | 0 .../NOTICE | 0 .../README.md | 0 .../adr/resource-names.md | 0 .../jest.config.js | 0 .../lib/app-staging-synthesizer.ts | 0 .../lib/bootstrap-roles.ts | 0 .../lib/default-staging-stack.ts | 0 .../lib/index.ts | 0 .../package.json | 0 .../test/app-staging-synthesizer.test.ts | 0 .../test/assets/index.py | 0 .../test/integ.synthesizer.js.snapshot/StagingStack.assets.json | 0 .../test/integ.synthesizer.js.snapshot/StagingStack.template.json | 0 .../app-scoped-staging-test.assets.json | 0 .../app-scoped-staging-test.template.json | 0 .../__entrypoint__.js | 0 .../index.js | 0 .../test/integ.synthesizer.js.snapshot/cdk.out | 0 .../test/integ.synthesizer.js.snapshot/integ.json | 0 .../test/integ.synthesizer.js.snapshot/manifest.json | 0 .../synthesizerintegDefaultTestDeployAssert208D3414.assets.json | 0 .../synthesizerintegDefaultTestDeployAssert208D3414.template.json | 0 .../test/integ.synthesizer.js.snapshot/tree.json | 0 .../test/integ.synthesizer.ts | 0 28 files changed, 0 insertions(+), 0 deletions(-) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/.eslintrc.js (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/.gitignore (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/.npmignore (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/LICENSE (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/NOTICE (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/README.md (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/adr/resource-names.md (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/jest.config.js (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/lib/app-staging-synthesizer.ts (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/lib/bootstrap-roles.ts (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/lib/default-staging-stack.ts (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/lib/index.ts (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/package.json (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/app-staging-synthesizer.test.ts (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/assets/index.py (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/integ.synthesizer.js.snapshot/StagingStack.assets.json (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/integ.synthesizer.js.snapshot/StagingStack.template.json (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/integ.synthesizer.js.snapshot/cdk.out (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/integ.synthesizer.js.snapshot/integ.json (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/integ.synthesizer.js.snapshot/manifest.json (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/integ.synthesizer.js.snapshot/tree.json (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/integ.synthesizer.ts (100%) diff --git a/packages/@aws-cdk/app-staging-synthesizer/.eslintrc.js b/packages/@aws-cdk/app-staging-synthesizer-alpha/.eslintrc.js similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/.eslintrc.js rename to packages/@aws-cdk/app-staging-synthesizer-alpha/.eslintrc.js diff --git a/packages/@aws-cdk/app-staging-synthesizer/.gitignore b/packages/@aws-cdk/app-staging-synthesizer-alpha/.gitignore similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/.gitignore rename to packages/@aws-cdk/app-staging-synthesizer-alpha/.gitignore diff --git a/packages/@aws-cdk/app-staging-synthesizer/.npmignore b/packages/@aws-cdk/app-staging-synthesizer-alpha/.npmignore similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/.npmignore rename to packages/@aws-cdk/app-staging-synthesizer-alpha/.npmignore diff --git a/packages/@aws-cdk/app-staging-synthesizer/LICENSE b/packages/@aws-cdk/app-staging-synthesizer-alpha/LICENSE similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/LICENSE rename to packages/@aws-cdk/app-staging-synthesizer-alpha/LICENSE diff --git a/packages/@aws-cdk/app-staging-synthesizer/NOTICE b/packages/@aws-cdk/app-staging-synthesizer-alpha/NOTICE similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/NOTICE rename to packages/@aws-cdk/app-staging-synthesizer-alpha/NOTICE diff --git a/packages/@aws-cdk/app-staging-synthesizer/README.md b/packages/@aws-cdk/app-staging-synthesizer-alpha/README.md similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/README.md rename to packages/@aws-cdk/app-staging-synthesizer-alpha/README.md diff --git a/packages/@aws-cdk/app-staging-synthesizer/adr/resource-names.md b/packages/@aws-cdk/app-staging-synthesizer-alpha/adr/resource-names.md similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/adr/resource-names.md rename to packages/@aws-cdk/app-staging-synthesizer-alpha/adr/resource-names.md diff --git a/packages/@aws-cdk/app-staging-synthesizer/jest.config.js b/packages/@aws-cdk/app-staging-synthesizer-alpha/jest.config.js similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/jest.config.js rename to packages/@aws-cdk/app-staging-synthesizer-alpha/jest.config.js diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/app-staging-synthesizer.ts similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts rename to packages/@aws-cdk/app-staging-synthesizer-alpha/lib/app-staging-synthesizer.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/bootstrap-roles.ts similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts rename to packages/@aws-cdk/app-staging-synthesizer-alpha/lib/bootstrap-roles.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/default-staging-stack.ts similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts rename to packages/@aws-cdk/app-staging-synthesizer-alpha/lib/default-staging-stack.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/index.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/index.ts similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/lib/index.ts rename to packages/@aws-cdk/app-staging-synthesizer-alpha/lib/index.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/package.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/package.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/package.json rename to packages/@aws-cdk/app-staging-synthesizer-alpha/package.json diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/app-staging-synthesizer.test.ts similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/app-staging-synthesizer.test.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/assets/index.py b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/assets/index.py similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/assets/index.py rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/assets/index.py diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.assets.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/StagingStack.assets.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.assets.json rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/StagingStack.assets.json diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.template.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/StagingStack.template.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.template.json rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/StagingStack.template.json diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/cdk.out b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/cdk.out similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/cdk.out rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/cdk.out diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integ.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/integ.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integ.json rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/integ.json diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/manifest.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/manifest.json diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/tree.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/tree.json diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.ts similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.ts From 6fcc13d4f5c131f51635c354ac28af4fdce75106 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 5 Apr 2023 09:56:28 -0400 Subject: [PATCH 053/120] update package.json --- .../app-staging-synthesizer-alpha/package.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/package.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/package.json index a6262c9fd3186..7b5e5e9ef5dee 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/package.json +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/package.json @@ -1,5 +1,5 @@ { - "name": "@aws-cdk/app-staging-synthesizer", + "name": "@aws-cdk/app-staging-synthesizer-alpha", "private": true, "version": "0.0.0", "description": "Cdk synthesizer for with app-scoped staging stack", @@ -19,20 +19,20 @@ "java": { "maven": { "groupId": "software.amazon.awscdk", - "artifactId": "cdk-app-staging-synthesizer" + "artifactId": "cdk-app-staging-synthesizer-alpha" }, - "package": "software.amazon.awscdk.app.staging.synthesizer" + "package": "software.amazon.awscdk.app.staging.synthesizer.alpha" }, "python": { - "distName": "aws-cdk.app-staging-synthesizer", - "module": "aws_cdk.app_staging_synthesizer", + "distName": "aws-cdk.app-staging-synthesizer-alpha", + "module": "aws_cdk.app_staging_synthesizer_alpha", "classifiers": [ "Framework :: AWS CDK", "Framework :: AWS CDK :: 2" ] }, "dotnet": { - "namespace": "Amazon.CDK.App.Staging.Synthesizer", + "namespace": "Amazon.CDK.App.Staging.Synthesizer.Alpha", "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/main/logo/default-256-dark.png" } } @@ -40,7 +40,7 @@ "repository": { "type": "git", "url": "https://github.com/aws/aws-cdk.git", - "directory": "packages/@aws-cdk/app-staging-synthesizer" + "directory": "packages/@aws-cdk/app-staging-synthesizer-alpha" }, "scripts": { "build": "cdk-build", From 477f81fe1397037bcd57a568125cb2c1ad12cdff Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 5 Apr 2023 15:11:19 -0400 Subject: [PATCH 054/120] rename --- .../.eslintrc.js | 0 .../.gitignore | 0 .../.npmignore | 0 .../LICENSE | 0 .../NOTICE | 0 .../README.md | 0 .../adr/resource-names.md | 0 .../jest.config.js | 0 .../lib/app-staging-synthesizer.ts | 0 .../lib/bootstrap-roles.ts | 0 .../lib/default-staging-stack.ts | 0 .../lib/index.ts | 0 .../package.json | 0 .../test/app-staging-synthesizer.test.ts | 0 .../test/assets/index.py | 0 .../StagingStack.assets.json | 0 .../StagingStack.template.json | 0 .../app-scoped-staging-test.assets.json | 0 .../app-scoped-staging-test.template.json | 0 .../__entrypoint__.js | 0 .../index.js | 0 .../test/integ.synthesizer.js.snapshot/cdk.out | 0 .../test/integ.synthesizer.js.snapshot/integ.json | 0 .../integ.synthesizer.js.snapshot/manifest.json | 0 ...tegDefaultTestDeployAssert208D3414.assets.json | 0 ...gDefaultTestDeployAssert208D3414.template.json | 0 .../test/integ.synthesizer.js.snapshot/tree.json | 0 .../test/integ.synthesizer.ts | 15 +++++++++++++-- 28 files changed, 13 insertions(+), 2 deletions(-) rename packages/@aws-cdk/{app-staging-synthesizer-alpha => app-staging-synthesizer}/.eslintrc.js (100%) rename packages/@aws-cdk/{app-staging-synthesizer-alpha => app-staging-synthesizer}/.gitignore (100%) rename packages/@aws-cdk/{app-staging-synthesizer-alpha => app-staging-synthesizer}/.npmignore (100%) rename packages/@aws-cdk/{app-staging-synthesizer-alpha => app-staging-synthesizer}/LICENSE (100%) rename packages/@aws-cdk/{app-staging-synthesizer-alpha => app-staging-synthesizer}/NOTICE (100%) rename packages/@aws-cdk/{app-staging-synthesizer-alpha => app-staging-synthesizer}/README.md (100%) rename packages/@aws-cdk/{app-staging-synthesizer-alpha => app-staging-synthesizer}/adr/resource-names.md (100%) rename packages/@aws-cdk/{app-staging-synthesizer-alpha => app-staging-synthesizer}/jest.config.js (100%) rename packages/@aws-cdk/{app-staging-synthesizer-alpha => app-staging-synthesizer}/lib/app-staging-synthesizer.ts (100%) rename packages/@aws-cdk/{app-staging-synthesizer-alpha => app-staging-synthesizer}/lib/bootstrap-roles.ts (100%) rename packages/@aws-cdk/{app-staging-synthesizer-alpha => app-staging-synthesizer}/lib/default-staging-stack.ts (100%) rename packages/@aws-cdk/{app-staging-synthesizer-alpha => app-staging-synthesizer}/lib/index.ts (100%) rename packages/@aws-cdk/{app-staging-synthesizer-alpha => app-staging-synthesizer}/package.json (100%) rename packages/@aws-cdk/{app-staging-synthesizer-alpha => app-staging-synthesizer}/test/app-staging-synthesizer.test.ts (100%) rename packages/@aws-cdk/{app-staging-synthesizer-alpha => app-staging-synthesizer}/test/assets/index.py (100%) rename packages/@aws-cdk/{app-staging-synthesizer-alpha => app-staging-synthesizer}/test/integ.synthesizer.js.snapshot/StagingStack.assets.json (100%) rename packages/@aws-cdk/{app-staging-synthesizer-alpha => app-staging-synthesizer}/test/integ.synthesizer.js.snapshot/StagingStack.template.json (100%) rename packages/@aws-cdk/{app-staging-synthesizer-alpha => app-staging-synthesizer}/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json (100%) rename packages/@aws-cdk/{app-staging-synthesizer-alpha => app-staging-synthesizer}/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json (100%) rename packages/@aws-cdk/{app-staging-synthesizer-alpha => app-staging-synthesizer}/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js (100%) rename packages/@aws-cdk/{app-staging-synthesizer-alpha => app-staging-synthesizer}/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js (100%) rename packages/@aws-cdk/{app-staging-synthesizer-alpha => app-staging-synthesizer}/test/integ.synthesizer.js.snapshot/cdk.out (100%) rename packages/@aws-cdk/{app-staging-synthesizer-alpha => app-staging-synthesizer}/test/integ.synthesizer.js.snapshot/integ.json (100%) rename packages/@aws-cdk/{app-staging-synthesizer-alpha => app-staging-synthesizer}/test/integ.synthesizer.js.snapshot/manifest.json (100%) rename packages/@aws-cdk/{app-staging-synthesizer-alpha => app-staging-synthesizer}/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json (100%) rename packages/@aws-cdk/{app-staging-synthesizer-alpha => app-staging-synthesizer}/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json (100%) rename packages/@aws-cdk/{app-staging-synthesizer-alpha => app-staging-synthesizer}/test/integ.synthesizer.js.snapshot/tree.json (100%) rename packages/@aws-cdk/{app-staging-synthesizer-alpha => app-staging-synthesizer}/test/integ.synthesizer.ts (50%) diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/.eslintrc.js b/packages/@aws-cdk/app-staging-synthesizer/.eslintrc.js similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer-alpha/.eslintrc.js rename to packages/@aws-cdk/app-staging-synthesizer/.eslintrc.js diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/.gitignore b/packages/@aws-cdk/app-staging-synthesizer/.gitignore similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer-alpha/.gitignore rename to packages/@aws-cdk/app-staging-synthesizer/.gitignore diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/.npmignore b/packages/@aws-cdk/app-staging-synthesizer/.npmignore similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer-alpha/.npmignore rename to packages/@aws-cdk/app-staging-synthesizer/.npmignore diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/LICENSE b/packages/@aws-cdk/app-staging-synthesizer/LICENSE similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer-alpha/LICENSE rename to packages/@aws-cdk/app-staging-synthesizer/LICENSE diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/NOTICE b/packages/@aws-cdk/app-staging-synthesizer/NOTICE similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer-alpha/NOTICE rename to packages/@aws-cdk/app-staging-synthesizer/NOTICE diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/README.md b/packages/@aws-cdk/app-staging-synthesizer/README.md similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer-alpha/README.md rename to packages/@aws-cdk/app-staging-synthesizer/README.md diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/adr/resource-names.md b/packages/@aws-cdk/app-staging-synthesizer/adr/resource-names.md similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer-alpha/adr/resource-names.md rename to packages/@aws-cdk/app-staging-synthesizer/adr/resource-names.md diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/jest.config.js b/packages/@aws-cdk/app-staging-synthesizer/jest.config.js similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer-alpha/jest.config.js rename to packages/@aws-cdk/app-staging-synthesizer/jest.config.js diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer-alpha/lib/app-staging-synthesizer.ts rename to packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/bootstrap-roles.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer-alpha/lib/bootstrap-roles.ts rename to packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer-alpha/lib/default-staging-stack.ts rename to packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/index.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/index.ts similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer-alpha/lib/index.ts rename to packages/@aws-cdk/app-staging-synthesizer/lib/index.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/package.json b/packages/@aws-cdk/app-staging-synthesizer/package.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer-alpha/package.json rename to packages/@aws-cdk/app-staging-synthesizer/package.json diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/app-staging-synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer-alpha/test/app-staging-synthesizer.test.ts rename to packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/assets/index.py b/packages/@aws-cdk/app-staging-synthesizer/test/assets/index.py similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer-alpha/test/assets/index.py rename to packages/@aws-cdk/app-staging-synthesizer/test/assets/index.py diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/StagingStack.assets.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.assets.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/StagingStack.assets.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.assets.json diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/StagingStack.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.template.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/StagingStack.template.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.template.json diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/cdk.out b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/cdk.out similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/cdk.out rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/cdk.out diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/integ.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integ.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/integ.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integ.json diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/manifest.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/manifest.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/tree.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.js.snapshot/tree.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts similarity index 50% rename from packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.ts rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts index 52c03275c2057..11cb0f42bf5c4 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts @@ -1,12 +1,19 @@ import * as path from 'path'; import { App, Stack } from 'aws-cdk-lib'; import * as lambda from 'aws-cdk-lib/aws-lambda'; -import { AppStagingSynthesizer } from '../lib'; +import { AppStagingSynthesizer, BootstrapRole } from '../lib'; +import * as integ from '@aws-cdk/integ-tests-alpha'; const app = new App(); const stack = new Stack(app, 'app-scoped-staging-test', { - synthesizer: AppStagingSynthesizer.stackPerEnv({ appId: 'app-id' }), + synthesizer: AppStagingSynthesizer.stackPerEnv({ + appId: 'app-id', + qualifier: 'hnb659fds', + bootstrapRoles: { + cloudFormationExecutionRole: BootstrapRole.fromRoleArn('arn:aws:iam::489318732371:role/cdk-hnb659fds-cfn-exec-role-489318732371-us-east-2'), + }, + }), env: { account: '489318732371', region: 'us-east-2', @@ -19,4 +26,8 @@ new lambda.Function(stack, 'lambda', { runtime: lambda.Runtime.PYTHON_3_9, }); +new integ.IntegTest(app, 'integ-tests', { + testCases: [stack], +}); + app.synth(); From a9e4b5aa9c8079b45fe81c6165b4730c32db5cb1 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 5 Apr 2023 17:15:19 -0400 Subject: [PATCH 055/120] integ test works --- .../app-staging-synthesizer/package.json | 14 +- .../StagingStack.assets.json | 32 -- .../StagingStack.template.json | 198 -------- ...ngStack489318732371us-east-2.template.json | 169 +++++++ .../app-scoped-staging-test.assets.json | 28 +- .../app-scoped-staging-test.template.json | 56 ++- .../__entrypoint__.js | 144 ------ .../index.js | 78 ---- .../index.py | 1 + .../integ.synthesizer.js.snapshot/cdk.out | 2 +- .../integ.synthesizer.js.snapshot/integ.json | 8 +- ...faultTestDeployAssert44C8D370.assets.json} | 4 +- ...ultTestDeployAssert44C8D370.template.json} | 0 .../manifest.json | 113 ++--- .../integ.synthesizer.js.snapshot/tree.json | 438 ++++++++++++------ .../test/integ.synthesizer.ts | 2 +- 16 files changed, 605 insertions(+), 682 deletions(-) delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.assets.json delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.template.json create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack489318732371us-east-2.template.json delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53/index.py rename packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/{synthesizerintegDefaultTestDeployAssert208D3414.assets.json => integtestsDefaultTestDeployAssert44C8D370.assets.json} (84%) rename packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/{synthesizerintegDefaultTestDeployAssert208D3414.template.json => integtestsDefaultTestDeployAssert44C8D370.template.json} (100%) diff --git a/packages/@aws-cdk/app-staging-synthesizer/package.json b/packages/@aws-cdk/app-staging-synthesizer/package.json index 7b5e5e9ef5dee..a6262c9fd3186 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/package.json +++ b/packages/@aws-cdk/app-staging-synthesizer/package.json @@ -1,5 +1,5 @@ { - "name": "@aws-cdk/app-staging-synthesizer-alpha", + "name": "@aws-cdk/app-staging-synthesizer", "private": true, "version": "0.0.0", "description": "Cdk synthesizer for with app-scoped staging stack", @@ -19,20 +19,20 @@ "java": { "maven": { "groupId": "software.amazon.awscdk", - "artifactId": "cdk-app-staging-synthesizer-alpha" + "artifactId": "cdk-app-staging-synthesizer" }, - "package": "software.amazon.awscdk.app.staging.synthesizer.alpha" + "package": "software.amazon.awscdk.app.staging.synthesizer" }, "python": { - "distName": "aws-cdk.app-staging-synthesizer-alpha", - "module": "aws_cdk.app_staging_synthesizer_alpha", + "distName": "aws-cdk.app-staging-synthesizer", + "module": "aws_cdk.app_staging_synthesizer", "classifiers": [ "Framework :: AWS CDK", "Framework :: AWS CDK :: 2" ] }, "dotnet": { - "namespace": "Amazon.CDK.App.Staging.Synthesizer.Alpha", + "namespace": "Amazon.CDK.App.Staging.Synthesizer", "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/main/logo/default-256-dark.png" } } @@ -40,7 +40,7 @@ "repository": { "type": "git", "url": "https://github.com/aws/aws-cdk.git", - "directory": "packages/@aws-cdk/app-staging-synthesizer-alpha" + "directory": "packages/@aws-cdk/app-staging-synthesizer" }, "scripts": { "build": "cdk-build", diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.assets.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.assets.json deleted file mode 100644 index 1368be8d66072..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.assets.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "version": "30.1.0", - "files": { - "33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c": { - "source": { - "path": "asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c", - "packaging": "zip" - }, - "destinations": { - "current_account-current_region": { - "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c.zip", - "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" - } - } - }, - "cf71e98470e9434a04895f1736e006c3de6a6a9d2f563b2cef88849dacc1e1f6": { - "source": { - "path": "StagingStack.template.json", - "packaging": "file" - }, - "destinations": { - "current_account-current_region": { - "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "cf71e98470e9434a04895f1736e006c3de6a6a9d2f563b2cef88849dacc1e1f6.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/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.template.json deleted file mode 100644 index d6d3fa3f8da78..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack.template.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "Resources": { - "defaultbucket4957B632": { - "Type": "AWS::S3::Bucket", - "Properties": { - "BucketName": "default-bucket", - "Tags": [ - { - "Key": "aws-cdk:auto-delete-objects", - "Value": "true" - } - ] - }, - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, - "defaultbucketPolicyC116629D": { - "Type": "AWS::S3::BucketPolicy", - "Properties": { - "Bucket": { - "Ref": "defaultbucket4957B632" - }, - "PolicyDocument": { - "Statement": [ - { - "Action": [ - "s3:DeleteObject*", - "s3:GetBucket*", - "s3:List*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", - "Arn" - ] - } - }, - "Resource": [ - { - "Fn::GetAtt": [ - "defaultbucket4957B632", - "Arn" - ] - }, - { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "defaultbucket4957B632", - "Arn" - ] - }, - "/*" - ] - ] - } - ] - } - ], - "Version": "2012-10-17" - } - } - }, - "defaultbucketAutoDeleteObjectsCustomResource08E22CE8": { - "Type": "Custom::S3AutoDeleteObjects", - "Properties": { - "ServiceToken": { - "Fn::GetAtt": [ - "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", - "Arn" - ] - }, - "BucketName": { - "Ref": "defaultbucket4957B632" - } - }, - "DependsOn": [ - "defaultbucketPolicyC116629D" - ], - "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": "33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c.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": "defaultbucket4957B632" - }, - " S3 bucket." - ] - ] - } - }, - "DependsOn": [ - "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092" - ] - }, - "cdkQualifierfilepublishingroleAWSAccountIdAWSRegionE5E2CED6": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "sts.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - }, - "RoleName": "cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}" - } - } - }, - "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/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack489318732371us-east-2.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack489318732371us-east-2.template.json new file mode 100644 index 0000000000000..b7f5290d73733 --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack489318732371us-east-2.template.json @@ -0,0 +1,169 @@ +{ + "Resources": { + "CdkFilePublishingRole119E2944": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::489318732371:root" + } + } + ], + "Version": "2012-10-17" + }, + "RoleName": "cdk-file-publishing-role-us-east-2-app-id" + } + }, + "CdkFilePublishingRoleDefaultPolicyE914275E": { + "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": [ + "CdkStagingBucket1636058C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "CdkStagingBucket1636058C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "kms:Decrypt", + "kms:DescribeKey", + "kms:Encrypt", + "kms:GenerateDataKey*", + "kms:ReEncrypt*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "BucketKey7092080A", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "CdkFilePublishingRoleDefaultPolicyE914275E", + "Roles": [ + { + "Ref": "CdkFilePublishingRole119E2944" + } + ] + } + }, + "BucketKey7092080A": { + "Type": "AWS::KMS::Key", + "Properties": { + "KeyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::489318732371:root" + }, + "Resource": "*" + }, + { + "Action": [ + "kms:CancelKeyDeletion", + "kms:Create*", + "kms:Delete*", + "kms:Describe*", + "kms:Disable*", + "kms:Enable*", + "kms:Get*", + "kms:List*", + "kms:Put*", + "kms:Revoke*", + "kms:ScheduleKeyDeletion", + "kms:TagResource", + "kms:UntagResource", + "kms:Update*" + ], + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::489318732371:root" + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "BucketKeyAlias69A0886F": { + "Type": "AWS::KMS::Alias", + "Properties": { + "AliasName": "alias/CdkStagingBucketKey489318732371-us-east-2-app-id", + "TargetKeyId": { + "Fn::GetAtt": [ + "BucketKey7092080A", + "Arn" + ] + } + } + }, + "CdkStagingBucket1636058C": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "KMSMasterKeyID": { + "Fn::GetAtt": [ + "BucketKey7092080A", + "Arn" + ] + }, + "SSEAlgorithm": "aws:kms" + } + } + ] + }, + "BucketName": "cdk-489318732371-us-east-2-app-id" + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json index df9572539a3ab..c365ad9ea7749 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json @@ -1,29 +1,31 @@ { - "version": "30.1.0", + "version": "31.0.0", "files": { - "abcdef": { + "47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53": { "source": { - "path": "/Users/conroyka/Desktop/aws-cdk-kaizen/kaizen-aws-ACTUAL/aws-cdk/packages/@aws-cdk/core-synthesizer/test/integ.synthesizer.js", - "packaging": "file" + "path": "asset.47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53", + "packaging": "zip" }, "destinations": { - "current_account-current_region": { - "bucketName": "default-bucket", - "objectKey": "abcdef.js", - "assumeRoleArn": "arn:${Token[AWS.Partition.9]}:iam:${Token[AWS.Region.10]}:${Token[AWS.AccountId.6]}:role:cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + "489318732371-us-east-2": { + "bucketName": "cdk-489318732371-us-east-2-app-id", + "objectKey": "47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53.zip", + "region": "us-east-2", + "assumeRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-file-publishing-role-us-east-2-app-id" } } }, - "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a": { + "041f053398069413175306d7e2e5dd7fe62b1f288bf8efb64288eefbdcb23d8a": { "source": { "path": "app-scoped-staging-test.template.json", "packaging": "file" }, "destinations": { - "current_account-current_region": { - "bucketName": "default-bucket", - "objectKey": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a.json", - "assumeRoleArn": "arn:${Token[AWS.Partition.9]}:iam:${Token[AWS.Region.10]}:${Token[AWS.AccountId.6]}:role:cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + "489318732371-us-east-2": { + "bucketName": "cdk-489318732371-us-east-2-app-id", + "objectKey": "041f053398069413175306d7e2e5dd7fe62b1f288bf8efb64288eefbdcb23d8a.json", + "region": "us-east-2", + "assumeRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-file-publishing-role-us-east-2-app-id" } } } diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json index 9e26dfeeb6e64..3a94d29acfed8 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json @@ -1 +1,55 @@ -{} \ No newline at end of file +{ + "Resources": { + "lambdaServiceRole494E4CA6": { + "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" + ] + ] + } + ] + } + }, + "lambda8B5974B5": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "cdk-489318732371-us-east-2-app-id", + "S3Key": "47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53.zip" + }, + "Role": { + "Fn::GetAtt": [ + "lambdaServiceRole494E4CA6", + "Arn" + ] + }, + "Handler": "index.handler", + "Runtime": "python3.9" + }, + "DependsOn": [ + "lambdaServiceRole494E4CA6" + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js deleted file mode 100644 index 1e3a3093c1706..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/__entrypoint__.js +++ /dev/null @@ -1,144 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.withRetries = 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 }, - }; - const retryOptions = { - attempts: 5, - sleep: 1000, - }; - await withRetries(retryOptions, 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); -} -function withRetries(options, fn) { - return async (...xs) => { - let attempts = options.attempts; - let ms = options.sleep; - while (true) { - try { - return await fn(...xs); - } - catch (e) { - if (attempts-- <= 0) { - throw e; - } - await sleep(Math.floor(Math.random() * ms)); - ms *= 2; - } - } - }; -} -exports.withRetries = withRetries; -async function sleep(ms) { - return new Promise((ok) => setTimeout(ok, ms)); -} -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9kZWpzLWVudHJ5cG9pbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJub2RlanMtZW50cnlwb2ludC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwrQkFBK0I7QUFDL0IsMkJBQTJCO0FBRTNCLGlCQUFpQjtBQUNKLFFBQUEsUUFBUSxHQUFHO0lBQ3RCLGVBQWUsRUFBRSxzQkFBc0I7SUFDdkMsR0FBRyxFQUFFLFVBQVU7SUFDZixrQkFBa0IsRUFBRSxJQUFJO0lBQ3hCLGdCQUFnQixFQUFFLFNBQVM7Q0FDNUIsQ0FBQztBQUVGLE1BQU0sZ0NBQWdDLEdBQUcsd0RBQXdELENBQUM7QUFDbEcsTUFBTSwwQkFBMEIsR0FBRyw4REFBOEQsQ0FBQztBQVczRixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtELEVBQUUsT0FBMEI7SUFDMUcsTUFBTSxjQUFjLEdBQUcsRUFBRSxHQUFHLEtBQUssRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLENBQUM7SUFDeEQsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxjQUFjLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFM0QsdUVBQXVFO0lBQ3ZFLHVFQUF1RTtJQUN2RSxhQUFhO0lBQ2IsSUFBSSxLQUFLLENBQUMsV0FBVyxLQUFLLFFBQVEsSUFBSSxLQUFLLENBQUMsa0JBQWtCLEtBQUssZ0NBQWdDLEVBQUU7UUFDbkcsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsdURBQXVELENBQUMsQ0FBQztRQUN0RSxNQUFNLGNBQWMsQ0FBQyxTQUFTLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDdkMsT0FBTztLQUNSO0lBRUQsSUFBSTtRQUNGLHlFQUF5RTtRQUN6RSxpRUFBaUU7UUFDakUsd0NBQXdDO1FBQ3hDLGlFQUFpRTtRQUNqRSxNQUFNLFdBQVcsR0FBWSxPQUFPLENBQUMsZ0JBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLE9BQU8sQ0FBQztRQUN4RSxNQUFNLE1BQU0sR0FBRyxNQUFNLFdBQVcsQ0FBQyxjQUFjLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFFMUQsdURBQXVEO1FBQ3ZELE1BQU0sYUFBYSxHQUFHLGNBQWMsQ0FBQyxLQUFLLEVBQUUsTUFBTSxDQUFDLENBQUM7UUFFcEQsMkJBQTJCO1FBQzNCLE1BQU0sY0FBYyxDQUFDLFNBQVMsRUFBRSxhQUFhLENBQUMsQ0FBQztLQUNoRDtJQUFDLE9BQU8sQ0FBQyxFQUFFO1FBQ1YsTUFBTSxJQUFJLEdBQWE7WUFDckIsR0FBRyxLQUFLO1lBQ1IsTUFBTSxFQUFFLGdCQUFRLENBQUMsa0JBQWtCLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPO1NBQzFELENBQUM7UUFFRixJQUFJLENBQUMsSUFBSSxDQUFDLGtCQUFrQixFQUFFO1lBQzVCLHlFQUF5RTtZQUN6RSxtRUFBbUU7WUFDbkUsd0VBQXdFO1lBQ3hFLHFFQUFxRTtZQUNyRSxnQ0FBZ0M7WUFDaEMsSUFBSSxLQUFLLENBQUMsV0FBVyxLQUFLLFFBQVEsRUFBRTtnQkFDbEMsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsNEdBQTRHLENBQUMsQ0FBQztnQkFDM0gsSUFBSSxDQUFDLGtCQUFrQixHQUFHLGdDQUFnQyxDQUFDO2FBQzVEO2lCQUFNO2dCQUNMLGtFQUFrRTtnQkFDbEUsNkRBQTZEO2dCQUM3RCxnQkFBUSxDQUFDLEdBQUcsQ0FBQyw2REFBNkQsSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUM7YUFDcEc7U0FDRjtRQUVELG1FQUFtRTtRQUNuRSxNQUFNLGNBQWMsQ0FBQyxRQUFRLEVBQUUsSUFBSSxDQUFDLENBQUM7S0FDdEM7QUFDSCxDQUFDO0FBbkRELDBCQW1EQztBQUVELFNBQVMsY0FBYyxDQUNyQixVQUF5RixFQUN6RixrQkFBMEMsRUFBRztJQUU3QyxzRUFBc0U7SUFDdEUsdUJBQXVCO0lBQ3ZCLE1BQU0sa0JBQWtCLEdBQUcsZUFBZSxDQUFDLGtCQUFrQixJQUFJLFVBQVUsQ0FBQyxrQkFBa0IsSUFBSSxVQUFVLENBQUMsU0FBUyxDQUFDO0lBRXZILGtFQUFrRTtJQUNsRSxJQUFJLFVBQVUsQ0FBQyxXQUFXLEtBQUssUUFBUSxJQUFJLGtCQUFrQixLQUFLLFVBQVUsQ0FBQyxrQkFBa0IsRUFBRTtRQUMvRixNQUFNLElBQUksS0FBSyxDQUFDLHdEQUF3RCxVQUFVLENBQUMsa0JBQWtCLFNBQVMsZUFBZSxDQUFDLGtCQUFrQixtQkFBbUIsQ0FBQyxDQUFDO0tBQ3RLO0lBRUQsMERBQTBEO0lBQzFELE9BQU87UUFDTCxHQUFHLFVBQVU7UUFDYixHQUFHLGVBQWU7UUFDbEIsa0JBQWtCLEVBQUUsa0JBQWtCO0tBQ3ZDLENBQUM7QUFDSixDQUFDO0FBRUQsS0FBSyxVQUFVLGNBQWMsQ0FBQyxNQUE0QixFQUFFLEtBQWU7SUFDekUsTUFBTSxJQUFJLEdBQW1EO1FBQzNELE1BQU0sRUFBRSxNQUFNO1FBQ2QsTUFBTSxFQUFFLEtBQUssQ0FBQyxNQUFNLElBQUksTUFBTTtRQUM5QixPQUFPLEVBQUUsS0FBSyxDQUFDLE9BQU87UUFDdEIsU0FBUyxFQUFFLEtBQUssQ0FBQyxTQUFTO1FBQzFCLGtCQUFrQixFQUFFLEtBQUssQ0FBQyxrQkFBa0IsSUFBSSwwQkFBMEI7UUFDMUUsaUJBQWlCLEVBQUUsS0FBSyxDQUFDLGlCQUFpQjtRQUMxQyxNQUFNLEVBQUUsS0FBSyxDQUFDLE1BQU07UUFDcEIsSUFBSSxFQUFFLEtBQUssQ0FBQyxJQUFJO0tBQ2pCLENBQUM7SUFFRixnQkFBUSxDQUFDLEdBQUcsQ0FBQyxtQ0FBbUMsRUFBRSxJQUFJLENBQUMsQ0FBQztJQUV4RCxNQUFNLFlBQVksR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQzFDLE1BQU0sU0FBUyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLFdBQVcsQ0FBQyxDQUFDO0lBQy9DLE1BQU0sR0FBRyxHQUFHO1FBQ1YsUUFBUSxFQUFFLFNBQVMsQ0FBQyxRQUFRO1FBQzVCLElBQUksRUFBRSxTQUFTLENBQUMsSUFBSTtRQUNwQixNQUFNLEVBQUUsS0FBSztRQUNiLE9BQU8sRUFBRSxFQUFFLGNBQWMsRUFBRSxFQUFFLEVBQUUsZ0JBQWdCLEVBQUUsWUFBWSxDQUFDLE1BQU0sRUFBRTtLQUN2RSxDQUFDO0lBRUYsTUFBTSxZQUFZLEdBQUc7UUFDbkIsUUFBUSxFQUFFLENBQUM7UUFDWCxLQUFLLEVBQUUsSUFBSTtLQUNaLENBQUM7SUFDRixNQUFNLFdBQVcsQ0FBQyxZQUFZLEVBQUUsZ0JBQVEsQ0FBQyxlQUFlLENBQUMsQ0FBQyxHQUFHLEVBQUUsWUFBWSxDQUFDLENBQUM7QUFDL0UsQ0FBQztBQUVELEtBQUssVUFBVSxzQkFBc0IsQ0FBQyxPQUE2QixFQUFFLFlBQW9CO0lBQ3ZGLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEVBQUU7UUFDckMsSUFBSTtZQUNGLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQztZQUN2RCxPQUFPLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQztZQUM1QixPQUFPLENBQUMsS0FBSyxDQUFDLFlBQVksQ0FBQyxDQUFDO1lBQzVCLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQztTQUNmO1FBQUMsT0FBTyxDQUFDLEVBQUU7WUFDVixNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7U0FDWDtJQUNILENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVELFNBQVMsVUFBVSxDQUFDLEdBQVcsRUFBRSxHQUFHLE1BQWE7SUFDL0Msc0NBQXNDO0lBQ3RDLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsTUFBTSxDQUFDLENBQUM7QUFDOUIsQ0FBQztBQVNELFNBQWdCLFdBQVcsQ0FBMEIsT0FBcUIsRUFBRSxFQUE0QjtJQUN0RyxPQUFPLEtBQUssRUFBRSxHQUFHLEVBQUssRUFBRSxFQUFFO1FBQ3hCLElBQUksUUFBUSxHQUFHLE9BQU8sQ0FBQyxRQUFRLENBQUM7UUFDaEMsSUFBSSxFQUFFLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQztRQUN2QixPQUFPLElBQUksRUFBRTtZQUNYLElBQUk7Z0JBQ0YsT0FBTyxNQUFNLEVBQUUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDO2FBQ3hCO1lBQUMsT0FBTyxDQUFDLEVBQUU7Z0JBQ1YsSUFBSSxRQUFRLEVBQUUsSUFBSSxDQUFDLEVBQUU7b0JBQ25CLE1BQU0sQ0FBQyxDQUFDO2lCQUNUO2dCQUNELE1BQU0sS0FBSyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUFDLENBQUM7Z0JBQzVDLEVBQUUsSUFBSSxDQUFDLENBQUM7YUFDVDtTQUNGO0lBQ0gsQ0FBQyxDQUFDO0FBQ0osQ0FBQztBQWhCRCxrQ0FnQkM7QUFFRCxLQUFLLFVBQVUsS0FBSyxDQUFDLEVBQVU7SUFDN0IsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsVUFBVSxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDO0FBQ2pELENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBodHRwcyBmcm9tICdodHRwcyc7XG5pbXBvcnQgKiBhcyB1cmwgZnJvbSAndXJsJztcblxuLy8gZm9yIHVuaXQgdGVzdHNcbmV4cG9ydCBjb25zdCBleHRlcm5hbCA9IHtcbiAgc2VuZEh0dHBSZXF1ZXN0OiBkZWZhdWx0U2VuZEh0dHBSZXF1ZXN0LFxuICBsb2c6IGRlZmF1bHRMb2csXG4gIGluY2x1ZGVTdGFja1RyYWNlczogdHJ1ZSxcbiAgdXNlckhhbmRsZXJJbmRleDogJy4vaW5kZXgnLFxufTtcblxuY29uc3QgQ1JFQVRFX0ZBSUxFRF9QSFlTSUNBTF9JRF9NQVJLRVIgPSAnQVdTQ0RLOjpDdXN0b21SZXNvdXJjZVByb3ZpZGVyRnJhbWV3b3JrOjpDUkVBVEVfRkFJTEVEJztcbmNvbnN0IE1JU1NJTkdfUEhZU0lDQUxfSURfTUFSS0VSID0gJ0FXU0NESzo6Q3VzdG9tUmVzb3VyY2VQcm92aWRlckZyYW1ld29yazo6TUlTU0lOR19QSFlTSUNBTF9JRCc7XG5cbmV4cG9ydCB0eXBlIFJlc3BvbnNlID0gQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCAmIEhhbmRsZXJSZXNwb25zZTtcbmV4cG9ydCB0eXBlIEhhbmRsZXIgPSAoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQsIGNvbnRleHQ6IEFXU0xhbWJkYS5Db250ZXh0KSA9PiBQcm9taXNlPEhhbmRsZXJSZXNwb25zZSB8IHZvaWQ+O1xuZXhwb3J0IHR5cGUgSGFuZGxlclJlc3BvbnNlID0gdW5kZWZpbmVkIHwge1xuICBEYXRhPzogYW55O1xuICBQaHlzaWNhbFJlc291cmNlSWQ/OiBzdHJpbmc7XG4gIFJlYXNvbj86IHN0cmluZztcbiAgTm9FY2hvPzogYm9vbGVhbjtcbn07XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBoYW5kbGVyKGV2ZW50OiBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50LCBjb250ZXh0OiBBV1NMYW1iZGEuQ29udGV4dCkge1xuICBjb25zdCBzYW5pdGl6ZWRFdmVudCA9IHsgLi4uZXZlbnQsIFJlc3BvbnNlVVJMOiAnLi4uJyB9O1xuICBleHRlcm5hbC5sb2coSlNPTi5zdHJpbmdpZnkoc2FuaXRpemVkRXZlbnQsIHVuZGVmaW5lZCwgMikpO1xuXG4gIC8vIGlnbm9yZSBERUxFVEUgZXZlbnQgd2hlbiB0aGUgcGh5c2ljYWwgcmVzb3VyY2UgSUQgaXMgdGhlIG1hcmtlciB0aGF0XG4gIC8vIGluZGljYXRlcyB0aGF0IHRoaXMgREVMRVRFIGlzIGEgc3Vic2VxdWVudCBERUxFVEUgdG8gYSBmYWlsZWQgQ1JFQVRFXG4gIC8vIG9wZXJhdGlvbi5cbiAgaWYgKGV2ZW50LlJlcXVlc3RUeXBlID09PSAnRGVsZXRlJyAmJiBldmVudC5QaHlzaWNhbFJlc291cmNlSWQgPT09IENSRUFURV9GQUlMRURfUEhZU0lDQUxfSURfTUFSS0VSKSB7XG4gICAgZXh0ZXJuYWwubG9nKCdpZ25vcmluZyBERUxFVEUgZXZlbnQgY2F1c2VkIGJ5IGEgZmFpbGVkIENSRUFURSBldmVudCcpO1xuICAgIGF3YWl0IHN1Ym1pdFJlc3BvbnNlKCdTVUNDRVNTJywgZXZlbnQpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIHRyeSB7XG4gICAgLy8gaW52b2tlIHRoZSB1c2VyIGhhbmRsZXIuIHRoaXMgaXMgaW50ZW50aW9uYWxseSBpbnNpZGUgdGhlIHRyeS1jYXRjaCB0b1xuICAgIC8vIGVuc3VyZSB0aGF0IGlmIHRoZXJlIGlzIGFuIGVycm9yIGl0J3MgcmVwb3J0ZWQgYXMgYSBmYWlsdXJlIHRvXG4gICAgLy8gY2xvdWRmb3JtYXRpb24gKG90aGVyd2lzZSBjZm4gd2FpdHMpLlxuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tcmVxdWlyZS1pbXBvcnRzXG4gICAgY29uc3QgdXNlckhhbmRsZXI6IEhhbmRsZXIgPSByZXF1aXJlKGV4dGVybmFsLnVzZXJIYW5kbGVySW5kZXgpLmhhbmRsZXI7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdXNlckhhbmRsZXIoc2FuaXRpemVkRXZlbnQsIGNvbnRleHQpO1xuXG4gICAgLy8gdmFsaWRhdGUgdXNlciByZXNwb25zZSBhbmQgY3JlYXRlIHRoZSBjb21iaW5lZCBldmVudFxuICAgIGNvbnN0IHJlc3BvbnNlRXZlbnQgPSByZW5kZXJSZXNwb25zZShldmVudCwgcmVzdWx0KTtcblxuICAgIC8vIHN1Ym1pdCB0byBjZm4gYXMgc3VjY2Vzc1xuICAgIGF3YWl0IHN1Ym1pdFJlc3BvbnNlKCdTVUNDRVNTJywgcmVzcG9uc2VFdmVudCk7XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICBjb25zdCByZXNwOiBSZXNwb25zZSA9IHtcbiAgICAgIC4uLmV2ZW50LFxuICAgICAgUmVhc29uOiBleHRlcm5hbC5pbmNsdWRlU3RhY2tUcmFjZXMgPyBlLnN0YWNrIDogZS5tZXNzYWdlLFxuICAgIH07XG5cbiAgICBpZiAoIXJlc3AuUGh5c2ljYWxSZXNvdXJjZUlkKSB7XG4gICAgICAvLyBzcGVjaWFsIGNhc2U6IGlmIENSRUFURSBmYWlscywgd2hpY2ggdXN1YWxseSBpbXBsaWVzLCB3ZSB1c3VhbGx5IGRvbid0XG4gICAgICAvLyBoYXZlIGEgcGh5c2ljYWwgcmVzb3VyY2UgaWQuIGluIHRoaXMgY2FzZSwgdGhlIHN1YnNlcXVlbnQgREVMRVRFXG4gICAgICAvLyBvcGVyYXRpb24gZG9lcyBub3QgaGF2ZSBhbnkgbWVhbmluZywgYW5kIHdpbGwgbGlrZWx5IGZhaWwgYXMgd2VsbC4gdG9cbiAgICAgIC8vIGFkZHJlc3MgdGhpcywgd2UgdXNlIGEgbWFya2VyIHNvIHRoZSBwcm92aWRlciBmcmFtZXdvcmsgY2FuIHNpbXBseVxuICAgICAgLy8gaWdub3JlIHRoZSBzdWJzZXF1ZW50IERFTEVURS5cbiAgICAgIGlmIChldmVudC5SZXF1ZXN0VHlwZSA9PT0gJ0NyZWF0ZScpIHtcbiAgICAgICAgZXh0ZXJuYWwubG9nKCdDUkVBVEUgZmFpbGVkLCByZXNwb25kaW5nIHdpdGggYSBtYXJrZXIgcGh5c2ljYWwgcmVzb3VyY2UgaWQgc28gdGhhdCB0aGUgc3Vic2VxdWVudCBERUxFVEUgd2lsbCBiZSBpZ25vcmVkJyk7XG4gICAgICAgIHJlc3AuUGh5c2ljYWxSZXNvdXJjZUlkID0gQ1JFQVRFX0ZBSUxFRF9QSFlTSUNBTF9JRF9NQVJLRVI7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBvdGhlcndpc2UsIGlmIFBoeXNpY2FsUmVzb3VyY2VJZCBpcyBub3Qgc3BlY2lmaWVkLCBzb21ldGhpbmcgaXNcbiAgICAgICAgLy8gdGVycmlibHkgd3JvbmcgYmVjYXVzZSBhbGwgb3RoZXIgZXZlbnRzIHNob3VsZCBoYXZlIGFuIElELlxuICAgICAgICBleHRlcm5hbC5sb2coYEVSUk9SOiBNYWxmb3JtZWQgZXZlbnQuIFwiUGh5c2ljYWxSZXNvdXJjZUlkXCIgaXMgcmVxdWlyZWQ6ICR7SlNPTi5zdHJpbmdpZnkoZXZlbnQpfWApO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIHRoaXMgaXMgYW4gYWN0dWFsIGVycm9yLCBmYWlsIHRoZSBhY3Rpdml0eSBhbHRvZ2V0aGVyIGFuZCBleGlzdC5cbiAgICBhd2FpdCBzdWJtaXRSZXNwb25zZSgnRkFJTEVEJywgcmVzcCk7XG4gIH1cbn1cblxuZnVuY3Rpb24gcmVuZGVyUmVzcG9uc2UoXG4gIGNmblJlcXVlc3Q6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQgJiB7IFBoeXNpY2FsUmVzb3VyY2VJZD86IHN0cmluZyB9LFxuICBoYW5kbGVyUmVzcG9uc2U6IHZvaWQgfCBIYW5kbGVyUmVzcG9uc2UgPSB7IH0pOiBSZXNwb25zZSB7XG5cbiAgLy8gaWYgcGh5c2ljYWwgSUQgaXMgbm90IHJldHVybmVkLCB3ZSBoYXZlIHNvbWUgZGVmYXVsdHMgZm9yIHlvdSBiYXNlZFxuICAvLyBvbiB0aGUgcmVxdWVzdCB0eXBlLlxuICBjb25zdCBwaHlzaWNhbFJlc291cmNlSWQgPSBoYW5kbGVyUmVzcG9uc2UuUGh5c2ljYWxSZXNvdXJjZUlkID8/IGNmblJlcXVlc3QuUGh5c2ljYWxSZXNvdXJjZUlkID8/IGNmblJlcXVlc3QuUmVxdWVzdElkO1xuXG4gIC8vIGlmIHdlIGFyZSBpbiBERUxFVEUgYW5kIHBoeXNpY2FsIElEIHdhcyBjaGFuZ2VkLCBpdCdzIGFuIGVycm9yLlxuICBpZiAoY2ZuUmVxdWVzdC5SZXF1ZXN0VHlwZSA9PT0gJ0RlbGV0ZScgJiYgcGh5c2ljYWxSZXNvdXJjZUlkICE9PSBjZm5SZXF1ZXN0LlBoeXNpY2FsUmVzb3VyY2VJZCkge1xuICAgIHRocm93IG5ldyBFcnJvcihgREVMRVRFOiBjYW5ub3QgY2hhbmdlIHRoZSBwaHlzaWNhbCByZXNvdXJjZSBJRCBmcm9tIFwiJHtjZm5SZXF1ZXN0LlBoeXNpY2FsUmVzb3VyY2VJZH1cIiB0byBcIiR7aGFuZGxlclJlc3BvbnNlLlBoeXNpY2FsUmVzb3VyY2VJZH1cIiBkdXJpbmcgZGVsZXRpb25gKTtcbiAgfVxuXG4gIC8vIG1lcmdlIHJlcXVlc3QgZXZlbnQgYW5kIHJlc3VsdCBldmVudCAocmVzdWx0IHByZXZhaWxzKS5cbiAgcmV0dXJuIHtcbiAgICAuLi5jZm5SZXF1ZXN0LFxuICAgIC4uLmhhbmRsZXJSZXNwb25zZSxcbiAgICBQaHlzaWNhbFJlc291cmNlSWQ6IHBoeXNpY2FsUmVzb3VyY2VJZCxcbiAgfTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gc3VibWl0UmVzcG9uc2Uoc3RhdHVzOiAnU1VDQ0VTUycgfCAnRkFJTEVEJywgZXZlbnQ6IFJlc3BvbnNlKSB7XG4gIGNvbnN0IGpzb246IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlUmVzcG9uc2UgPSB7XG4gICAgU3RhdHVzOiBzdGF0dXMsXG4gICAgUmVhc29uOiBldmVudC5SZWFzb24gPz8gc3RhdHVzLFxuICAgIFN0YWNrSWQ6IGV2ZW50LlN0YWNrSWQsXG4gICAgUmVxdWVzdElkOiBldmVudC5SZXF1ZXN0SWQsXG4gICAgUGh5c2ljYWxSZXNvdXJjZUlkOiBldmVudC5QaHlzaWNhbFJlc291cmNlSWQgfHwgTUlTU0lOR19QSFlTSUNBTF9JRF9NQVJLRVIsXG4gICAgTG9naWNhbFJlc291cmNlSWQ6IGV2ZW50LkxvZ2ljYWxSZXNvdXJjZUlkLFxuICAgIE5vRWNobzogZXZlbnQuTm9FY2hvLFxuICAgIERhdGE6IGV2ZW50LkRhdGEsXG4gIH07XG5cbiAgZXh0ZXJuYWwubG9nKCdzdWJtaXQgcmVzcG9uc2UgdG8gY2xvdWRmb3JtYXRpb24nLCBqc29uKTtcblxuICBjb25zdCByZXNwb25zZUJvZHkgPSBKU09OLnN0cmluZ2lmeShqc29uKTtcbiAgY29uc3QgcGFyc2VkVXJsID0gdXJsLnBhcnNlKGV2ZW50LlJlc3BvbnNlVVJMKTtcbiAgY29uc3QgcmVxID0ge1xuICAgIGhvc3RuYW1lOiBwYXJzZWRVcmwuaG9zdG5hbWUsXG4gICAgcGF0aDogcGFyc2VkVXJsLnBhdGgsXG4gICAgbWV0aG9kOiAnUFVUJyxcbiAgICBoZWFkZXJzOiB7ICdjb250ZW50LXR5cGUnOiAnJywgJ2NvbnRlbnQtbGVuZ3RoJzogcmVzcG9uc2VCb2R5Lmxlbmd0aCB9LFxuICB9O1xuXG4gIGNvbnN0IHJldHJ5T3B0aW9ucyA9IHtcbiAgICBhdHRlbXB0czogNSxcbiAgICBzbGVlcDogMTAwMCxcbiAgfTtcbiAgYXdhaXQgd2l0aFJldHJpZXMocmV0cnlPcHRpb25zLCBleHRlcm5hbC5zZW5kSHR0cFJlcXVlc3QpKHJlcSwgcmVzcG9uc2VCb2R5KTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZGVmYXVsdFNlbmRIdHRwUmVxdWVzdChvcHRpb25zOiBodHRwcy5SZXF1ZXN0T3B0aW9ucywgcmVzcG9uc2VCb2R5OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVxdWVzdCA9IGh0dHBzLnJlcXVlc3Qob3B0aW9ucywgXyA9PiByZXNvbHZlKCkpO1xuICAgICAgcmVxdWVzdC5vbignZXJyb3InLCByZWplY3QpO1xuICAgICAgcmVxdWVzdC53cml0ZShyZXNwb25zZUJvZHkpO1xuICAgICAgcmVxdWVzdC5lbmQoKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICByZWplY3QoZSk7XG4gICAgfVxuICB9KTtcbn1cblxuZnVuY3Rpb24gZGVmYXVsdExvZyhmbXQ6IHN0cmluZywgLi4ucGFyYW1zOiBhbnlbXSkge1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZVxuICBjb25zb2xlLmxvZyhmbXQsIC4uLnBhcmFtcyk7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUmV0cnlPcHRpb25zIHtcbiAgLyoqIEhvdyBtYW55IHJldHJpZXMgKHdpbGwgYXQgbGVhc3QgdHJ5IG9uY2UpICovXG4gIHJlYWRvbmx5IGF0dGVtcHRzOiBudW1iZXI7XG4gIC8qKiBTbGVlcCBiYXNlLCBpbiBtcyAqL1xuICByZWFkb25seSBzbGVlcDogbnVtYmVyO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gd2l0aFJldHJpZXM8QSBleHRlbmRzIEFycmF5PGFueT4sIEI+KG9wdGlvbnM6IFJldHJ5T3B0aW9ucywgZm46ICguLi54czogQSkgPT4gUHJvbWlzZTxCPik6ICguLi54czogQSkgPT4gUHJvbWlzZTxCPiB7XG4gIHJldHVybiBhc3luYyAoLi4ueHM6IEEpID0+IHtcbiAgICBsZXQgYXR0ZW1wdHMgPSBvcHRpb25zLmF0dGVtcHRzO1xuICAgIGxldCBtcyA9IG9wdGlvbnMuc2xlZXA7XG4gICAgd2hpbGUgKHRydWUpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIHJldHVybiBhd2FpdCBmbiguLi54cyk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGlmIChhdHRlbXB0cy0tIDw9IDApIHtcbiAgICAgICAgICB0aHJvdyBlO1xuICAgICAgICB9XG4gICAgICAgIGF3YWl0IHNsZWVwKE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIG1zKSk7XG4gICAgICAgIG1zICo9IDI7XG4gICAgICB9XG4gICAgfVxuICB9O1xufVxuXG5hc3luYyBmdW5jdGlvbiBzbGVlcChtczogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gIHJldHVybiBuZXcgUHJvbWlzZSgob2spID0+IHNldFRpbWVvdXQob2ssIG1zKSk7XG59Il19 \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js deleted file mode 100644 index 7ce4156d4ba41..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c/index.js +++ /dev/null @@ -1,78 +0,0 @@ -"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/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53/index.py b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53/index.py new file mode 100644 index 0000000000000..ed0f110e2e61e --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53/index.py @@ -0,0 +1 @@ +print('hello') \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/cdk.out b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/cdk.out index b72fef144f05c..7925065efbcc4 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/cdk.out +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/cdk.out @@ -1 +1 @@ -{"version":"30.1.0"} \ No newline at end of file +{"version":"31.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integ.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integ.json index 54178327802a7..868928baf7ea2 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integ.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integ.json @@ -1,12 +1,12 @@ { - "version": "30.1.0", + "version": "31.0.0", "testCases": { - "synthesizer-integ/DefaultTest": { + "integ-tests/DefaultTest": { "stacks": [ "app-scoped-staging-test" ], - "assertionStack": "synthesizer-integ/DefaultTest/DeployAssert", - "assertionStackName": "synthesizerintegDefaultTestDeployAssert208D3414" + "assertionStack": "integ-tests/DefaultTest/DeployAssert", + "assertionStackName": "integtestsDefaultTestDeployAssert44C8D370" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json similarity index 84% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json index 6a530e5336c5d..7526fee9ff76c 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.assets.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json @@ -1,9 +1,9 @@ { - "version": "30.1.0", + "version": "31.0.0", "files": { "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { "source": { - "path": "synthesizerintegDefaultTestDeployAssert208D3414.template.json", + "path": "integtestsDefaultTestDeployAssert44C8D370.template.json", "packaging": "file" }, "destinations": { diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.template.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synthesizerintegDefaultTestDeployAssert208D3414.template.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.template.json diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json index 5e4c80d2f3e33..215455b8b4654 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "30.1.0", + "version": "31.0.0", "artifacts": { "app-scoped-staging-test.assets": { "type": "cdk:asset-manifest", @@ -9,116 +9,97 @@ }, "app-scoped-staging-test": { "type": "aws:cloudformation:stack", - "environment": "aws://unknown-account/unknown-region", + "environment": "aws://489318732371/us-east-2", "properties": { "templateFile": "app-scoped-staging-test.template.json", "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-deploy-role-${AWS::AccountId}-${AWS::Region}", "additionalDependencies": [ "app-scoped-staging-test.assets" ], - "stackTemplateAssetObjectUrl": "s3://default-bucket/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a.json" - }, - "dependencies": [ - "app-scoped-staging-test.assets" - ], - "displayName": "app-scoped-staging-test" - }, - "StagingStack.assets": { - "type": "cdk:asset-manifest", - "properties": { - "file": "StagingStack.assets.json", - "requiresBootstrapStackVersion": 6, - "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" - } - }, - "StagingStack": { - "type": "aws:cloudformation:stack", - "environment": "aws://unknown-account/unknown-region", - "properties": { - "templateFile": "StagingStack.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}/cf71e98470e9434a04895f1736e006c3de6a6a9d2f563b2cef88849dacc1e1f6.json", - "requiresBootstrapStackVersion": 6, - "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", - "additionalDependencies": [ - "StagingStack.assets" - ], + "stackTemplateAssetObjectUrl": "s3://cdk-489318732371-us-east-2-app-id/041f053398069413175306d7e2e5dd7fe62b1f288bf8efb64288eefbdcb23d8a.json", + "cloudFormationExecutionRoleArn": "arn:aws:iam::489318732371:role/cdk-hnb659fds-cfn-exec-role-489318732371-us-east-2", "lookupRole": { - "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", - "requiresBootstrapStackVersion": 8, - "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-lookup-role-${AWS::AccountId}-${AWS::Region}" } }, "dependencies": [ - "StagingStack.assets" + "StagingStack489318732371us-east-2", + "app-scoped-staging-test.assets" ], "metadata": { - "/StagingStack/default-bucket/Resource": [ + "/app-scoped-staging-test/lambda/ServiceRole/Resource": [ { "type": "aws:cdk:logicalId", - "data": "defaultbucket4957B632" + "data": "lambdaServiceRole494E4CA6" } ], - "/StagingStack/default-bucket/Policy/Resource": [ + "/app-scoped-staging-test/lambda/Resource": [ { "type": "aws:cdk:logicalId", - "data": "defaultbucketPolicyC116629D" + "data": "lambda8B5974B5" } - ], - "/StagingStack/default-bucket/AutoDeleteObjectsCustomResource/Default": [ - { - "type": "aws:cdk:logicalId", - "data": "defaultbucketAutoDeleteObjectsCustomResource08E22CE8" - } - ], - "/StagingStack/Custom::S3AutoDeleteObjectsCustomResourceProvider/Role": [ + ] + }, + "displayName": "app-scoped-staging-test" + }, + "StagingStack489318732371us-east-2": { + "type": "aws:cloudformation:stack", + "environment": "aws://489318732371/us-east-2", + "properties": { + "templateFile": "StagingStack489318732371us-east-2.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-hnb659fds-deploy-role-489318732371-us-east-2", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-hnb659fds-cfn-exec-role-489318732371-us-east-2", + "stackName": "StagingStackapp-id" + }, + "metadata": { + "/StagingStack489318732371us-east-2/CdkFilePublishingRole/Resource": [ { "type": "aws:cdk:logicalId", - "data": "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092" + "data": "CdkFilePublishingRole119E2944" } ], - "/StagingStack/Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler": [ + "/StagingStack489318732371us-east-2/CdkFilePublishingRole/DefaultPolicy/Resource": [ { "type": "aws:cdk:logicalId", - "data": "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F" + "data": "CdkFilePublishingRoleDefaultPolicyE914275E" } ], - "/StagingStack/cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}/Resource": [ + "/StagingStack489318732371us-east-2/BucketKey/Resource": [ { "type": "aws:cdk:logicalId", - "data": "cdkQualifierfilepublishingroleAWSAccountIdAWSRegionE5E2CED6" + "data": "BucketKey7092080A" } ], - "/StagingStack/BootstrapVersion": [ + "/StagingStack489318732371us-east-2/BucketKey/Alias/Resource": [ { "type": "aws:cdk:logicalId", - "data": "BootstrapVersion" + "data": "BucketKeyAlias69A0886F" } ], - "/StagingStack/CheckBootstrapVersion": [ + "/StagingStack489318732371us-east-2/CdkStagingBucket/Resource": [ { "type": "aws:cdk:logicalId", - "data": "CheckBootstrapVersion" + "data": "CdkStagingBucket1636058C" } ] }, - "displayName": "StagingStack" + "displayName": "StagingStack489318732371us-east-2" }, - "synthesizerintegDefaultTestDeployAssert208D3414.assets": { + "integtestsDefaultTestDeployAssert44C8D370.assets": { "type": "cdk:asset-manifest", "properties": { - "file": "synthesizerintegDefaultTestDeployAssert208D3414.assets.json", + "file": "integtestsDefaultTestDeployAssert44C8D370.assets.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" } }, - "synthesizerintegDefaultTestDeployAssert208D3414": { + "integtestsDefaultTestDeployAssert44C8D370": { "type": "aws:cloudformation:stack", "environment": "aws://unknown-account/unknown-region", "properties": { - "templateFile": "synthesizerintegDefaultTestDeployAssert208D3414.template.json", + "templateFile": "integtestsDefaultTestDeployAssert44C8D370.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}", @@ -126,7 +107,7 @@ "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ - "synthesizerintegDefaultTestDeployAssert208D3414.assets" + "integtestsDefaultTestDeployAssert44C8D370.assets" ], "lookupRole": { "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", @@ -135,23 +116,23 @@ } }, "dependencies": [ - "synthesizerintegDefaultTestDeployAssert208D3414.assets" + "integtestsDefaultTestDeployAssert44C8D370.assets" ], "metadata": { - "/synthesizer-integ/DefaultTest/DeployAssert/BootstrapVersion": [ + "/integ-tests/DefaultTest/DeployAssert/BootstrapVersion": [ { "type": "aws:cdk:logicalId", "data": "BootstrapVersion" } ], - "/synthesizer-integ/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + "/integ-tests/DefaultTest/DeployAssert/CheckBootstrapVersion": [ { "type": "aws:cdk:logicalId", "data": "CheckBootstrapVersion" } ] }, - "displayName": "synthesizer-integ/DefaultTest/DeployAssert" + "displayName": "integ-tests/DefaultTest/DeployAssert" }, "Tree": { "type": "cdk:tree", diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json index 50c68898dce48..70d259e7aac44 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json @@ -7,73 +7,203 @@ "app-scoped-staging-test": { "id": "app-scoped-staging-test", "path": "app-scoped-staging-test", + "children": { + "lambda": { + "id": "lambda", + "path": "app-scoped-staging-test/lambda", + "children": { + "ServiceRole": { + "id": "ServiceRole", + "path": "app-scoped-staging-test/lambda/ServiceRole", + "children": { + "ImportServiceRole": { + "id": "ImportServiceRole", + "path": "app-scoped-staging-test/lambda/ServiceRole/ImportServiceRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "app-scoped-staging-test/lambda/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-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Code": { + "id": "Code", + "path": "app-scoped-staging-test/lambda/Code", + "children": { + "Stage": { + "id": "Stage", + "path": "app-scoped-staging-test/lambda/Code/Stage", + "constructInfo": { + "fqn": "aws-cdk-lib.AssetStaging", + "version": "0.0.0" + } + }, + "AssetBucket": { + "id": "AssetBucket", + "path": "app-scoped-staging-test/lambda/Code/AssetBucket", + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.BucketBase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3_assets.Asset", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "app-scoped-staging-test/lambda/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Function", + "aws:cdk:cloudformation:props": { + "code": { + "s3Bucket": "cdk-489318732371-us-east-2-app-id", + "s3Key": "47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53.zip" + }, + "role": { + "Fn::GetAtt": [ + "lambdaServiceRole494E4CA6", + "Arn" + ] + }, + "handler": "index.handler", + "runtime": "python3.9" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.CfnFunction", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.Function", + "version": "0.0.0" + } + } + }, "constructInfo": { - "fqn": "@aws-cdk/core.Stack", + "fqn": "aws-cdk-lib.Stack", "version": "0.0.0" } }, - "StagingStack": { - "id": "StagingStack", - "path": "StagingStack", + "StagingStack489318732371us-east-2": { + "id": "StagingStack489318732371us-east-2", + "path": "StagingStack489318732371us-east-2", "children": { - "default-bucket": { - "id": "default-bucket", - "path": "StagingStack/default-bucket", + "CdkFilePublishingRole": { + "id": "CdkFilePublishingRole", + "path": "StagingStack489318732371us-east-2/CdkFilePublishingRole", "children": { + "ImportCdkFilePublishingRole": { + "id": "ImportCdkFilePublishingRole", + "path": "StagingStack489318732371us-east-2/CdkFilePublishingRole/ImportCdkFilePublishingRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, "Resource": { "id": "Resource", - "path": "StagingStack/default-bucket/Resource", + "path": "StagingStack489318732371us-east-2/CdkFilePublishingRole/Resource", "attributes": { - "aws:cdk:cloudformation:type": "AWS::S3::Bucket", + "aws:cdk:cloudformation:type": "AWS::IAM::Role", "aws:cdk:cloudformation:props": { - "bucketName": "default-bucket", - "tags": [ - { - "key": "aws-cdk:auto-delete-objects", - "value": "true" - } - ] + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::489318732371:root" + } + } + ], + "Version": "2012-10-17" + }, + "roleName": "cdk-file-publishing-role-us-east-2-app-id" } }, "constructInfo": { - "fqn": "@aws-cdk/aws-s3.CfnBucket", + "fqn": "aws-cdk-lib.aws_iam.CfnRole", "version": "0.0.0" } }, - "Policy": { - "id": "Policy", - "path": "StagingStack/default-bucket/Policy", + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "StagingStack489318732371us-east-2/CdkFilePublishingRole/DefaultPolicy", "children": { "Resource": { "id": "Resource", - "path": "StagingStack/default-bucket/Policy/Resource", + "path": "StagingStack489318732371us-east-2/CdkFilePublishingRole/DefaultPolicy/Resource", "attributes": { - "aws:cdk:cloudformation:type": "AWS::S3::BucketPolicy", + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", "aws:cdk:cloudformation:props": { - "bucket": { - "Ref": "defaultbucket4957B632" - }, "policyDocument": { "Statement": [ { "Action": [ + "s3:Abort*", "s3:DeleteObject*", "s3:GetBucket*", - "s3:List*" + "s3:GetObject*", + "s3:List*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging" ], "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", - "Arn" - ] - } - }, "Resource": [ { "Fn::GetAtt": [ - "defaultbucket4957B632", + "CdkStagingBucket1636058C", "Arn" ] }, @@ -83,7 +213,7 @@ [ { "Fn::GetAtt": [ - "defaultbucket4957B632", + "CdkStagingBucket1636058C", "Arn" ] }, @@ -92,198 +222,236 @@ ] } ] + }, + { + "Action": [ + "kms:Decrypt", + "kms:DescribeKey", + "kms:Encrypt", + "kms:GenerateDataKey*", + "kms:ReEncrypt*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "BucketKey7092080A", + "Arn" + ] + } } ], "Version": "2012-10-17" - } + }, + "policyName": "CdkFilePublishingRoleDefaultPolicyE914275E", + "roles": [ + { + "Ref": "CdkFilePublishingRole119E2944" + } + ] } }, "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": "StagingStack/default-bucket/AutoDeleteObjectsCustomResource", - "children": { - "Default": { - "id": "Default", - "path": "StagingStack/default-bucket/AutoDeleteObjectsCustomResource/Default", - "constructInfo": { - "fqn": "@aws-cdk/core.CfnResource", + "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", "version": "0.0.0" } } }, "constructInfo": { - "fqn": "@aws-cdk/core.CustomResource", + "fqn": "aws-cdk-lib.aws_iam.Policy", "version": "0.0.0" } } }, "constructInfo": { - "fqn": "@aws-cdk/aws-s3.Bucket", + "fqn": "aws-cdk-lib.aws_iam.Role", "version": "0.0.0" } }, - "Custom::S3AutoDeleteObjectsCustomResourceProvider": { - "id": "Custom::S3AutoDeleteObjectsCustomResourceProvider", - "path": "StagingStack/Custom::S3AutoDeleteObjectsCustomResourceProvider", + "BucketKey": { + "id": "BucketKey", + "path": "StagingStack489318732371us-east-2/BucketKey", "children": { - "Staging": { - "id": "Staging", - "path": "StagingStack/Custom::S3AutoDeleteObjectsCustomResourceProvider/Staging", - "constructInfo": { - "fqn": "@aws-cdk/core.AssetStaging", - "version": "0.0.0" - } - }, - "Role": { - "id": "Role", - "path": "StagingStack/Custom::S3AutoDeleteObjectsCustomResourceProvider/Role", + "Resource": { + "id": "Resource", + "path": "StagingStack489318732371us-east-2/BucketKey/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::KMS::Key", + "aws:cdk:cloudformation:props": { + "keyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::489318732371:root" + }, + "Resource": "*" + }, + { + "Action": [ + "kms:CancelKeyDeletion", + "kms:Create*", + "kms:Delete*", + "kms:Describe*", + "kms:Disable*", + "kms:Enable*", + "kms:Get*", + "kms:List*", + "kms:Put*", + "kms:Revoke*", + "kms:ScheduleKeyDeletion", + "kms:TagResource", + "kms:UntagResource", + "kms:Update*" + ], + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::489318732371:root" + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + } + }, "constructInfo": { - "fqn": "@aws-cdk/core.CfnResource", + "fqn": "aws-cdk-lib.aws_kms.CfnKey", "version": "0.0.0" } }, - "Handler": { - "id": "Handler", - "path": "StagingStack/Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler", + "Alias": { + "id": "Alias", + "path": "StagingStack489318732371us-east-2/BucketKey/Alias", + "children": { + "Resource": { + "id": "Resource", + "path": "StagingStack489318732371us-east-2/BucketKey/Alias/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::KMS::Alias", + "aws:cdk:cloudformation:props": { + "aliasName": "alias/CdkStagingBucketKey489318732371-us-east-2-app-id", + "targetKeyId": { + "Fn::GetAtt": [ + "BucketKey7092080A", + "Arn" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_kms.CfnAlias", + "version": "0.0.0" + } + } + }, "constructInfo": { - "fqn": "@aws-cdk/core.CfnResource", + "fqn": "aws-cdk-lib.aws_kms.Alias", "version": "0.0.0" } } }, "constructInfo": { - "fqn": "@aws-cdk/core.CustomResourceProvider", + "fqn": "aws-cdk-lib.aws_kms.Key", "version": "0.0.0" } }, - "cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}": { - "id": "cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}", - "path": "StagingStack/cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}", + "CdkStagingBucket": { + "id": "CdkStagingBucket", + "path": "StagingStack489318732371us-east-2/CdkStagingBucket", "children": { - "Importcdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}": { - "id": "Importcdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}", - "path": "StagingStack/cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}/Importcdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}", - "constructInfo": { - "fqn": "@aws-cdk/core.Resource", - "version": "0.0.0" - } - }, "Resource": { "id": "Resource", - "path": "StagingStack/cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}/Resource", + "path": "StagingStack489318732371us-east-2/CdkStagingBucket/Resource", "attributes": { - "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:type": "AWS::S3::Bucket", "aws:cdk:cloudformation:props": { - "assumeRolePolicyDocument": { - "Statement": [ + "bucketEncryption": { + "serverSideEncryptionConfiguration": [ { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "sts.amazonaws.com" + "serverSideEncryptionByDefault": { + "sseAlgorithm": "aws:kms", + "kmsMasterKeyId": { + "Fn::GetAtt": [ + "BucketKey7092080A", + "Arn" + ] + } } } - ], - "Version": "2012-10-17" + ] }, - "roleName": "cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + "bucketName": "cdk-489318732371-us-east-2-app-id" } }, "constructInfo": { - "fqn": "@aws-cdk/aws-iam.CfnRole", + "fqn": "aws-cdk-lib.aws_s3.CfnBucket", "version": "0.0.0" } } }, "constructInfo": { - "fqn": "@aws-cdk/aws-iam.Role", - "version": "0.0.0" - } - }, - "BootstrapVersion": { - "id": "BootstrapVersion", - "path": "StagingStack/BootstrapVersion", - "constructInfo": { - "fqn": "@aws-cdk/core.CfnParameter", - "version": "0.0.0" - } - }, - "CheckBootstrapVersion": { - "id": "CheckBootstrapVersion", - "path": "StagingStack/CheckBootstrapVersion", - "constructInfo": { - "fqn": "@aws-cdk/core.CfnRule", + "fqn": "aws-cdk-lib.aws_s3.Bucket", "version": "0.0.0" } } }, "constructInfo": { - "fqn": "@aws-cdk/core-synthesizer.DefaultStagingStack", + "fqn": "@aws-cdk/app-staging-synthesizer.DefaultStagingStack", "version": "0.0.0" } }, - "synthesizer-integ": { - "id": "synthesizer-integ", - "path": "synthesizer-integ", + "integ-tests": { + "id": "integ-tests", + "path": "integ-tests", "children": { "DefaultTest": { "id": "DefaultTest", - "path": "synthesizer-integ/DefaultTest", + "path": "integ-tests/DefaultTest", "children": { "Default": { "id": "Default", - "path": "synthesizer-integ/DefaultTest/Default", + "path": "integ-tests/DefaultTest/Default", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.264" + "version": "10.1.270" } }, "DeployAssert": { "id": "DeployAssert", - "path": "synthesizer-integ/DefaultTest/DeployAssert", + "path": "integ-tests/DefaultTest/DeployAssert", "children": { "BootstrapVersion": { "id": "BootstrapVersion", - "path": "synthesizer-integ/DefaultTest/DeployAssert/BootstrapVersion", + "path": "integ-tests/DefaultTest/DeployAssert/BootstrapVersion", "constructInfo": { - "fqn": "@aws-cdk/core.CfnParameter", + "fqn": "aws-cdk-lib.CfnParameter", "version": "0.0.0" } }, "CheckBootstrapVersion": { "id": "CheckBootstrapVersion", - "path": "synthesizer-integ/DefaultTest/DeployAssert/CheckBootstrapVersion", + "path": "integ-tests/DefaultTest/DeployAssert/CheckBootstrapVersion", "constructInfo": { - "fqn": "@aws-cdk/core.CfnRule", + "fqn": "aws-cdk-lib.CfnRule", "version": "0.0.0" } } }, "constructInfo": { - "fqn": "@aws-cdk/core.Stack", + "fqn": "aws-cdk-lib.Stack", "version": "0.0.0" } } }, "constructInfo": { - "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", "version": "0.0.0" } } }, "constructInfo": { - "fqn": "@aws-cdk/integ-tests.IntegTest", + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", "version": "0.0.0" } }, @@ -292,12 +460,12 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.264" + "version": "10.1.270" } } }, "constructInfo": { - "fqn": "@aws-cdk/core.App", + "fqn": "aws-cdk-lib.App", "version": "0.0.0" } } diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts index 11cb0f42bf5c4..ab682e6ab7220 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts @@ -1,8 +1,8 @@ import * as path from 'path'; +import * as integ from '@aws-cdk/integ-tests-alpha'; import { App, Stack } from 'aws-cdk-lib'; import * as lambda from 'aws-cdk-lib/aws-lambda'; import { AppStagingSynthesizer, BootstrapRole } from '../lib'; -import * as integ from '@aws-cdk/integ-tests-alpha'; const app = new App(); From 9269b9c3999104d4c9a5e84ce0ad5e822b0186d0 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 6 Apr 2023 16:33:03 -0400 Subject: [PATCH 056/120] resolve qualifier in the synth --- .../lib/app-staging-synthesizer.ts | 25 +++++++++--------- .../lib/default-staging-stack.ts | 5 ++-- .../test/app-staging-synthesizer.test.ts | 24 +++++++++++++++++ ...3a7c0374db287baed4c18c990b4af1444d8d53.zip | Bin 0 -> 146 bytes .../test/integ.synthesizer.ts | 12 +++++---- packages/aws-cdk-lib/package.json | 1 + 6 files changed, 48 insertions(+), 19 deletions(-) create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/.cache/47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53.zip diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts index 7bd0b32f6ed1a..3b1170779beb1 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts @@ -13,6 +13,7 @@ import { StackSynthesizer, Token, } from 'aws-cdk-lib'; +import { StringSpecializer } from 'aws-cdk-lib/core/lib/stack-synthesizers/_shared'; import * as cxapi from 'aws-cdk-lib/cx-api'; import { BootstrapRoles, StagingRoles } from './bootstrap-roles'; import { IStagingStack as IStagingStack, DefaultStagingStack } from './default-staging-stack'; @@ -44,9 +45,6 @@ export interface StackPerEnvProps { /** * Qualifier to disambiguate multiple environments in the same account * - * You can use this and leave the other naming properties empty if you have deployed - * the bootstrap environment with standard names but only different qualifiers. - * * @default - Value of context key '@aws-cdk/core:bootstrapQualifier' if set, otherwise `DEFAULT_QUALIFIER` */ readonly qualifier?: string; @@ -104,9 +102,6 @@ interface AppStagingSynthesizerProps { /** * Qualifier to disambiguate multiple environments in the same account * - * You can use this and leave the other naming properties empty if you have deployed - * the bootstrap environment with standard names but only different qualifiers. - * * @default - Value of context key '@aws-cdk/core:bootstrapQualifier' if set, otherwise `DEFAULT_QUALIFIER` */ readonly qualifier?: string; @@ -140,6 +135,7 @@ export class AppStagingSynthesizer extends StackSynthesizer implements IReusable } return new AppStagingSynthesizer({ + qualifier: props.qualifier, bootstrapRoles: props.bootstrapRoles, stagingStackFactory: { stagingStackFactory(boundStack: Stack) { @@ -292,15 +288,20 @@ class BoundAppStagingSynthesizer extends StackSynthesizer implements IBoundAppSt super.bind(stack); this.qualifier = props.qualifier ?? stack.node.tryGetContext(BOOTSTRAP_QUALIFIER_CONTEXT) ?? BoundAppStagingSynthesizer.DEFAULT_QUALIFIER; + const spec = new StringSpecializer(stack, this.qualifier); + const specialize = (arn?: string) => { + if (!arn) { return undefined; } + return spec.specialize(arn); + }; // Roles are implemented this way because roleArn could be undefined, signifying that we are // to use cli credentials instead. - this.lookupRoleArn = props.bootstrapRoles?.lookupRole ? - props.bootstrapRoles.lookupRole.roleArn : BoundAppStagingSynthesizer.DEFAULT_LOOKUP_ROLE_ARN; - this.cloudFormationExecutionRoleArn = props.bootstrapRoles?.cloudFormationExecutionRole ? - props.bootstrapRoles.cloudFormationExecutionRole.roleArn : BoundAppStagingSynthesizer.DEFAULT_CLOUDFORMATION_ROLE_ARN; - this.deploymentActionRoleArn = props.bootstrapRoles?.deploymentActionRole ? - props.bootstrapRoles.deploymentActionRole.roleArn : BoundAppStagingSynthesizer.DEFAULT_DEPLOY_ROLE_ARN; + this.lookupRoleArn = specialize(props.bootstrapRoles?.lookupRole ? + props.bootstrapRoles.lookupRole.roleArn : BoundAppStagingSynthesizer.DEFAULT_LOOKUP_ROLE_ARN); + this.cloudFormationExecutionRoleArn = specialize(props.bootstrapRoles?.cloudFormationExecutionRole ? + props.bootstrapRoles.cloudFormationExecutionRole.roleArn : BoundAppStagingSynthesizer.DEFAULT_CLOUDFORMATION_ROLE_ARN); + this.deploymentActionRoleArn = specialize(props.bootstrapRoles?.deploymentActionRole ? + props.bootstrapRoles.deploymentActionRole.roleArn : BoundAppStagingSynthesizer.DEFAULT_DEPLOY_ROLE_ARN); this.stagingStack = props.stagingStackFactory.stagingStackFactory(stack); } diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts index 10ef738ebfcfb..4bdf66cf5646e 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts @@ -231,11 +231,11 @@ export class DefaultStagingStack extends Stack implements IStagingStack { throw new Error('Cannot call getImagePublishingRoleArn before createImagePublishingRole'); } return Stack.of(this).formatArn({ - partition: '${AWS::{artition}', + partition: '${AWS::Partition}', region: '', // iam is global service: 'iam', resource: 'role', - resourceName: this.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_NAME, + resourceName: this.DEFAULT_IMAGE_ASSET_PUBISHING_ROLE_NAME, arnFormat: ArnFormat.SLASH_RESOURCE_NAME, }); } @@ -279,6 +279,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { encryptionKey: key, }); bucket.grantReadWrite(role); + // bucket.grantReadWrite(iam.Role.fromRoleArn(this, 'blah', 'arn:aws:iam::489318732371:role/cdk-hnb659fds-cfn-exec-role-489318732371-us-east-2')); return stagingBucketName; } diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts index 0f6ce54fd414e..6142eefb448ee 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts @@ -375,6 +375,30 @@ describe('Boostrap Roles', () => { expect(() => new Stack(app, 'Stack')).toThrowError('fileAssetPublishingRole and dockerAssetPublishingRole cannot be specified as cliCredentials(). Please supply an arn to reference an existing IAM role.'); }); + + test('qualifier is resolved in the synthesizer', () => { + const app = new App({ + defaultStackSynthesizer: AppStagingSynthesizer.stackPerEnv({ + qualifier: 'abcdef', + appId: APP_ID, + }), + }); + new Stack(app, 'Stack', { + env: { + account: '000000000000', + region: 'us-east-1', + }, + }); + + // WHEN + const asm = app.synth(); + + // THEN + const stackArtifact = asm.getStackArtifact('Stack'); + + // Bootstrapped role's asset manifest tokens are resolved, where possible + expect(stackArtifact.cloudFormationExecutionRoleArn).toEqual('arn:${AWS::Partition}:iam::000000000000:role/cdk-abcdef-cfn-exec-role-000000000000-us-east-1'); + }); }); function isAssetManifest(x: cxapi.CloudArtifact): x is cxapi.AssetManifestArtifact { diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/.cache/47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53.zip b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/.cache/47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53.zip new file mode 100644 index 0000000000000000000000000000000000000000..10b0e12b11cadc1de5d512902d2f933f7f03822d GIT binary patch literal 146 zcmWIWW@Zs#-~d7f21Ou70FX##UP@|(UO}a{#;G&f7X!|FoIQCm{5lImfHylwN#5pC u0iYT_AP(?mWYT3un1d__G6w<_7?w1GSTIuqyjj^md`2KN1JZ6F4g&yMb{DAt literal 0 HcmV?d00001 diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts index ab682e6ab7220..f47ffdfc89526 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts @@ -1,5 +1,5 @@ import * as path from 'path'; -import * as integ from '@aws-cdk/integ-tests-alpha'; +// import * as integ from '@aws-cdk/integ-tests-alpha'; import { App, Stack } from 'aws-cdk-lib'; import * as lambda from 'aws-cdk-lib/aws-lambda'; import { AppStagingSynthesizer, BootstrapRole } from '../lib'; @@ -8,10 +8,12 @@ const app = new App(); const stack = new Stack(app, 'app-scoped-staging-test', { synthesizer: AppStagingSynthesizer.stackPerEnv({ - appId: 'app-id', + appId: 'zabef', qualifier: 'hnb659fds', bootstrapRoles: { cloudFormationExecutionRole: BootstrapRole.fromRoleArn('arn:aws:iam::489318732371:role/cdk-hnb659fds-cfn-exec-role-489318732371-us-east-2'), + lookupRole: BootstrapRole.fromRoleArn('arn:aws:iam::489318732371:role/cdk-hnb659fds-lookup-role-489318732371-us-east-2'), + deploymentActionRole: BootstrapRole.fromRoleArn('arn:aws:iam::489318732371:role/cdk-hnb659fds-deploy-role-489318732371-us-east-2'), }, }), env: { @@ -26,8 +28,8 @@ new lambda.Function(stack, 'lambda', { runtime: lambda.Runtime.PYTHON_3_9, }); -new integ.IntegTest(app, 'integ-tests', { - testCases: [stack], -}); +// new integ.IntegTest(app, 'integ-tests', { +// testCases: [stack], +// }); app.synth(); diff --git a/packages/aws-cdk-lib/package.json b/packages/aws-cdk-lib/package.json index edc062482677d..1c5685855401a 100644 --- a/packages/aws-cdk-lib/package.json +++ b/packages/aws-cdk-lib/package.json @@ -425,6 +425,7 @@ "./cloud-assembly-schema": "./cloud-assembly-schema/index.js", "./cloudformation-include": "./cloudformation-include/index.js", "./core/lib/helpers-internal": "./core/lib/helpers-internal/index.js", + "./core/lib/stack-synthesizers/_shared": "./core/lib/stack-synthesizers/_shared.js", "./custom-resources": "./custom-resources/index.js", "./cx-api": "./cx-api/index.js", "./lambda-layer-awscli": "./lambda-layer-awscli/index.js", From 97283645f6c5af3a75465535218c0bc252c0712e Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 6 Apr 2023 18:04:30 -0400 Subject: [PATCH 057/120] 90% of lifecycle rules for ephemeral assets --- .../lib/app-staging-synthesizer.ts | 10 ++- .../lib/default-staging-stack.ts | 39 ++++++++++ .../test/app-staging-synthesizer.test.ts | 71 +++++++++++++++++++ packages/aws-cdk-lib/core/lib/assets.ts | 14 ++++ 4 files changed, 133 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts index 3b1170779beb1..ff38b93363a41 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts @@ -18,6 +18,11 @@ import * as cxapi from 'aws-cdk-lib/cx-api'; import { BootstrapRoles, StagingRoles } from './bootstrap-roles'; import { IStagingStack as IStagingStack, DefaultStagingStack } from './default-staging-stack'; +/** + * @internal + */ +export const EPHEMERAL_PREFIX = 'eph-'; + /** * Properties for stackPerEnv static method */ @@ -48,6 +53,8 @@ export interface StackPerEnvProps { * @default - Value of context key '@aws-cdk/core:bootstrapQualifier' if set, otherwise `DEFAULT_QUALIFIER` */ readonly qualifier?: string; + + readonly retainEphemeralFileAssets?: boolean; } /** @@ -182,6 +189,7 @@ export class AppStagingSynthesizer extends StackSynthesizer implements IReusable stackName, fileAssetPublishingRole: props.stagingRoles?.fileAssetPublishingRole, imageAssetPublishingRole: props.stagingRoles?.dockerAssetPublishingRole, + retainEphemeralFileAssets: props.retainEphemeralFileAssets, }); boundStack.addDependency(stagingStack.dependencyStack, 'stack depends on the staging stack for staging resources'); @@ -337,7 +345,7 @@ class BoundAppStagingSynthesizer extends StackSynthesizer implements IBoundAppSt const { bucketName, assumeRoleArn } = this.stagingStack.addFile(asset); const location = this.assetManifest.defaultAddFileAsset(this.boundStack, asset, { bucketName: bucketName, - // bucketPrefix: bucketPrefix, + bucketPrefix: asset.ephemeral ? EPHEMERAL_PREFIX : undefined, role: { assumeRoleArn, }, diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts index 4bdf66cf5646e..65b8501abaec1 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts @@ -3,6 +3,7 @@ import { ArnFormat, BootstraplessSynthesizer, DockerImageAssetSource, + Duration, FileAssetSource, RemovalPolicy, Stack, @@ -13,6 +14,7 @@ import * as iam from 'aws-cdk-lib/aws-iam'; import * as kms from 'aws-cdk-lib/aws-kms'; import * as s3 from 'aws-cdk-lib/aws-s3'; import { IConstruct } from 'constructs'; +import { EPHEMERAL_PREFIX } from './app-staging-synthesizer'; import { BootstrapRole } from './bootstrap-roles'; /** @@ -106,6 +108,23 @@ export interface DefaultStagingStackProps extends StackProps { */ readonly imageAssetPublishingRole?: BootstrapRole; + /** + * Specify a custom lifecycle rule for ephemeral file assets. If you + * specify this property, you must set `prefix: 'eph-'` as part of the rule. + * This is the only way to identify ephemeral assets. + * + * @default - ephemeral assets will be deleted after 10 days + */ + readonly ephemeralFileAssetLifecycleRule?: s3.LifecycleRule; + + /** + * Retain all assets in the s3 bucket, even the ones that have been + * marked ephemeral. + * + * @default false + */ + readonly retainEphemeralFileAssets?: boolean; + /** * Repository lifecycle rules (not fully implemented) */ @@ -156,6 +175,8 @@ export class DefaultStagingStack extends Stack implements IStagingStack { private readonly fileAssetPublishingRoleId = 'CdkFilePublishingRole'; private readonly imageAssetPublishingRoleArn?: string; private readonly imageAssetPublishingRoleId = 'CdkImagePublishingRole'; + private readonly ephemeralFileAssetLifecycleRule?: s3.LifecycleRule; + private readonly retainEphemeralFileAssets?: boolean; // private readonly repositoryLifecycleRules: Record; constructor(scope: App, id: string, props: DefaultStagingStackProps) { @@ -167,6 +188,8 @@ export class DefaultStagingStack extends Stack implements IStagingStack { this.appId = props.appId; this.dependencyStack = this; + this.ephemeralFileAssetLifecycleRule = this.validateEphemeralAssetLifecycleRule(props.ephemeralFileAssetLifecycleRule); + this.retainEphemeralFileAssets = props.retainEphemeralFileAssets; this.stagingBucketName = props.stagingBucketName; this.fileAssetPublishingRoleArn = props.fileAssetPublishingRole ? this.validateStagingRole(props.fileAssetPublishingRole).roleArn : undefined; this.imageAssetPublishingRoleArn = props.imageAssetPublishingRole ? @@ -182,6 +205,14 @@ export class DefaultStagingStack extends Stack implements IStagingStack { return stagingRole; } + private validateEphemeralAssetLifecycleRule(rule?: s3.LifecycleRule) { + if (!rule) { return rule; } + if (rule.prefix !== EPHEMERAL_PREFIX) { + throw new Error(`ephemeralAssetLifecycleRule must contain "prefix: '${EPHEMERAL_PREFIX}'" but got 'prefix: ${rule.prefix}. This prefix is the only way to identify ephemeral assets.`); + } + return rule; + } + // private processLifecycleRules(rules: StagingRepoLifecycleRule[]) { // const ruleMap: Record = {}; // for (const rule of rules) { @@ -279,6 +310,14 @@ export class DefaultStagingStack extends Stack implements IStagingStack { encryptionKey: key, }); bucket.grantReadWrite(role); + + if (this.retainEphemeralFileAssets !== true) { + const rule = this.ephemeralFileAssetLifecycleRule ?? { + prefix: EPHEMERAL_PREFIX, + expiration: Duration.days(10), + }; + bucket.addLifecycleRule(rule); + } // bucket.grantReadWrite(iam.Role.fromRoleArn(this, 'blah', 'arn:aws:iam::489318732371:role/cdk-hnb659fds-cfn-exec-role-489318732371-us-east-2')); return stagingBucketName; diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts index 6142eefb448ee..1ba84871e75d4 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts @@ -5,6 +5,7 @@ import * as cxschema from 'aws-cdk-lib/cloud-assembly-schema'; import { evaluateCFN } from 'aws-cdk-lib/core/test/evaluate-cfn'; import * as cxapi from 'aws-cdk-lib/cx-api'; import { AppStagingSynthesizer, BootstrapRole, StackPerEnvProps } from '../lib'; +import { Template, Match } from 'aws-cdk-lib/assertions'; // import { Repository } from 'aws-cdk-lib/aws-ecr'; // import { Bucket } from 'aws-cdk-lib/aws-s3'; @@ -163,6 +164,76 @@ describe(AppStagingSynthesizer, () => { expect(evalCFN(location1.bucketName)).toEqual(evalCFN(location2.bucketName)); }); + describe('ephemeral assets', () => { + test('ephemeral assets have the \'eph\' prefix', () => { + // WHEN + const location = stack.synthesizer.addFileAsset({ + fileName: __filename, + packaging: FileAssetPackaging.FILE, + sourceHash: 'abcdef', + ephemeral: true, + }); + + // THEN - asset has bucket prefix + expect(evalCFN(location.objectKey)).toEqual('eph-abcdef.js'); + }); + + test('s3 bucket has lifecycle rule on ephemeral assets by default', () => { + // GIVEN + new CfnResource(stack, 'Resource', { + type: 'Some::Resource', + }); + + // WHEN + const asm = app.synth(); + + // THEN + const stagingStackArtifact = asm.getStackArtifact('StagingStack000000000000us-east-1'); + + Template.fromJSON(stagingStackArtifact.template).hasResourceProperties('AWS::S3::Bucket', { + LifecycleConfiguration: { + Rules: [{ + ExpirationInDays: 10, + Prefix: 'eph-', + Status: 'Enabled', + }], + }, + }); + }); + + test('lifecycle rule on ephemeral assets can be customized', () => { + + }); + + test('lifecycle rule on ephemeral assets can be turned off', () => { + // GIVEN + const app2 = new App({ + defaultStackSynthesizer: TestAppScopedStagingSynthesizer.stackPerEnv({ + retainEphemeralFileAssets: true, + }), + }); + const stack2 = new Stack(app2, 'Stack', { + env: { + account: '000000000000', + region: 'us-west-2', + }, + }); + new CfnResource(stack2, 'Resource', { + type: 'Some::Resource', + }); + + // WHEN + const asm = app2.synth(); + + // THEN + const stagingStackArtifact = asm.getStackArtifact('StagingStack000000000000us-west-2'); + + Template.fromJSON(stagingStackArtifact.template).hasResourceProperties('AWS::S3::Bucket', { + LifecycleConfiguration: Match.absent(), + }); + }); + }); + // test('add docker image asset', () => { // // WHEN // const location = stack.synthesizer.addDockerImageAsset({ diff --git a/packages/aws-cdk-lib/core/lib/assets.ts b/packages/aws-cdk-lib/core/lib/assets.ts index eae6306d550bb..ece80bcc33377 100644 --- a/packages/aws-cdk-lib/core/lib/assets.ts +++ b/packages/aws-cdk-lib/core/lib/assets.ts @@ -131,6 +131,20 @@ export interface FileAssetSource { * @default - Required if `fileName` is specified. */ readonly packaging?: FileAssetPackaging; + + /** + * Whether or not the asset is ephemeral; i.e. only used during deployment + * and not needed afterwards. Setting this property to true has an impact + * on the lifecycle of the asset, because we will assume that it is safe to + * delete after the CloudFormation deployment succeeds. + * + * For example, Lambda Function assets are copied over to Lambda during + * deployment. Therefore, it is not necessary to store the asset in S3, so + * we consider those assets ephemeral. + * + * @default false + */ + readonly ephemeral?: boolean; } export interface DockerImageAssetSource { From 048795950daab60b5890f9c0ab578ea59f0e97ea Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Fri, 7 Apr 2023 16:22:20 -0400 Subject: [PATCH 058/120] grant access to s3 bucket to deploy role --- .../lib/app-staging-synthesizer.ts | 17 ++++++++++++++--- .../lib/default-staging-stack.ts | 17 ++++++++++++++++- .../test/app-staging-synthesizer.test.ts | 19 +++++++++++++++++++ .../test/integ.synthesizer.ts | 16 ++++++++-------- 4 files changed, 57 insertions(+), 12 deletions(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts index ff38b93363a41..61a89fa3410d2 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts @@ -151,6 +151,16 @@ export class AppStagingSynthesizer extends StackSynthesizer implements IReusable throw new Error(`Stack ${boundStack.stackName} must be part of an App`); } + // eslint-disable-next-line max-len + const qualifier = props.qualifier ?? boundStack.node.tryGetContext(BOOTSTRAP_QUALIFIER_CONTEXT) ?? BoundAppStagingSynthesizer.DEFAULT_QUALIFIER; + const spec = new StringSpecializer(boundStack, qualifier); + const specialize = (arn?: string) => { + if (!arn) { return undefined; } + return spec.specialize(arn); + }; + const deployActionRoleArn = specialize(props.bootstrapRoles?.deploymentActionRole?.roleArn ? + props.bootstrapRoles.deploymentActionRole.roleArn : BoundAppStagingSynthesizer.DEFAULT_DEPLOY_ROLE_ARN); + let stackId = 'StagingStack'; // Ensure we do not have a scenario where the App includes BOTH // environment-agnostic stacks and set environment stacks. @@ -189,6 +199,7 @@ export class AppStagingSynthesizer extends StackSynthesizer implements IReusable stackName, fileAssetPublishingRole: props.stagingRoles?.fileAssetPublishingRole, imageAssetPublishingRole: props.stagingRoles?.dockerAssetPublishingRole, + deployActionRoleArn, retainEphemeralFileAssets: props.retainEphemeralFileAssets, }); boundStack.addDependency(stagingStack.dependencyStack, 'stack depends on the staging stack for staging resources'); @@ -304,11 +315,11 @@ class BoundAppStagingSynthesizer extends StackSynthesizer implements IBoundAppSt // Roles are implemented this way because roleArn could be undefined, signifying that we are // to use cli credentials instead. - this.lookupRoleArn = specialize(props.bootstrapRoles?.lookupRole ? + this.lookupRoleArn = specialize(props.bootstrapRoles?.lookupRole?.roleArn ? props.bootstrapRoles.lookupRole.roleArn : BoundAppStagingSynthesizer.DEFAULT_LOOKUP_ROLE_ARN); - this.cloudFormationExecutionRoleArn = specialize(props.bootstrapRoles?.cloudFormationExecutionRole ? + this.cloudFormationExecutionRoleArn = specialize(props.bootstrapRoles?.cloudFormationExecutionRole?.roleArn ? props.bootstrapRoles.cloudFormationExecutionRole.roleArn : BoundAppStagingSynthesizer.DEFAULT_CLOUDFORMATION_ROLE_ARN); - this.deploymentActionRoleArn = specialize(props.bootstrapRoles?.deploymentActionRole ? + this.deploymentActionRoleArn = specialize(props.bootstrapRoles?.deploymentActionRole?.roleArn ? props.bootstrapRoles.deploymentActionRole.roleArn : BoundAppStagingSynthesizer.DEFAULT_DEPLOY_ROLE_ARN); this.stagingStack = props.stagingStackFactory.stagingStackFactory(stack); diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts index 65b8501abaec1..f43c856179a7c 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts @@ -108,6 +108,8 @@ export interface DefaultStagingStackProps extends StackProps { */ readonly imageAssetPublishingRole?: BootstrapRole; + readonly deployActionRoleArn?: string; + /** * Specify a custom lifecycle rule for ephemeral file assets. If you * specify this property, you must set `prefix: 'eph-'` as part of the rule. @@ -177,6 +179,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { private readonly imageAssetPublishingRoleId = 'CdkImagePublishingRole'; private readonly ephemeralFileAssetLifecycleRule?: s3.LifecycleRule; private readonly retainEphemeralFileAssets?: boolean; + private readonly deployActionRoleArn?: string; // private readonly repositoryLifecycleRules: Record; constructor(scope: App, id: string, props: DefaultStagingStackProps) { @@ -188,6 +191,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { this.appId = props.appId; this.dependencyStack = this; + this.deployActionRoleArn = props.deployActionRoleArn; this.ephemeralFileAssetLifecycleRule = this.validateEphemeralAssetLifecycleRule(props.ephemeralFileAssetLifecycleRule); this.retainEphemeralFileAssets = props.retainEphemeralFileAssets; this.stagingBucketName = props.stagingBucketName; @@ -311,6 +315,18 @@ export class DefaultStagingStack extends Stack implements IStagingStack { }); bucket.grantReadWrite(role); + if (this.deployActionRoleArn) { + bucket.addToResourcePolicy(new iam.PolicyStatement({ + actions: [ + 's3:GetObject*', + 's3:GetBucket*', + 's3:List*', + ], + resources: [bucket.bucketArn, bucket.arnForObjects('*')], + principals: [new iam.ArnPrincipal(this.deployActionRoleArn)], + })); + } + if (this.retainEphemeralFileAssets !== true) { const rule = this.ephemeralFileAssetLifecycleRule ?? { prefix: EPHEMERAL_PREFIX, @@ -318,7 +334,6 @@ export class DefaultStagingStack extends Stack implements IStagingStack { }; bucket.addLifecycleRule(rule); } - // bucket.grantReadWrite(iam.Role.fromRoleArn(this, 'blah', 'arn:aws:iam::489318732371:role/cdk-hnb659fds-cfn-exec-role-489318732371-us-east-2')); return stagingBucketName; } diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts index 1ba84871e75d4..044251820519c 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts @@ -202,7 +202,26 @@ describe(AppStagingSynthesizer, () => { }); test('lifecycle rule on ephemeral assets can be customized', () => { + // GIVEN + new CfnResource(stack, 'Resource', { + type: 'Some::Resource', + }); + + // WHEN + const asm = app.synth(); + + // THEN + const stagingStackArtifact = asm.getStackArtifact('StagingStack000000000000us-east-1'); + Template.fromJSON(stagingStackArtifact.template).hasResourceProperties('AWS::S3::Bucket', { + LifecycleConfiguration: { + Rules: [{ + ExpirationInDays: 10, + Prefix: 'eph-', + Status: 'Enabled', + }], + }, + }); }); test('lifecycle rule on ephemeral assets can be turned off', () => { diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts index f47ffdfc89526..f9b43d007568e 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts @@ -2,19 +2,19 @@ import * as path from 'path'; // import * as integ from '@aws-cdk/integ-tests-alpha'; import { App, Stack } from 'aws-cdk-lib'; import * as lambda from 'aws-cdk-lib/aws-lambda'; -import { AppStagingSynthesizer, BootstrapRole } from '../lib'; +import { AppStagingSynthesizer } from '../lib'; const app = new App(); const stack = new Stack(app, 'app-scoped-staging-test', { synthesizer: AppStagingSynthesizer.stackPerEnv({ - appId: 'zabef', - qualifier: 'hnb659fds', - bootstrapRoles: { - cloudFormationExecutionRole: BootstrapRole.fromRoleArn('arn:aws:iam::489318732371:role/cdk-hnb659fds-cfn-exec-role-489318732371-us-east-2'), - lookupRole: BootstrapRole.fromRoleArn('arn:aws:iam::489318732371:role/cdk-hnb659fds-lookup-role-489318732371-us-east-2'), - deploymentActionRole: BootstrapRole.fromRoleArn('arn:aws:iam::489318732371:role/cdk-hnb659fds-deploy-role-489318732371-us-east-2'), - }, + appId: 'newId', + // qualifier: 'hnb659fds', + // bootstrapRoles: { + // cloudFormationExecutionRole: BootstrapRole.fromRoleArn('arn:aws:iam::489318732371:role/cdk-hnb659fds-cfn-exec-role-489318732371-us-east-2'), + // lookupRole: BootstrapRole.fromRoleArn('arn:aws:iam::489318732371:role/cdk-hnb659fds-lookup-role-489318732371-us-east-2'), + // deploymentActionRole: BootstrapRole.fromRoleArn('arn:aws:iam::489318732371:role/cdk-hnb659fds-deploy-role-489318732371-us-east-2'), + // }, }), env: { account: '489318732371', From 7460def16bfa2f076e496e763d827ff61a626282 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Fri, 7 Apr 2023 17:50:43 -0400 Subject: [PATCH 059/120] introduce new translation functions in core --- .../core/lib/helpers-internal/index.ts | 1 + .../helpers-internal/string-specializer.ts | 72 +++++++++++++++++++ .../core/lib/stack-synthesizers/_shared.ts | 45 ------------ .../asset-manifest-builder.ts | 2 +- .../cli-credentials-synthesizer.ts | 3 +- .../stack-synthesizers/default-synthesizer.ts | 3 +- .../stack-synthesizers/stack-synthesizer.ts | 3 +- packages/aws-cdk-lib/core/lib/stack.ts | 2 +- .../string-specializer.test.ts | 15 ++++ packages/aws-cdk-lib/package.json | 1 - 10 files changed, 96 insertions(+), 51 deletions(-) create mode 100644 packages/aws-cdk-lib/core/lib/helpers-internal/string-specializer.ts create mode 100644 packages/aws-cdk-lib/core/test/helpers-internal/string-specializer.test.ts diff --git a/packages/aws-cdk-lib/core/lib/helpers-internal/index.ts b/packages/aws-cdk-lib/core/lib/helpers-internal/index.ts index 9a36222b224cf..bc3f28e0107bb 100644 --- a/packages/aws-cdk-lib/core/lib/helpers-internal/index.ts +++ b/packages/aws-cdk-lib/core/lib/helpers-internal/index.ts @@ -2,3 +2,4 @@ export * from './cfn-parse'; // Other libraries are going to need this as well export { md5hash } from '../private/md5'; export * from './customize-roles'; +export * from './string-specializer'; \ No newline at end of file diff --git a/packages/aws-cdk-lib/core/lib/helpers-internal/string-specializer.ts b/packages/aws-cdk-lib/core/lib/helpers-internal/string-specializer.ts new file mode 100644 index 0000000000000..30d5489eff020 --- /dev/null +++ b/packages/aws-cdk-lib/core/lib/helpers-internal/string-specializer.ts @@ -0,0 +1,72 @@ +import { Stack } from '../stack'; +import * as cxapi from '../../../cx-api'; +import { Token } from '../token'; +import { Aws } from '../cfn-pseudo'; + +/** + * A "replace-all" function that doesn't require us escaping a literal string to a regex + */ +function replaceAll(s: string, search: string, replace: string) { + return s.split(search).join(replace); +} + +export class StringSpecializer { + constructor(private readonly stack: Stack, private readonly qualifier: string) { + } + + /** + * Function to replace placeholders in the input string as much as possible + * + * We replace: + * - ${Qualifier}: always + * - ${AWS::AccountId}, ${AWS::Region}: only if we have the actual values available + * - ${AWS::Partition}: never, since we never have the actual partition value. + */ + public specialize(s: string): string { + s = replaceAll(s, '${Qualifier}', this.qualifier); + return cxapi.EnvironmentPlaceholders.replace(s, { + region: resolvedOr(this.stack.region, cxapi.EnvironmentPlaceholders.CURRENT_REGION), + accountId: resolvedOr(this.stack.account, cxapi.EnvironmentPlaceholders.CURRENT_ACCOUNT), + partition: cxapi.EnvironmentPlaceholders.CURRENT_PARTITION, + }); + } + + /** + * Specialize only the qualifier + */ + public qualifierOnly(s: string): string { + return replaceAll(s, '${Qualifier}', this.qualifier); + } +} + +/** + * Return the given value if resolved or fall back to a default + */ +export function resolvedOr(x: string, def: A): string | A { + return Token.isUnresolved(x) ? def : x; +} + +const ASSET_TOKENS = ['${AWS::Partition}', '${AWS::Region}', '${AWS::AccountId}']; +const CFN_TOKENS = [Aws.PARTITION, Aws.REGION, Aws.ACCOUNT_ID]; + +/** + * Replaces CloudFormation Tokens ('Aws.PARTITION') with corresponding + * Asset Tokens ('${AWS::Partition}'). + */ +export function translateCfnTokenToAssetToken(arn: string) { + for (let i = 0; i < CFN_TOKENS.length; i++) { + arn = replaceAll(arn, CFN_TOKENS[i], ASSET_TOKENS[i]); + } + return arn; +} + +/** + * Replaces Asset Tokens ('${AWS::Partition}') with corresponding + * CloudFormation Tokens ('Aws.PARTITION'). + */ +export function translateAssetTokenToCfnToken(arn: string) { + for (let i = 0; i < ASSET_TOKENS.length; i++) { + arn = replaceAll(arn, ASSET_TOKENS[i], CFN_TOKENS[i]); + } + return arn; +} diff --git a/packages/aws-cdk-lib/core/lib/stack-synthesizers/_shared.ts b/packages/aws-cdk-lib/core/lib/stack-synthesizers/_shared.ts index c8165e72f9c0a..e4d56fa460734 100644 --- a/packages/aws-cdk-lib/core/lib/stack-synthesizers/_shared.ts +++ b/packages/aws-cdk-lib/core/lib/stack-synthesizers/_shared.ts @@ -1,10 +1,8 @@ import * as crypto from 'crypto'; import * as cxschema from '../../../cloud-assembly-schema'; -import * as cxapi from '../../../cx-api'; import { Node, IConstruct } from 'constructs'; import { ISynthesisSession } from './types'; import { Stack } from '../stack'; -import { Token } from '../token'; /** * Shared logic of writing stack artifact to the Cloud Assembly @@ -126,46 +124,3 @@ export function assertBound(x: A | undefined): asserts x is NonNullable { function nonEmptyDict(xs: Record) { return Object.keys(xs).length > 0 ? xs : undefined; } - -/** - * A "replace-all" function that doesn't require us escaping a literal string to a regex - */ -function replaceAll(s: string, search: string, replace: string) { - return s.split(search).join(replace); -} - -export class StringSpecializer { - constructor(private readonly stack: Stack, private readonly qualifier: string) { - } - - /** - * Function to replace placeholders in the input string as much as possible - * - * We replace: - * - ${Qualifier}: always - * - ${AWS::AccountId}, ${AWS::Region}: only if we have the actual values available - * - ${AWS::Partition}: never, since we never have the actual partition value. - */ - public specialize(s: string): string { - s = replaceAll(s, '${Qualifier}', this.qualifier); - return cxapi.EnvironmentPlaceholders.replace(s, { - region: resolvedOr(this.stack.region, cxapi.EnvironmentPlaceholders.CURRENT_REGION), - accountId: resolvedOr(this.stack.account, cxapi.EnvironmentPlaceholders.CURRENT_ACCOUNT), - partition: cxapi.EnvironmentPlaceholders.CURRENT_PARTITION, - }); - } - - /** - * Specialize only the qualifier - */ - public qualifierOnly(s: string): string { - return replaceAll(s, '${Qualifier}', this.qualifier); - } -} - -/** - * Return the given value if resolved or fall back to a default - */ -export function resolvedOr(x: string, def: A): string | A { - return Token.isUnresolved(x) ? def : x; -} diff --git a/packages/aws-cdk-lib/core/lib/stack-synthesizers/asset-manifest-builder.ts b/packages/aws-cdk-lib/core/lib/stack-synthesizers/asset-manifest-builder.ts index c25f620a314db..067826fa30bf4 100644 --- a/packages/aws-cdk-lib/core/lib/stack-synthesizers/asset-manifest-builder.ts +++ b/packages/aws-cdk-lib/core/lib/stack-synthesizers/asset-manifest-builder.ts @@ -1,7 +1,7 @@ import * as fs from 'fs'; import * as path from 'path'; import * as cxschema from '../../../cloud-assembly-schema'; -import { resolvedOr } from './_shared'; +import { resolvedOr } from '../helpers-internal/string-specializer'; import { ISynthesisSession } from './types'; import { FileAssetSource, FileAssetPackaging, DockerImageAssetSource } from '../assets'; import { Stack } from '../stack'; diff --git a/packages/aws-cdk-lib/core/lib/stack-synthesizers/cli-credentials-synthesizer.ts b/packages/aws-cdk-lib/core/lib/stack-synthesizers/cli-credentials-synthesizer.ts index a85f483c3654c..a5242f230f7c7 100644 --- a/packages/aws-cdk-lib/core/lib/stack-synthesizers/cli-credentials-synthesizer.ts +++ b/packages/aws-cdk-lib/core/lib/stack-synthesizers/cli-credentials-synthesizer.ts @@ -1,5 +1,6 @@ import * as cxapi from '../../../cx-api'; -import { assertBound, StringSpecializer } from './_shared'; +import { StringSpecializer } from '../helpers-internal/string-specializer'; +import { assertBound } from './_shared'; import { AssetManifestBuilder } from './asset-manifest-builder'; import { BOOTSTRAP_QUALIFIER_CONTEXT, DefaultStackSynthesizer } from './default-synthesizer'; import { StackSynthesizer } from './stack-synthesizer'; diff --git a/packages/aws-cdk-lib/core/lib/stack-synthesizers/default-synthesizer.ts b/packages/aws-cdk-lib/core/lib/stack-synthesizers/default-synthesizer.ts index 9185b5e05016e..e7eaee813239e 100644 --- a/packages/aws-cdk-lib/core/lib/stack-synthesizers/default-synthesizer.ts +++ b/packages/aws-cdk-lib/core/lib/stack-synthesizers/default-synthesizer.ts @@ -1,5 +1,6 @@ import * as cxapi from '../../../cx-api'; -import { assertBound, StringSpecializer } from './_shared'; +import { StringSpecializer } from '../helpers-internal'; +import { assertBound } from './_shared'; import { AssetManifestBuilder } from './asset-manifest-builder'; import { StackSynthesizer } from './stack-synthesizer'; import { ISynthesisSession, IReusableStackSynthesizer, IBoundStackSynthesizer } from './types'; diff --git a/packages/aws-cdk-lib/core/lib/stack-synthesizers/stack-synthesizer.ts b/packages/aws-cdk-lib/core/lib/stack-synthesizers/stack-synthesizer.ts index 5306d98b4789e..8d544130ca004 100644 --- a/packages/aws-cdk-lib/core/lib/stack-synthesizers/stack-synthesizer.ts +++ b/packages/aws-cdk-lib/core/lib/stack-synthesizers/stack-synthesizer.ts @@ -2,7 +2,8 @@ import * as fs from 'fs'; import * as path from 'path'; import * as cxschema from '../../../cloud-assembly-schema'; import * as cxapi from '../../../cx-api'; -import { addStackArtifactToAssembly, contentHash, resolvedOr } from './_shared'; +import { resolvedOr } from '../helpers-internal/string-specializer'; +import { addStackArtifactToAssembly, contentHash } from './_shared'; import { IStackSynthesizer, ISynthesisSession } from './types'; import { DockerImageAssetLocation, DockerImageAssetSource, FileAssetLocation, FileAssetSource, FileAssetPackaging } from '../assets'; import { Fn } from '../cfn-fn'; diff --git a/packages/aws-cdk-lib/core/lib/stack.ts b/packages/aws-cdk-lib/core/lib/stack.ts index d798d0a50e50a..30566a720c655 100644 --- a/packages/aws-cdk-lib/core/lib/stack.ts +++ b/packages/aws-cdk-lib/core/lib/stack.ts @@ -1717,7 +1717,7 @@ import { Names } from './names'; import { Reference } from './reference'; import { IResolvable } from './resolvable'; import { DefaultStackSynthesizer, IStackSynthesizer, ISynthesisSession, LegacyStackSynthesizer, BOOTSTRAP_QUALIFIER_CONTEXT, isReusableStackSynthesizer } from './stack-synthesizers'; -import { StringSpecializer } from './stack-synthesizers/_shared'; +import { StringSpecializer } from './helpers-internal/string-specializer'; import { Stage } from './stage'; import { ITaggable, TagManager } from './tag-manager'; import { Token, Tokenization } from './token'; diff --git a/packages/aws-cdk-lib/core/test/helpers-internal/string-specializer.test.ts b/packages/aws-cdk-lib/core/test/helpers-internal/string-specializer.test.ts new file mode 100644 index 0000000000000..382b3d268b148 --- /dev/null +++ b/packages/aws-cdk-lib/core/test/helpers-internal/string-specializer.test.ts @@ -0,0 +1,15 @@ +import { Aws } from '../../lib'; +import { translateAssetTokenToCfnToken, translateCfnTokenToAssetToken } from '../../lib/helpers-internal'; + +describe('translations between token kinds', () => { + const CfnTokenArn = `arn:${Aws.PARTITION}:resource:${Aws.REGION}:${Aws.ACCOUNT_ID}:name`; + const AssetTokenArn = 'arn:${AWS::Partition}:resource:${AWS::Region}:${AWS::AccountId}:name'; + + test('translateAssetTokenToCfnToken', () => { + expect(translateAssetTokenToCfnToken(AssetTokenArn)).toEqual(CfnTokenArn); + }); + + test('translateCfnTokenToAssetToken', () => { + expect(translateCfnTokenToAssetToken(CfnTokenArn)).toEqual(AssetTokenArn); + }); +}); \ No newline at end of file diff --git a/packages/aws-cdk-lib/package.json b/packages/aws-cdk-lib/package.json index 1c5685855401a..edc062482677d 100644 --- a/packages/aws-cdk-lib/package.json +++ b/packages/aws-cdk-lib/package.json @@ -425,7 +425,6 @@ "./cloud-assembly-schema": "./cloud-assembly-schema/index.js", "./cloudformation-include": "./cloudformation-include/index.js", "./core/lib/helpers-internal": "./core/lib/helpers-internal/index.js", - "./core/lib/stack-synthesizers/_shared": "./core/lib/stack-synthesizers/_shared.js", "./custom-resources": "./custom-resources/index.js", "./cx-api": "./cx-api/index.js", "./lambda-layer-awscli": "./lambda-layer-awscli/index.js", From 08cd887838ae187034738a02e52b7e43182c9c2e Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Fri, 7 Apr 2023 17:50:55 -0400 Subject: [PATCH 060/120] use new translation functions --- .../lib/app-staging-synthesizer.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts index 61a89fa3410d2..535fc5db48726 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts @@ -13,7 +13,7 @@ import { StackSynthesizer, Token, } from 'aws-cdk-lib'; -import { StringSpecializer } from 'aws-cdk-lib/core/lib/stack-synthesizers/_shared'; +import { StringSpecializer, translateAssetTokenToCfnToken } from 'aws-cdk-lib/core/lib/helpers-internal'; import * as cxapi from 'aws-cdk-lib/cx-api'; import { BootstrapRoles, StagingRoles } from './bootstrap-roles'; import { IStagingStack as IStagingStack, DefaultStagingStack } from './default-staging-stack'; @@ -154,12 +154,14 @@ export class AppStagingSynthesizer extends StackSynthesizer implements IReusable // eslint-disable-next-line max-len const qualifier = props.qualifier ?? boundStack.node.tryGetContext(BOOTSTRAP_QUALIFIER_CONTEXT) ?? BoundAppStagingSynthesizer.DEFAULT_QUALIFIER; const spec = new StringSpecializer(boundStack, qualifier); - const specialize = (arn?: string) => { - if (!arn) { return undefined; } - return spec.specialize(arn); - }; - const deployActionRoleArn = specialize(props.bootstrapRoles?.deploymentActionRole?.roleArn ? - props.bootstrapRoles.deploymentActionRole.roleArn : BoundAppStagingSynthesizer.DEFAULT_DEPLOY_ROLE_ARN); + let deployActionRoleArn = undefined; + if (props.bootstrapRoles?.deploymentActionRole === undefined || props.bootstrapRoles.deploymentActionRole.roleArn) { + deployActionRoleArn = translateAssetTokenToCfnToken( + spec.specialize( + props.bootstrapRoles?.deploymentActionRole?.roleArn ?? BoundAppStagingSynthesizer.DEFAULT_DEPLOY_ROLE_ARN, + ), + ); + } let stackId = 'StagingStack'; // Ensure we do not have a scenario where the App includes BOTH From 794cc680265fc8991079bcea060822ac28f1e95a Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Mon, 10 Apr 2023 16:02:14 -0400 Subject: [PATCH 061/120] revamp bootstrap roles api --- .../lib/app-staging-synthesizer.ts | 46 ++++---- .../lib/bootstrap-roles.ts | 30 +++++- .../lib/default-staging-stack.ts | 7 +- ...3a7c0374db287baed4c18c990b4af1444d8d53.zip | Bin 146 -> 0 bytes ...ngStack489318732371us-east-2.template.json | 72 ++++++++++++- .../app-scoped-staging-test.assets.json | 12 +-- .../app-scoped-staging-test.template.json | 2 +- .../manifest.json | 16 ++- .../integ.synthesizer.js.snapshot/tree.json | 102 ++++++++++++++++-- .../test/integ.synthesizer.ts | 16 +-- 10 files changed, 238 insertions(+), 65 deletions(-) delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/.cache/47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53.zip diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts index 535fc5db48726..2c64d52fd6c8b 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts @@ -13,9 +13,9 @@ import { StackSynthesizer, Token, } from 'aws-cdk-lib'; -import { StringSpecializer, translateAssetTokenToCfnToken } from 'aws-cdk-lib/core/lib/helpers-internal'; +import { StringSpecializer } from 'aws-cdk-lib/core/lib/helpers-internal'; import * as cxapi from 'aws-cdk-lib/cx-api'; -import { BootstrapRoles, StagingRoles } from './bootstrap-roles'; +import { BootstrapRole, BootstrapRoles, StagingRoles } from './bootstrap-roles'; import { IStagingStack as IStagingStack, DefaultStagingStack } from './default-staging-stack'; /** @@ -151,17 +151,14 @@ export class AppStagingSynthesizer extends StackSynthesizer implements IReusable throw new Error(`Stack ${boundStack.stackName} must be part of an App`); } - // eslint-disable-next-line max-len - const qualifier = props.qualifier ?? boundStack.node.tryGetContext(BOOTSTRAP_QUALIFIER_CONTEXT) ?? BoundAppStagingSynthesizer.DEFAULT_QUALIFIER; + const qualifier = props.qualifier ?? + boundStack.node.tryGetContext(BOOTSTRAP_QUALIFIER_CONTEXT) ?? + BoundAppStagingSynthesizer.DEFAULT_QUALIFIER; + const spec = new StringSpecializer(boundStack, qualifier); - let deployActionRoleArn = undefined; - if (props.bootstrapRoles?.deploymentActionRole === undefined || props.bootstrapRoles.deploymentActionRole.roleArn) { - deployActionRoleArn = translateAssetTokenToCfnToken( - spec.specialize( - props.bootstrapRoles?.deploymentActionRole?.roleArn ?? BoundAppStagingSynthesizer.DEFAULT_DEPLOY_ROLE_ARN, - ), - ); - } + const deployActionRole = props.bootstrapRoles?.deploymentActionRole + ?? BootstrapRole.fromRoleArn(BoundAppStagingSynthesizer.DEFAULT_DEPLOY_ROLE_ARN); + const deployActionRoleArn = !deployActionRole.isCliCredentials() ? deployActionRole.renderRoleArn({ spec, tokenType: 'cfn' }) : undefined; let stackId = 'StagingStack'; // Ensure we do not have a scenario where the App includes BOTH @@ -310,19 +307,18 @@ class BoundAppStagingSynthesizer extends StackSynthesizer implements IBoundAppSt this.qualifier = props.qualifier ?? stack.node.tryGetContext(BOOTSTRAP_QUALIFIER_CONTEXT) ?? BoundAppStagingSynthesizer.DEFAULT_QUALIFIER; const spec = new StringSpecializer(stack, this.qualifier); - const specialize = (arn?: string) => { - if (!arn) { return undefined; } - return spec.specialize(arn); - }; - - // Roles are implemented this way because roleArn could be undefined, signifying that we are - // to use cli credentials instead. - this.lookupRoleArn = specialize(props.bootstrapRoles?.lookupRole?.roleArn ? - props.bootstrapRoles.lookupRole.roleArn : BoundAppStagingSynthesizer.DEFAULT_LOOKUP_ROLE_ARN); - this.cloudFormationExecutionRoleArn = specialize(props.bootstrapRoles?.cloudFormationExecutionRole?.roleArn ? - props.bootstrapRoles.cloudFormationExecutionRole.roleArn : BoundAppStagingSynthesizer.DEFAULT_CLOUDFORMATION_ROLE_ARN); - this.deploymentActionRoleArn = specialize(props.bootstrapRoles?.deploymentActionRole?.roleArn ? - props.bootstrapRoles.deploymentActionRole.roleArn : BoundAppStagingSynthesizer.DEFAULT_DEPLOY_ROLE_ARN); + + const lookupRole = props.bootstrapRoles?.lookupRole ?? BootstrapRole.fromRoleArn(BoundAppStagingSynthesizer.DEFAULT_LOOKUP_ROLE_ARN); + this.lookupRoleArn = !lookupRole.isCliCredentials() ? lookupRole.renderRoleArn({ spec }) : undefined; + + const cloudFormationExecutionRole = props.bootstrapRoles?.cloudFormationExecutionRole ?? + BootstrapRole.fromRoleArn(BoundAppStagingSynthesizer.DEFAULT_CLOUDFORMATION_ROLE_ARN); + this.cloudFormationExecutionRoleArn = !cloudFormationExecutionRole.isCliCredentials() ? + cloudFormationExecutionRole.renderRoleArn({ spec }) : undefined; + + const deploymentActionRole = props.bootstrapRoles?.deploymentActionRole ?? + BootstrapRole.fromRoleArn(BoundAppStagingSynthesizer.DEFAULT_DEPLOY_ROLE_ARN); + this.deploymentActionRoleArn = !deploymentActionRole.isCliCredentials() ? deploymentActionRole.renderRoleArn({ spec }) : undefined; this.stagingStack = props.stagingStackFactory.stagingStackFactory(stack); } diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts index 4b54a0e089604..113e23bdc1235 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts @@ -1,3 +1,5 @@ +import { StringSpecializer, translateAssetTokenToCfnToken, translateCfnTokenToAssetToken } from 'aws-cdk-lib/core/lib/helpers-internal'; + /** * Bootstrapped role specifier. These roles must exist already. * This class does not create new IAM Roles. @@ -7,7 +9,7 @@ export class BootstrapRole { * Use the currently assumed role/credentials */ public static cliCredentials() { - return new BootstrapRole(undefined); + return new BootstrapRole(BootstrapRole.CLI_CREDS); } /** @@ -17,9 +19,33 @@ export class BootstrapRole { return new BootstrapRole(arn); } - private constructor(/** Bootstrap role arn */ public readonly roleArn: string | undefined) {} + private static CLI_CREDS = 'cli-credentials'; + + private constructor(private readonly roleArn: string) {} + + public isCliCredentials() { + return this.roleArn === BootstrapRole.CLI_CREDS; + } + + public renderRoleArn(options: { + spec?: StringSpecializer, + tokenType?: 'asset' | 'cfn', + } = {}) { + if (this.isCliCredentials()) { return undefined; } + if (!options.spec) { return this.roleArn; } + + const arn = options.spec.specialize(this.roleArn); + if (options.tokenType === 'asset') { + return translateCfnTokenToAssetToken(arn); + } else if (options.tokenType === 'cfn') { + return translateAssetTokenToCfnToken(arn); + } else { + return arn; + } + } } + /** * Roles that are bootstrapped to your account. */ diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts index f43c856179a7c..52119e31d52e5 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts @@ -195,15 +195,16 @@ export class DefaultStagingStack extends Stack implements IStagingStack { this.ephemeralFileAssetLifecycleRule = this.validateEphemeralAssetLifecycleRule(props.ephemeralFileAssetLifecycleRule); this.retainEphemeralFileAssets = props.retainEphemeralFileAssets; this.stagingBucketName = props.stagingBucketName; - this.fileAssetPublishingRoleArn = props.fileAssetPublishingRole ? this.validateStagingRole(props.fileAssetPublishingRole).roleArn : undefined; + this.fileAssetPublishingRoleArn = props.fileAssetPublishingRole ? + this.validateStagingRole(props.fileAssetPublishingRole).renderRoleArn() : undefined; this.imageAssetPublishingRoleArn = props.imageAssetPublishingRole ? - this.validateStagingRole(props.imageAssetPublishingRole).roleArn : undefined; + this.validateStagingRole(props.imageAssetPublishingRole).renderRoleArn() : undefined; // this.repositoryLifecycleRules = this.processLifecycleRules(props.repositoryLifecycleRules ?? []); this.stagingRepos = {}; } private validateStagingRole(stagingRole: BootstrapRole) { - if (stagingRole.roleArn === undefined) { + if (stagingRole.isCliCredentials()) { throw new Error('fileAssetPublishingRole and dockerAssetPublishingRole cannot be specified as cliCredentials(). Please supply an arn to reference an existing IAM role.'); } return stagingRole; diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/.cache/47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53.zip b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/.cache/47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53.zip deleted file mode 100644 index 10b0e12b11cadc1de5d512902d2f933f7f03822d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 146 zcmWIWW@Zs#-~d7f21Ou70FX##UP@|(UO}a{#;G&f7X!|FoIQCm{5lImfHylwN#5pC u0iYT_AP(?mWYT3un1d__G6w<_7?w1GSTIuqyjj^md`2KN1JZ6F4g&yMb{DAt diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack489318732371us-east-2.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack489318732371us-east-2.template.json index b7f5290d73733..fef8ed207d57a 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack489318732371us-east-2.template.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack489318732371us-east-2.template.json @@ -15,7 +15,7 @@ ], "Version": "2012-10-17" }, - "RoleName": "cdk-file-publishing-role-us-east-2-app-id" + "RoleName": "cdk-file-publishing-role-us-east-2-newId3" } }, "CdkFilePublishingRoleDefaultPolicyE914275E": { @@ -133,7 +133,7 @@ "BucketKeyAlias69A0886F": { "Type": "AWS::KMS::Alias", "Properties": { - "AliasName": "alias/CdkStagingBucketKey489318732371-us-east-2-app-id", + "AliasName": "alias/CdkStagingBucketKey489318732371-us-east-2-newId3", "TargetKeyId": { "Fn::GetAtt": [ "BucketKey7092080A", @@ -160,10 +160,76 @@ } ] }, - "BucketName": "cdk-489318732371-us-east-2-app-id" + "BucketName": "cdk-489318732371-us-east-2-newid3", + "LifecycleConfiguration": { + "Rules": [ + { + "ExpirationInDays": 10, + "Prefix": "eph-", + "Status": "Enabled" + } + ] + } }, "UpdateReplacePolicy": "Retain", "DeletionPolicy": "Retain" + }, + "CdkStagingBucketPolicy42BD1F92": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "CdkStagingBucket1636058C" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::489318732371:role/cdk-hnb659fds-deploy-role-489318732371-us-east-2" + ] + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "CdkStagingBucket1636058C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "CdkStagingBucket1636058C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } } } } \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json index c365ad9ea7749..68dddcdc3f5b7 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json @@ -8,24 +8,24 @@ }, "destinations": { "489318732371-us-east-2": { - "bucketName": "cdk-489318732371-us-east-2-app-id", + "bucketName": "cdk-489318732371-us-east-2-newid3", "objectKey": "47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53.zip", "region": "us-east-2", - "assumeRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-file-publishing-role-us-east-2-app-id" + "assumeRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-file-publishing-role-us-east-2-newId3" } } }, - "041f053398069413175306d7e2e5dd7fe62b1f288bf8efb64288eefbdcb23d8a": { + "3721bf9337687fef9ff88a4b3d490d60c51c3261d2c060cea7ce7d33780fd99a": { "source": { "path": "app-scoped-staging-test.template.json", "packaging": "file" }, "destinations": { "489318732371-us-east-2": { - "bucketName": "cdk-489318732371-us-east-2-app-id", - "objectKey": "041f053398069413175306d7e2e5dd7fe62b1f288bf8efb64288eefbdcb23d8a.json", + "bucketName": "cdk-489318732371-us-east-2-newid3", + "objectKey": "3721bf9337687fef9ff88a4b3d490d60c51c3261d2c060cea7ce7d33780fd99a.json", "region": "us-east-2", - "assumeRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-file-publishing-role-us-east-2-app-id" + "assumeRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-file-publishing-role-us-east-2-newId3" } } } diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json index 3a94d29acfed8..40cc32122dd89 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json @@ -35,7 +35,7 @@ "Type": "AWS::Lambda::Function", "Properties": { "Code": { - "S3Bucket": "cdk-489318732371-us-east-2-app-id", + "S3Bucket": "cdk-489318732371-us-east-2-newid3", "S3Key": "47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53.zip" }, "Role": { diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json index 215455b8b4654..a8aec02d1cdcf 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json @@ -13,14 +13,14 @@ "properties": { "templateFile": "app-scoped-staging-test.template.json", "validateOnSynth": false, - "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-deploy-role-${AWS::AccountId}-${AWS::Region}", + "assumeRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-hnb659fds-deploy-role-489318732371-us-east-2", "additionalDependencies": [ "app-scoped-staging-test.assets" ], - "stackTemplateAssetObjectUrl": "s3://cdk-489318732371-us-east-2-app-id/041f053398069413175306d7e2e5dd7fe62b1f288bf8efb64288eefbdcb23d8a.json", - "cloudFormationExecutionRoleArn": "arn:aws:iam::489318732371:role/cdk-hnb659fds-cfn-exec-role-489318732371-us-east-2", + "stackTemplateAssetObjectUrl": "s3://cdk-489318732371-us-east-2-newid3/3721bf9337687fef9ff88a4b3d490d60c51c3261d2c060cea7ce7d33780fd99a.json", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-hnb659fds-cfn-exec-role-489318732371-us-east-2", "lookupRole": { - "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-lookup-role-${AWS::AccountId}-${AWS::Region}" + "arn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-hnb659fds-lookup-role-489318732371-us-east-2" } }, "dependencies": [ @@ -51,7 +51,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-hnb659fds-deploy-role-489318732371-us-east-2", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-hnb659fds-cfn-exec-role-489318732371-us-east-2", - "stackName": "StagingStackapp-id" + "stackName": "StagingStacknewId3" }, "metadata": { "/StagingStack489318732371us-east-2/CdkFilePublishingRole/Resource": [ @@ -83,6 +83,12 @@ "type": "aws:cdk:logicalId", "data": "CdkStagingBucket1636058C" } + ], + "/StagingStack489318732371us-east-2/CdkStagingBucket/Policy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "CdkStagingBucketPolicy42BD1F92" + } ] }, "displayName": "StagingStack489318732371us-east-2" diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json index 70d259e7aac44..7200d7fecaa44 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json @@ -102,7 +102,7 @@ "aws:cdk:cloudformation:type": "AWS::Lambda::Function", "aws:cdk:cloudformation:props": { "code": { - "s3Bucket": "cdk-489318732371-us-east-2-app-id", + "s3Bucket": "cdk-489318732371-us-east-2-newid3", "s3Key": "47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53.zip" }, "role": { @@ -166,7 +166,7 @@ ], "Version": "2012-10-17" }, - "roleName": "cdk-file-publishing-role-us-east-2-app-id" + "roleName": "cdk-file-publishing-role-us-east-2-newId3" } }, "constructInfo": { @@ -330,7 +330,7 @@ "attributes": { "aws:cdk:cloudformation:type": "AWS::KMS::Alias", "aws:cdk:cloudformation:props": { - "aliasName": "alias/CdkStagingBucketKey489318732371-us-east-2-app-id", + "aliasName": "alias/CdkStagingBucketKey489318732371-us-east-2-newId3", "targetKeyId": { "Fn::GetAtt": [ "BucketKey7092080A", @@ -381,13 +381,97 @@ } ] }, - "bucketName": "cdk-489318732371-us-east-2-app-id" + "bucketName": "cdk-489318732371-us-east-2-newid3", + "lifecycleConfiguration": { + "rules": [ + { + "expirationInDays": 10, + "prefix": "eph-", + "status": "Enabled" + } + ] + } } }, "constructInfo": { "fqn": "aws-cdk-lib.aws_s3.CfnBucket", "version": "0.0.0" } + }, + "Policy": { + "id": "Policy", + "path": "StagingStack489318732371us-east-2/CdkStagingBucket/Policy", + "children": { + "Resource": { + "id": "Resource", + "path": "StagingStack489318732371us-east-2/CdkStagingBucket/Policy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::S3::BucketPolicy", + "aws:cdk:cloudformation:props": { + "bucket": { + "Ref": "CdkStagingBucket1636058C" + }, + "policyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::489318732371:role/cdk-hnb659fds-deploy-role-489318732371-us-east-2" + ] + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "CdkStagingBucket1636058C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "CdkStagingBucket1636058C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.CfnBucketPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.BucketPolicy", + "version": "0.0.0" + } } }, "constructInfo": { @@ -397,7 +481,7 @@ } }, "constructInfo": { - "fqn": "@aws-cdk/app-staging-synthesizer.DefaultStagingStack", + "fqn": "aws-cdk-lib.Stack", "version": "0.0.0" } }, @@ -445,14 +529,14 @@ } }, "constructInfo": { - "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } } }, "constructInfo": { - "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } }, "Tree": { diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts index f9b43d007568e..722adb0b0c233 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts @@ -1,5 +1,5 @@ import * as path from 'path'; -// import * as integ from '@aws-cdk/integ-tests-alpha'; +import * as integ from '@aws-cdk/integ-tests-alpha'; import { App, Stack } from 'aws-cdk-lib'; import * as lambda from 'aws-cdk-lib/aws-lambda'; import { AppStagingSynthesizer } from '../lib'; @@ -8,13 +8,7 @@ const app = new App(); const stack = new Stack(app, 'app-scoped-staging-test', { synthesizer: AppStagingSynthesizer.stackPerEnv({ - appId: 'newId', - // qualifier: 'hnb659fds', - // bootstrapRoles: { - // cloudFormationExecutionRole: BootstrapRole.fromRoleArn('arn:aws:iam::489318732371:role/cdk-hnb659fds-cfn-exec-role-489318732371-us-east-2'), - // lookupRole: BootstrapRole.fromRoleArn('arn:aws:iam::489318732371:role/cdk-hnb659fds-lookup-role-489318732371-us-east-2'), - // deploymentActionRole: BootstrapRole.fromRoleArn('arn:aws:iam::489318732371:role/cdk-hnb659fds-deploy-role-489318732371-us-east-2'), - // }, + appId: 'newId3', }), env: { account: '489318732371', @@ -28,8 +22,8 @@ new lambda.Function(stack, 'lambda', { runtime: lambda.Runtime.PYTHON_3_9, }); -// new integ.IntegTest(app, 'integ-tests', { -// testCases: [stack], -// }); +new integ.IntegTest(app, 'integ-tests', { + testCases: [stack], +}); app.synth(); From 2104e68f2f26641bc50465378958b75f0847f4a8 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Mon, 10 Apr 2023 16:10:03 -0400 Subject: [PATCH 062/120] move tests around --- .../test/app-staging-synthesizer.test.ts | 150 +----------------- .../test/bootstrap-roles.test.ts | 145 +++++++++++++++++ .../app-staging-synthesizer/test/util.ts | 34 ++++ 3 files changed, 180 insertions(+), 149 deletions(-) create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/util.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts index 044251820519c..8e5131fee82f8 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts @@ -6,19 +6,10 @@ import { evaluateCFN } from 'aws-cdk-lib/core/test/evaluate-cfn'; import * as cxapi from 'aws-cdk-lib/cx-api'; import { AppStagingSynthesizer, BootstrapRole, StackPerEnvProps } from '../lib'; import { Template, Match } from 'aws-cdk-lib/assertions'; +import { APP_ID, CFN_CONTEXT, CLOUDFORMATION_EXECUTION_ROLE, DEPLOY_ACTION_ROLE, LOOKUP_ROLE } from './util'; // import { Repository } from 'aws-cdk-lib/aws-ecr'; // import { Bucket } from 'aws-cdk-lib/aws-s3'; -const CFN_CONTEXT = { - 'AWS::Region': 'the_region', - 'AWS::AccountId': 'the_account', - 'AWS::URLSuffix': 'domain.aws', -}; -const APP_ID = 'appId'; -const CLOUDFORMATION_EXECUTION_ROLE = 'role'; -const DEPLOY_ACTION_ROLE = 'role'; -const LOOKUP_ROLE = 'role'; - describe(AppStagingSynthesizer, () => { let app: App; let stack: Stack; @@ -352,145 +343,6 @@ describe(AppStagingSynthesizer, () => { } }); -describe('Boostrap Roles', () => { - test('Can supply existing arns for bootstrapped roles', () => { - // GIVEN - const app = new App({ - defaultStackSynthesizer: AppStagingSynthesizer.stackPerEnv({ - appId: APP_ID, - bootstrapRoles: { - cloudFormationExecutionRole: BootstrapRole.fromRoleArn(CLOUDFORMATION_EXECUTION_ROLE), - lookupRole: BootstrapRole.fromRoleArn(LOOKUP_ROLE), - deploymentActionRole: BootstrapRole.fromRoleArn(DEPLOY_ACTION_ROLE), - }, - }), - }); - const stack = new Stack(app, 'Stack', { - env: { - account: '000000000000', - region: 'us-east-1', - }, - }); - new CfnResource(stack, 'Resource', { - type: 'Some::Resource', - }); - - // WHEN - const asm = app.synth(); - - // THEN - const stackArtifact = asm.getStackArtifact('Stack'); - - // Bootstrapped roles are as advertised - expect(stackArtifact.cloudFormationExecutionRoleArn).toEqual(CLOUDFORMATION_EXECUTION_ROLE); - expect(stackArtifact.lookupRole).toEqual({ arn: LOOKUP_ROLE }); - expect(stackArtifact.assumeRoleArn).toEqual(DEPLOY_ACTION_ROLE); - }); - - test('can supply existing arns for staging roles', () => { - // GIVEN - const app = new App({ - defaultStackSynthesizer: AppStagingSynthesizer.stackPerEnv({ - appId: APP_ID, - stagingRoles: { - fileAssetPublishingRole: BootstrapRole.fromRoleArn('arn'), - }, - }), - }); - const stack = new Stack(app, 'Stack', { - env: { - account: '000000000000', - region: 'us-east-1', - }, - }); - new CfnResource(stack, 'Resource', { - type: 'Some::Resource', - }); - - // WHEN - const asm = app.synth(); - - // THEN - // Staging roles are as advertised - const manifestArtifact = asm.artifacts.filter(isAssetManifest)[0]; - expect(manifestArtifact).toBeDefined(); - const manifest: cxschema.AssetManifest = JSON.parse(fs.readFileSync(manifestArtifact.file, { encoding: 'utf-8' })); - const firstFile: any = (manifest.files ? manifest.files[Object.keys(manifest.files)[0]] : undefined) ?? {}; - expect(firstFile.destinations['000000000000-us-east-1'].assumeRoleArn).toEqual('arn'); - }); - - test('bootstrap roles can be specified as current cli credentials instead', () => { - // GIVEN - const app = new App({ - defaultStackSynthesizer: AppStagingSynthesizer.stackPerEnv({ - appId: APP_ID, - bootstrapRoles: { - cloudFormationExecutionRole: BootstrapRole.cliCredentials(), - lookupRole: BootstrapRole.cliCredentials(), - deploymentActionRole: BootstrapRole.cliCredentials(), - }, - }), - }); - const stack = new Stack(app, 'Stack', { - env: { - account: '000000000000', - region: 'us-east-1', - }, - }); - new CfnResource(stack, 'Resource', { - type: 'Some::Resource', - }); - - // WHEN - const asm = app.synth(); - - // THEN - const stackArtifact = asm.getStackArtifact('Stack'); - - // Bootstrapped roles are undefined, which means current credentials are used - expect(stackArtifact.cloudFormationExecutionRoleArn).toBeUndefined(); - expect(stackArtifact.lookupRole).toBeUndefined(); - expect(stackArtifact.assumeRoleArn).toBeUndefined(); - }); - - test('staging roles cannot be specified as cli credentials', () => { - const app = new App({ - defaultStackSynthesizer: AppStagingSynthesizer.stackPerEnv({ - appId: APP_ID, - stagingRoles: { - fileAssetPublishingRole: BootstrapRole.cliCredentials(), - }, - }), - }); - - expect(() => new Stack(app, 'Stack')).toThrowError('fileAssetPublishingRole and dockerAssetPublishingRole cannot be specified as cliCredentials(). Please supply an arn to reference an existing IAM role.'); - }); - - test('qualifier is resolved in the synthesizer', () => { - const app = new App({ - defaultStackSynthesizer: AppStagingSynthesizer.stackPerEnv({ - qualifier: 'abcdef', - appId: APP_ID, - }), - }); - new Stack(app, 'Stack', { - env: { - account: '000000000000', - region: 'us-east-1', - }, - }); - - // WHEN - const asm = app.synth(); - - // THEN - const stackArtifact = asm.getStackArtifact('Stack'); - - // Bootstrapped role's asset manifest tokens are resolved, where possible - expect(stackArtifact.cloudFormationExecutionRoleArn).toEqual('arn:${AWS::Partition}:iam::000000000000:role/cdk-abcdef-cfn-exec-role-000000000000-us-east-1'); - }); -}); - function isAssetManifest(x: cxapi.CloudArtifact): x is cxapi.AssetManifestArtifact { return x instanceof cxapi.AssetManifestArtifact; } diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts new file mode 100644 index 0000000000000..2d1f32d15a491 --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts @@ -0,0 +1,145 @@ +import { App, Stack, CfnResource } from 'aws-cdk-lib'; +import { isAssetManifest } from 'aws-cdk-lib/pipelines/lib/private/cloud-assembly-internals'; +import { AppStagingSynthesizer, BootstrapRole } from '../lib'; +import { APP_ID, CLOUDFORMATION_EXECUTION_ROLE, DEPLOY_ACTION_ROLE, LOOKUP_ROLE } from './util'; +import * as cxschema from 'aws-cdk-lib/cloud-assembly-schema'; +import * as fs from 'fs'; + +describe('Boostrap Roles', () => { + test('Can supply existing arns for bootstrapped roles', () => { + // GIVEN + const app = new App({ + defaultStackSynthesizer: AppStagingSynthesizer.stackPerEnv({ + appId: APP_ID, + bootstrapRoles: { + cloudFormationExecutionRole: BootstrapRole.fromRoleArn(CLOUDFORMATION_EXECUTION_ROLE), + lookupRole: BootstrapRole.fromRoleArn(LOOKUP_ROLE), + deploymentActionRole: BootstrapRole.fromRoleArn(DEPLOY_ACTION_ROLE), + }, + }), + }); + const stack = new Stack(app, 'Stack', { + env: { + account: '000000000000', + region: 'us-east-1', + }, + }); + new CfnResource(stack, 'Resource', { + type: 'Some::Resource', + }); + + // WHEN + const asm = app.synth(); + + // THEN + const stackArtifact = asm.getStackArtifact('Stack'); + + // Bootstrapped roles are as advertised + expect(stackArtifact.cloudFormationExecutionRoleArn).toEqual(CLOUDFORMATION_EXECUTION_ROLE); + expect(stackArtifact.lookupRole).toEqual({ arn: LOOKUP_ROLE }); + expect(stackArtifact.assumeRoleArn).toEqual(DEPLOY_ACTION_ROLE); + }); + + test('can supply existing arns for staging roles', () => { + // GIVEN + const app = new App({ + defaultStackSynthesizer: AppStagingSynthesizer.stackPerEnv({ + appId: APP_ID, + stagingRoles: { + fileAssetPublishingRole: BootstrapRole.fromRoleArn('arn'), + }, + }), + }); + const stack = new Stack(app, 'Stack', { + env: { + account: '000000000000', + region: 'us-east-1', + }, + }); + new CfnResource(stack, 'Resource', { + type: 'Some::Resource', + }); + + // WHEN + const asm = app.synth(); + + // THEN + // Staging roles are as advertised + const manifestArtifact = asm.artifacts.filter(isAssetManifest)[0]; + expect(manifestArtifact).toBeDefined(); + const manifest: cxschema.AssetManifest = JSON.parse(fs.readFileSync(manifestArtifact.file, { encoding: 'utf-8' })); + const firstFile: any = (manifest.files ? manifest.files[Object.keys(manifest.files)[0]] : undefined) ?? {}; + expect(firstFile.destinations['000000000000-us-east-1'].assumeRoleArn).toEqual('arn'); + }); + + test('bootstrap roles can be specified as current cli credentials instead', () => { + // GIVEN + const app = new App({ + defaultStackSynthesizer: AppStagingSynthesizer.stackPerEnv({ + appId: APP_ID, + bootstrapRoles: { + cloudFormationExecutionRole: BootstrapRole.cliCredentials(), + lookupRole: BootstrapRole.cliCredentials(), + deploymentActionRole: BootstrapRole.cliCredentials(), + }, + }), + }); + const stack = new Stack(app, 'Stack', { + env: { + account: '000000000000', + region: 'us-east-1', + }, + }); + new CfnResource(stack, 'Resource', { + type: 'Some::Resource', + }); + + // WHEN + const asm = app.synth(); + + // THEN + const stackArtifact = asm.getStackArtifact('Stack'); + + // Bootstrapped roles are undefined, which means current credentials are used + expect(stackArtifact.cloudFormationExecutionRoleArn).toBeUndefined(); + expect(stackArtifact.lookupRole).toBeUndefined(); + expect(stackArtifact.assumeRoleArn).toBeUndefined(); + }); + + test('staging roles cannot be specified as cli credentials', () => { + const app = new App({ + defaultStackSynthesizer: AppStagingSynthesizer.stackPerEnv({ + appId: APP_ID, + stagingRoles: { + fileAssetPublishingRole: BootstrapRole.cliCredentials(), + }, + }), + }); + + expect(() => new Stack(app, 'Stack')).toThrowError('fileAssetPublishingRole and dockerAssetPublishingRole cannot be specified as cliCredentials(). Please supply an arn to reference an existing IAM role.'); + }); + + test('qualifier is resolved in the synthesizer', () => { + const app = new App({ + defaultStackSynthesizer: AppStagingSynthesizer.stackPerEnv({ + qualifier: 'abcdef', + appId: APP_ID, + }), + }); + new Stack(app, 'Stack', { + env: { + account: '000000000000', + region: 'us-east-1', + }, + }); + + // WHEN + const asm = app.synth(); + + // THEN + const stackArtifact = asm.getStackArtifact('Stack'); + + // Bootstrapped role's asset manifest tokens are resolved, where possible + expect(stackArtifact.cloudFormationExecutionRoleArn).toEqual('arn:${AWS::Partition}:iam::000000000000:role/cdk-abcdef-cfn-exec-role-000000000000-us-east-1'); + }); +}); \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/util.ts b/packages/@aws-cdk/app-staging-synthesizer/test/util.ts new file mode 100644 index 0000000000000..09cc34807d9e8 --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/util.ts @@ -0,0 +1,34 @@ +import { StackPerEnvProps, AppStagingSynthesizer, BootstrapRole } from '../lib'; +import * as cxapi from 'aws-cdk-lib/cx-api'; + +export const CFN_CONTEXT = { + 'AWS::Region': 'the_region', + 'AWS::AccountId': 'the_account', + 'AWS::URLSuffix': 'domain.aws', +}; +export const APP_ID = 'appId'; +export const CLOUDFORMATION_EXECUTION_ROLE = 'role'; +export const DEPLOY_ACTION_ROLE = 'role'; +export const LOOKUP_ROLE = 'role'; + +export function isAssetManifest(x: cxapi.CloudArtifact): x is cxapi.AssetManifestArtifact { + return x instanceof cxapi.AssetManifestArtifact; +} + +export function last(xs?: A[]): A | undefined { + return xs ? xs[xs.length - 1] : undefined; +} + +export class TestAppScopedStagingSynthesizer { + public static stackPerEnv(props: Partial = {}): AppStagingSynthesizer { + return AppStagingSynthesizer.stackPerEnv({ + appId: props.appId ?? APP_ID, + bootstrapRoles: { + cloudFormationExecutionRole: BootstrapRole.fromRoleArn(CLOUDFORMATION_EXECUTION_ROLE), + deploymentActionRole: BootstrapRole.fromRoleArn(DEPLOY_ACTION_ROLE), + lookupRole: BootstrapRole.fromRoleArn(LOOKUP_ROLE), + }, + ...props, + }); + } +} \ No newline at end of file From 8a12d60163e35a108e0cdf38734f7ed5239fea92 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Mon, 10 Apr 2023 16:10:51 -0400 Subject: [PATCH 063/120] more test stuff --- .../test/app-staging-synthesizer.test.ts | 27 ++----------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts index 8e5131fee82f8..c76b07e6a88c0 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts @@ -3,10 +3,9 @@ import * as fs from 'fs'; import { App, Stack, CfnResource, FileAssetPackaging, Token, Lazy } from 'aws-cdk-lib'; import * as cxschema from 'aws-cdk-lib/cloud-assembly-schema'; import { evaluateCFN } from 'aws-cdk-lib/core/test/evaluate-cfn'; -import * as cxapi from 'aws-cdk-lib/cx-api'; -import { AppStagingSynthesizer, BootstrapRole, StackPerEnvProps } from '../lib'; +import { AppStagingSynthesizer } from '../lib'; import { Template, Match } from 'aws-cdk-lib/assertions'; -import { APP_ID, CFN_CONTEXT, CLOUDFORMATION_EXECUTION_ROLE, DEPLOY_ACTION_ROLE, LOOKUP_ROLE } from './util'; +import { APP_ID, CFN_CONTEXT, TestAppScopedStagingSynthesizer, isAssetManifest, last } from './util'; // import { Repository } from 'aws-cdk-lib/aws-ecr'; // import { Bucket } from 'aws-cdk-lib/aws-s3'; @@ -342,25 +341,3 @@ describe(AppStagingSynthesizer, () => { return evaluateCFN(stack.resolve(value), CFN_CONTEXT); } }); - -function isAssetManifest(x: cxapi.CloudArtifact): x is cxapi.AssetManifestArtifact { - return x instanceof cxapi.AssetManifestArtifact; -} - -function last(xs?: A[]): A | undefined { - return xs ? xs[xs.length - 1] : undefined; -} - -class TestAppScopedStagingSynthesizer { - public static stackPerEnv(props: Partial = {}): AppStagingSynthesizer { - return AppStagingSynthesizer.stackPerEnv({ - appId: props.appId ?? APP_ID, - bootstrapRoles: { - cloudFormationExecutionRole: BootstrapRole.fromRoleArn(CLOUDFORMATION_EXECUTION_ROLE), - deploymentActionRole: BootstrapRole.fromRoleArn(DEPLOY_ACTION_ROLE), - lookupRole: BootstrapRole.fromRoleArn(LOOKUP_ROLE), - }, - ...props, - }); - } -} \ No newline at end of file From 745770ff92a99c7ba1bb0e7f719d23397a8beece Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Mon, 10 Apr 2023 16:38:02 -0400 Subject: [PATCH 064/120] finish s3 lifecycle rule tests --- .../lib/app-staging-synthesizer.ts | 17 +++++++ .../lib/default-staging-stack.ts | 2 +- .../test/app-staging-synthesizer.test.ts | 50 ++++++++++++++++--- 3 files changed, 61 insertions(+), 8 deletions(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts index 2c64d52fd6c8b..8f377f6817d6b 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts @@ -17,6 +17,7 @@ import { StringSpecializer } from 'aws-cdk-lib/core/lib/helpers-internal'; import * as cxapi from 'aws-cdk-lib/cx-api'; import { BootstrapRole, BootstrapRoles, StagingRoles } from './bootstrap-roles'; import { IStagingStack as IStagingStack, DefaultStagingStack } from './default-staging-stack'; +import * as s3 from 'aws-cdk-lib/aws-s3'; /** * @internal @@ -54,7 +55,22 @@ export interface StackPerEnvProps { */ readonly qualifier?: string; + /** + * Retain all assets in the s3 bucket, even the ones that have been + * marked ephemeral. + * + * @default false + */ readonly retainEphemeralFileAssets?: boolean; + + /** + * Specify a custom lifecycle rule for ephemeral file assets. If you + * specify this property, you must set `prefix: 'eph-'` as part of the rule. + * This is the only way to identify ephemeral assets. + * + * @default - ephemeral assets will be deleted after 10 days + */ + readonly ephemeralFileAssetLifecycleRule?: s3.LifecycleRule; } /** @@ -200,6 +216,7 @@ export class AppStagingSynthesizer extends StackSynthesizer implements IReusable imageAssetPublishingRole: props.stagingRoles?.dockerAssetPublishingRole, deployActionRoleArn, retainEphemeralFileAssets: props.retainEphemeralFileAssets, + ephemeralFileAssetLifecycleRule: props.ephemeralFileAssetLifecycleRule, }); boundStack.addDependency(stagingStack.dependencyStack, 'stack depends on the staging stack for staging resources'); diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts index 52119e31d52e5..347b19a28aa2c 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts @@ -213,7 +213,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { private validateEphemeralAssetLifecycleRule(rule?: s3.LifecycleRule) { if (!rule) { return rule; } if (rule.prefix !== EPHEMERAL_PREFIX) { - throw new Error(`ephemeralAssetLifecycleRule must contain "prefix: '${EPHEMERAL_PREFIX}'" but got 'prefix: ${rule.prefix}. This prefix is the only way to identify ephemeral assets.`); + throw new Error(`ephemeralAssetLifecycleRule must contain "prefix: '${EPHEMERAL_PREFIX}'" but got "prefix: ${rule.prefix}". This prefix is the only way to identify ephemeral assets.`); } return rule; } diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts index c76b07e6a88c0..e893dde7dae9d 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts @@ -1,6 +1,6 @@ /* eslint-disable jest/no-commented-out-tests */ import * as fs from 'fs'; -import { App, Stack, CfnResource, FileAssetPackaging, Token, Lazy } from 'aws-cdk-lib'; +import { App, Stack, CfnResource, FileAssetPackaging, Token, Lazy, Duration } from 'aws-cdk-lib'; import * as cxschema from 'aws-cdk-lib/cloud-assembly-schema'; import { evaluateCFN } from 'aws-cdk-lib/core/test/evaluate-cfn'; import { AppStagingSynthesizer } from '../lib'; @@ -193,6 +193,21 @@ describe(AppStagingSynthesizer, () => { test('lifecycle rule on ephemeral assets can be customized', () => { // GIVEN + app = new App({ + defaultStackSynthesizer: TestAppScopedStagingSynthesizer.stackPerEnv({ + ephemeralFileAssetLifecycleRule: { + prefix: 'eph-', + objectSizeGreaterThan: 10000, + expiration: Duration.days(1), + }, + }), + }); + stack = new Stack(app, 'Stack', { + env: { + account: '000000000000', + region: 'us-west-2', + }, + }); new CfnResource(stack, 'Resource', { type: 'Some::Resource', }); @@ -201,38 +216,59 @@ describe(AppStagingSynthesizer, () => { const asm = app.synth(); // THEN - const stagingStackArtifact = asm.getStackArtifact('StagingStack000000000000us-east-1'); + const stagingStackArtifact = asm.getStackArtifact('StagingStack000000000000us-west-2'); Template.fromJSON(stagingStackArtifact.template).hasResourceProperties('AWS::S3::Bucket', { LifecycleConfiguration: { Rules: [{ - ExpirationInDays: 10, + ExpirationInDays: 1, Prefix: 'eph-', Status: 'Enabled', + ObjectSizeGreaterThan: 10000, }], }, }); }); + test('customized lifecycle rule must have correct prefix', () => { + // GIVEN + app = new App({ + defaultStackSynthesizer: TestAppScopedStagingSynthesizer.stackPerEnv({ + ephemeralFileAssetLifecycleRule: { + objectSizeGreaterThan: 10000, + expiration: Duration.days(1), + }, + }), + }); + expect(() => { + new Stack(app, 'Stack', { + env: { + account: '000000000000', + region: 'us-west-2', + }, + }); + }).toThrowError('ephemeralAssetLifecycleRule must contain "prefix: \'eph-\'" but got "prefix: undefined". This prefix is the only way to identify ephemeral assets.'); + }); + test('lifecycle rule on ephemeral assets can be turned off', () => { // GIVEN - const app2 = new App({ + app = new App({ defaultStackSynthesizer: TestAppScopedStagingSynthesizer.stackPerEnv({ retainEphemeralFileAssets: true, }), }); - const stack2 = new Stack(app2, 'Stack', { + stack = new Stack(app, 'Stack', { env: { account: '000000000000', region: 'us-west-2', }, }); - new CfnResource(stack2, 'Resource', { + new CfnResource(stack, 'Resource', { type: 'Some::Resource', }); // WHEN - const asm = app2.synth(); + const asm = app.synth(); // THEN const stagingStackArtifact = asm.getStackArtifact('StagingStack000000000000us-west-2'); From 5db8d0a516bdc793644c77c725e17103abe2833b Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Mon, 10 Apr 2023 17:54:45 -0400 Subject: [PATCH 065/120] expose bucket prefix api --- .../app-staging-synthesizer/README.md | 37 +++++++++++++ .../lib/app-staging-synthesizer.ts | 9 +++- .../test/app-staging-synthesizer.test.ts | 53 ++++++++++++++++++- .../aws-cdk-lib/aws-s3-assets/lib/asset.ts | 15 ++++++ 4 files changed, 112 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer/README.md b/packages/@aws-cdk/app-staging-synthesizer/README.md index 3a6ba7bca4ab3..a8cdd41518f63 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/README.md +++ b/packages/@aws-cdk/app-staging-synthesizer/README.md @@ -198,3 +198,40 @@ const app = new App({ }), }); ``` + +### Ephemeral Assets + +Some assets that get put into the staging S3 Bucket are ephemeral - they are only necessary +during CloudFormation deployment and not after. As long as you know what assets are ephemeral, +you can tag them as such and they will be marked with an `eph-` prefix when they are staged. +This allows configuration of a lifecycle rule specifically for ephemeral assets. + +A good example is a Lambda Function asset. The asset is only useful in the S3 Bucket at deploy +time, because the source code gets copied into Lambda itself. So we can mark Lambda assets +as ephemeral: + +```ts +new lambda.Function(stack, 'lambda', { + code: lambda.AssetCode.fromAsset(path.join(__dirname, 'assets'), { + ephemeral: true, + }), + handler: 'index.handler', + runtime: lambda.Runtime.PYTHON_3_9, +}); +``` + +This means that the asset will go into the S3 Bucket with the prefix `eph-`. It will also be +subject to the lifecycle rule set on ephemeral assets. By default, the rule is `expiration: Duration.days(10)`. +You can specify your own rule like this: + +```ts +const app = new App({ + defaultStackSynthesizer: TestAppScopedStagingSynthesizer.stackPerEnv({ + ephemeralFileAssetLifecycleRule: { + prefix: 'eph-', // required + objectSizeGreaterThan: 10000, + expiration: Duration.days(1), + }, + }), +}); +``` \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts index 8f377f6817d6b..e9e1c733f1767 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts @@ -55,6 +55,8 @@ export interface StackPerEnvProps { */ readonly qualifier?: string; + readonly bucketPrefix?: string; + /** * Retain all assets in the s3 bucket, even the ones that have been * marked ephemeral. @@ -128,6 +130,8 @@ interface AppStagingSynthesizerProps { * @default - Value of context key '@aws-cdk/core:bootstrapQualifier' if set, otherwise `DEFAULT_QUALIFIER` */ readonly qualifier?: string; + + readonly bucketPrefix?: string; } /** @@ -160,6 +164,7 @@ export class AppStagingSynthesizer extends StackSynthesizer implements IReusable return new AppStagingSynthesizer({ qualifier: props.qualifier, bootstrapRoles: props.bootstrapRoles, + bucketPrefix: props.bucketPrefix, stagingStackFactory: { stagingStackFactory(boundStack: Stack) { const app = App.of(boundStack); @@ -317,11 +322,13 @@ class BoundAppStagingSynthesizer extends StackSynthesizer implements IBoundAppSt private readonly cloudFormationExecutionRoleArn?: string; private readonly deploymentActionRoleArn?: string; private readonly qualifier: string; + private readonly bucketPrefix?: string; constructor(stack: Stack, props: AppStagingSynthesizerProps) { super(); super.bind(stack); + this.bucketPrefix = props.bucketPrefix; this.qualifier = props.qualifier ?? stack.node.tryGetContext(BOOTSTRAP_QUALIFIER_CONTEXT) ?? BoundAppStagingSynthesizer.DEFAULT_QUALIFIER; const spec = new StringSpecializer(stack, this.qualifier); @@ -371,7 +378,7 @@ class BoundAppStagingSynthesizer extends StackSynthesizer implements IBoundAppSt const { bucketName, assumeRoleArn } = this.stagingStack.addFile(asset); const location = this.assetManifest.defaultAddFileAsset(this.boundStack, asset, { bucketName: bucketName, - bucketPrefix: asset.ephemeral ? EPHEMERAL_PREFIX : undefined, + bucketPrefix: asset.ephemeral ? EPHEMERAL_PREFIX : this.bucketPrefix, role: { assumeRoleArn, }, diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts index e893dde7dae9d..02be2972df4d7 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts @@ -154,8 +154,59 @@ describe(AppStagingSynthesizer, () => { expect(evalCFN(location1.bucketName)).toEqual(evalCFN(location2.bucketName)); }); + test('can configure bucket prefix', () => { + // GIVEN + app = new App({ + defaultStackSynthesizer: TestAppScopedStagingSynthesizer.stackPerEnv({ + bucketPrefix: 'prefix', + }), + }); + stack = new Stack(app, 'Stack', { + env: { + account: '000000000000', + region: 'us-west-2', + }, + }); + + // WHEN + const location = stack.synthesizer.addFileAsset({ + fileName: __filename, + packaging: FileAssetPackaging.FILE, + sourceHash: 'abcdef', + }); + + // THEN - assets have the same location + expect(evalCFN(location.objectKey)).toEqual('prefixabcdef.js'); + }); + describe('ephemeral assets', () => { - test('ephemeral assets have the \'eph\' prefix', () => { + test('ephemeral assets have the \'eph-\' prefix', () => { + // WHEN + const location = stack.synthesizer.addFileAsset({ + fileName: __filename, + packaging: FileAssetPackaging.FILE, + sourceHash: 'abcdef', + ephemeral: true, + }); + + // THEN - asset has bucket prefix + expect(evalCFN(location.objectKey)).toEqual('eph-abcdef.js'); + }); + + test('ephemeral assets do not get specified bucketPrefix', () => { + // GIVEN + app = new App({ + defaultStackSynthesizer: TestAppScopedStagingSynthesizer.stackPerEnv({ + bucketPrefix: 'prefix', + }), + }); + stack = new Stack(app, 'Stack', { + env: { + account: '000000000000', + region: 'us-west-2', + }, + }); + // WHEN const location = stack.synthesizer.addFileAsset({ fileName: __filename, diff --git a/packages/aws-cdk-lib/aws-s3-assets/lib/asset.ts b/packages/aws-cdk-lib/aws-s3-assets/lib/asset.ts index 0019e72d694ca..5a0149ab0cfd7 100644 --- a/packages/aws-cdk-lib/aws-s3-assets/lib/asset.ts +++ b/packages/aws-cdk-lib/aws-s3-assets/lib/asset.ts @@ -34,6 +34,20 @@ export interface AssetOptions extends CopyOptions, cdk.FileCopyOptions, cdk.Asse * @deprecated see `assetHash` and `assetHashType` */ readonly sourceHash?: string; + + /** + * Whether or not the asset is ephemeral; i.e. only used during deployment + * and not needed afterwards. Setting this property to true has an impact + * on the lifecycle of the asset, because we will assume that it is safe to + * delete after the CloudFormation deployment succeeds. + * + * For example, Lambda Function assets are copied over to Lambda during + * deployment. Therefore, it is not necessary to store the asset in S3, so + * we consider those assets ephemeral. + * + * @default false + */ + readonly ephemeral?: boolean; } export interface AssetProps extends AssetOptions { @@ -147,6 +161,7 @@ export class Asset extends Construct implements cdk.IAsset { packaging: staging.packaging, sourceHash: this.sourceHash, fileName: this.assetPath, + ephemeral: props.ephemeral, }); this.s3BucketName = location.bucketName; From 065e8d483a94e9c202344e7e3dd9a811d1e3be7b Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Tue, 11 Apr 2023 16:55:41 +0200 Subject: [PATCH 066/120] Fix build --- packages/aws-cdk-lib/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/package.json b/packages/aws-cdk-lib/package.json index edc062482677d..c2f5a94721267 100644 --- a/packages/aws-cdk-lib/package.json +++ b/packages/aws-cdk-lib/package.json @@ -50,7 +50,7 @@ "ts-node ./scripts/verify-imports-shielded.ts" ], "pre": [ - "npx ts-node region-info/build-tools/generate-static-data.ts", + "npx ts-node --preferTsExts region-info/build-tools/generate-static-data.ts", "node aws-events-targets/build-tools/gen.js", "(cp -f $(node -p 'require.resolve(\"aws-sdk/apis/metadata.json\")') custom-resources/lib/aws-custom-resource/sdk-api-metadata.json && rm -rf custom-resources/test/aws-custom-resource/cdk.out)", "(rm -rf core/test/fs/fixtures && cd core/test/fs && tar -xzf fixtures.tar.gz)", From 3d83f4a9614c122ccac27f5f1caa0aa232f48856 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Tue, 11 Apr 2023 15:58:29 -0400 Subject: [PATCH 067/120] env-agnostic test --- .../app-staging-synthesizer/README.md | 2 +- .../lib/app-staging-synthesizer.ts | 4 +- .../lib/default-staging-stack.ts | 2 +- .../StagingStack.template.json | 332 +++++++++ .../app-scoped-staging-test.assets.json | 32 + .../app-scoped-staging-test.template.json | 57 ++ .../index.py | 1 + .../cdk.out | 1 + .../integ.json | 12 + ...efaultTestDeployAssert44C8D370.assets.json | 19 + ...aultTestDeployAssert44C8D370.template.json | 36 + .../manifest.json | 150 ++++ .../tree.json | 655 ++++++++++++++++++ .../test/integ.env-agnostic-synth.ts | 27 + .../helpers-internal/string-specializer.ts | 8 +- 15 files changed, 1330 insertions(+), 8 deletions(-) create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/StagingStack.template.json create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/app-scoped-staging-test.assets.json create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/app-scoped-staging-test.template.json create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53/index.py create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/integ.json create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.template.json create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/tree.json create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/README.md b/packages/@aws-cdk/app-staging-synthesizer/README.md index a8cdd41518f63..98542ccbefc5b 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/README.md +++ b/packages/@aws-cdk/app-staging-synthesizer/README.md @@ -234,4 +234,4 @@ const app = new App({ }, }), }); -``` \ No newline at end of file +``` diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts index e9e1c733f1767..d764429779284 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts @@ -13,7 +13,7 @@ import { StackSynthesizer, Token, } from 'aws-cdk-lib'; -import { StringSpecializer } from 'aws-cdk-lib/core/lib/helpers-internal'; +import { StringSpecializer, translateCfnTokenToAssetToken } from 'aws-cdk-lib/core/lib/helpers-internal'; import * as cxapi from 'aws-cdk-lib/cx-api'; import { BootstrapRole, BootstrapRoles, StagingRoles } from './bootstrap-roles'; import { IStagingStack as IStagingStack, DefaultStagingStack } from './default-staging-stack'; @@ -377,7 +377,7 @@ class BoundAppStagingSynthesizer extends StackSynthesizer implements IBoundAppSt public addFileAsset(asset: FileAssetSource): FileAssetLocation { const { bucketName, assumeRoleArn } = this.stagingStack.addFile(asset); const location = this.assetManifest.defaultAddFileAsset(this.boundStack, asset, { - bucketName: bucketName, + bucketName: translateCfnTokenToAssetToken(bucketName), bucketPrefix: asset.ephemeral ? EPHEMERAL_PREFIX : this.bucketPrefix, role: { assumeRoleArn, diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts index 347b19a28aa2c..411948195d52f 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts @@ -288,7 +288,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { private createBucketKey(): kms.IKey { const bucketKeyId = 'BucketKey'; const key = this.node.tryFindChild(bucketKeyId) as kms.IKey ?? new kms.Key(this, bucketKeyId, { - alias: `CdkStagingBucketKey${this.account}-${this.region}-${this.appId}`, + alias: `alias/cdkstagingkey/${this.account}-${this.region}-${this.appId}`, admins: [new iam.AccountPrincipal(this.account)], }); return key; diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/StagingStack.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/StagingStack.template.json new file mode 100644 index 0000000000000..0da064542394a --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/StagingStack.template.json @@ -0,0 +1,332 @@ +{ + "Resources": { + "CdkFilePublishingRole119E2944": { + "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" + }, + "RoleName": { + "Fn::Join": [ + "", + [ + "cdk-file-publishing-role-", + { + "Ref": "AWS::Region" + }, + "-envAgnostic" + ] + ] + } + } + }, + "CdkFilePublishingRoleDefaultPolicyE914275E": { + "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": [ + "CdkStagingBucket1636058C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "CdkStagingBucket1636058C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "kms:Decrypt", + "kms:DescribeKey", + "kms:Encrypt", + "kms:GenerateDataKey*", + "kms:ReEncrypt*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "BucketKey7092080A", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "CdkFilePublishingRoleDefaultPolicyE914275E", + "Roles": [ + { + "Ref": "CdkFilePublishingRole119E2944" + } + ] + } + }, + "BucketKey7092080A": { + "Type": "AWS::KMS::Key", + "Properties": { + "KeyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": [ + "kms:CancelKeyDeletion", + "kms:Create*", + "kms:Delete*", + "kms:Describe*", + "kms:Disable*", + "kms:Enable*", + "kms:Get*", + "kms:List*", + "kms:Put*", + "kms:Revoke*", + "kms:ScheduleKeyDeletion", + "kms:TagResource", + "kms:UntagResource", + "kms:Update*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "BucketKeyAlias69A0886F": { + "Type": "AWS::KMS::Alias", + "Properties": { + "AliasName": { + "Fn::Join": [ + "", + [ + "alias/cdkstagingkey/", + { + "Ref": "AWS::AccountId" + }, + "-", + { + "Ref": "AWS::Region" + }, + "-envAgnostic" + ] + ] + }, + "TargetKeyId": { + "Fn::GetAtt": [ + "BucketKey7092080A", + "Arn" + ] + } + } + }, + "CdkStagingBucket1636058C": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "KMSMasterKeyID": { + "Fn::GetAtt": [ + "BucketKey7092080A", + "Arn" + ] + }, + "SSEAlgorithm": "aws:kms" + } + } + ] + }, + "BucketName": { + "Fn::Join": [ + "", + [ + "cdk-", + { + "Ref": "AWS::AccountId" + }, + "-", + { + "Ref": "AWS::Region" + }, + "-envagnostic" + ] + ] + }, + "LifecycleConfiguration": { + "Rules": [ + { + "ExpirationInDays": 10, + "Prefix": "eph-", + "Status": "Enabled" + } + ] + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "CdkStagingBucketPolicy42BD1F92": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "CdkStagingBucket1636058C" + }, + "PolicyDocument": { + "Statement": [ + { + "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": [ + "CdkStagingBucket1636058C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "CdkStagingBucket1636058C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/app-scoped-staging-test.assets.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/app-scoped-staging-test.assets.json new file mode 100644 index 0000000000000..5eb8915a33b5d --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/app-scoped-staging-test.assets.json @@ -0,0 +1,32 @@ +{ + "version": "31.0.0", + "files": { + "47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53": { + "source": { + "path": "asset.47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53", + "packaging": "zip" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-${AWS::AccountId}-${AWS::Region}-envagnostic", + "objectKey": "eph-47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53.zip", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${Token[AWS.AccountId.6]}:role/cdk-file-publishing-role-${Token[AWS.Region.10]}-envAgnostic" + } + } + }, + "c4ae2b1e3d91cf74a8a9afe9c8fdf04a9d878a81be39798ff35dc15b5e8c9cce": { + "source": { + "path": "app-scoped-staging-test.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-${AWS::AccountId}-${AWS::Region}-envagnostic", + "objectKey": "c4ae2b1e3d91cf74a8a9afe9c8fdf04a9d878a81be39798ff35dc15b5e8c9cce.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${Token[AWS.AccountId.6]}:role/cdk-file-publishing-role-${Token[AWS.Region.10]}-envAgnostic" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/app-scoped-staging-test.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/app-scoped-staging-test.template.json new file mode 100644 index 0000000000000..bf50d57f35574 --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/app-scoped-staging-test.template.json @@ -0,0 +1,57 @@ +{ + "Resources": { + "lambdaServiceRole494E4CA6": { + "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" + ] + ] + } + ] + } + }, + "lambda8B5974B5": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-${AWS::AccountId}-${AWS::Region}-envagnostic" + }, + "S3Key": "eph-47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53.zip" + }, + "Role": { + "Fn::GetAtt": [ + "lambdaServiceRole494E4CA6", + "Arn" + ] + }, + "Handler": "index.handler", + "Runtime": "python3.9" + }, + "DependsOn": [ + "lambdaServiceRole494E4CA6" + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53/index.py b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53/index.py new file mode 100644 index 0000000000000..ed0f110e2e61e --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53/index.py @@ -0,0 +1 @@ +print('hello') \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/cdk.out b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/cdk.out new file mode 100644 index 0000000000000..7925065efbcc4 --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"31.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/integ.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/integ.json new file mode 100644 index 0000000000000..868928baf7ea2 --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "31.0.0", + "testCases": { + "integ-tests/DefaultTest": { + "stacks": [ + "app-scoped-staging-test" + ], + "assertionStack": "integ-tests/DefaultTest/DeployAssert", + "assertionStackName": "integtestsDefaultTestDeployAssert44C8D370" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json new file mode 100644 index 0000000000000..7526fee9ff76c --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json @@ -0,0 +1,19 @@ +{ + "version": "31.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "integtestsDefaultTestDeployAssert44C8D370.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/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.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/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/manifest.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/manifest.json new file mode 100644 index 0000000000000..078aa72dceb2a --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/manifest.json @@ -0,0 +1,150 @@ +{ + "version": "31.0.0", + "artifacts": { + "app-scoped-staging-test.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "app-scoped-staging-test.assets.json" + } + }, + "app-scoped-staging-test": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "app-scoped-staging-test.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "additionalDependencies": [ + "app-scoped-staging-test.assets" + ], + "stackTemplateAssetObjectUrl": "s3://cdk-${AWS::AccountId}-${AWS::Region}-envagnostic/c4ae2b1e3d91cf74a8a9afe9c8fdf04a9d878a81be39798ff35dc15b5e8c9cce.json", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}" + } + }, + "dependencies": [ + "StagingStack", + "app-scoped-staging-test.assets" + ], + "metadata": { + "/app-scoped-staging-test/lambda/ServiceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "lambdaServiceRole494E4CA6" + } + ], + "/app-scoped-staging-test/lambda/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "lambda8B5974B5" + } + ] + }, + "displayName": "app-scoped-staging-test" + }, + "StagingStack": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "StagingStack.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}", + "stackName": "StagingStackenvAgnostic" + }, + "metadata": { + "/StagingStack/CdkFilePublishingRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "CdkFilePublishingRole119E2944" + } + ], + "/StagingStack/CdkFilePublishingRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "CdkFilePublishingRoleDefaultPolicyE914275E" + } + ], + "/StagingStack/BucketKey/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "BucketKey7092080A" + } + ], + "/StagingStack/BucketKey/Alias/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "BucketKeyAlias69A0886F" + } + ], + "/StagingStack/CdkStagingBucket/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "CdkStagingBucket1636058C" + } + ], + "/StagingStack/CdkStagingBucket/Policy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "CdkStagingBucketPolicy42BD1F92" + } + ] + }, + "displayName": "StagingStack" + }, + "integtestsDefaultTestDeployAssert44C8D370.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "integtestsDefaultTestDeployAssert44C8D370.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "integtestsDefaultTestDeployAssert44C8D370": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "integtestsDefaultTestDeployAssert44C8D370.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": [ + "integtestsDefaultTestDeployAssert44C8D370.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": [ + "integtestsDefaultTestDeployAssert44C8D370.assets" + ], + "metadata": { + "/integ-tests/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/integ-tests/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "integ-tests/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/tree.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/tree.json new file mode 100644 index 0000000000000..1bcfe8e0686a0 --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/tree.json @@ -0,0 +1,655 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "app-scoped-staging-test": { + "id": "app-scoped-staging-test", + "path": "app-scoped-staging-test", + "children": { + "lambda": { + "id": "lambda", + "path": "app-scoped-staging-test/lambda", + "children": { + "ServiceRole": { + "id": "ServiceRole", + "path": "app-scoped-staging-test/lambda/ServiceRole", + "children": { + "ImportServiceRole": { + "id": "ImportServiceRole", + "path": "app-scoped-staging-test/lambda/ServiceRole/ImportServiceRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "app-scoped-staging-test/lambda/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-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Code": { + "id": "Code", + "path": "app-scoped-staging-test/lambda/Code", + "children": { + "Stage": { + "id": "Stage", + "path": "app-scoped-staging-test/lambda/Code/Stage", + "constructInfo": { + "fqn": "aws-cdk-lib.AssetStaging", + "version": "0.0.0" + } + }, + "AssetBucket": { + "id": "AssetBucket", + "path": "app-scoped-staging-test/lambda/Code/AssetBucket", + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.BucketBase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3_assets.Asset", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "app-scoped-staging-test/lambda/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Function", + "aws:cdk:cloudformation:props": { + "code": { + "s3Bucket": { + "Fn::Sub": "cdk-${AWS::AccountId}-${AWS::Region}-envagnostic" + }, + "s3Key": "eph-47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53.zip" + }, + "role": { + "Fn::GetAtt": [ + "lambdaServiceRole494E4CA6", + "Arn" + ] + }, + "handler": "index.handler", + "runtime": "python3.9" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.CfnFunction", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.Function", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "StagingStack": { + "id": "StagingStack", + "path": "StagingStack", + "children": { + "CdkFilePublishingRole": { + "id": "CdkFilePublishingRole", + "path": "StagingStack/CdkFilePublishingRole", + "children": { + "ImportCdkFilePublishingRole": { + "id": "ImportCdkFilePublishingRole", + "path": "StagingStack/CdkFilePublishingRole/ImportCdkFilePublishingRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "StagingStack/CdkFilePublishingRole/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" + }, + "roleName": { + "Fn::Join": [ + "", + [ + "cdk-file-publishing-role-", + { + "Ref": "AWS::Region" + }, + "-envAgnostic" + ] + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "StagingStack/CdkFilePublishingRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "StagingStack/CdkFilePublishingRole/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": [ + "CdkStagingBucket1636058C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "CdkStagingBucket1636058C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "kms:Decrypt", + "kms:DescribeKey", + "kms:Encrypt", + "kms:GenerateDataKey*", + "kms:ReEncrypt*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "BucketKey7092080A", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "CdkFilePublishingRoleDefaultPolicyE914275E", + "roles": [ + { + "Ref": "CdkFilePublishingRole119E2944" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "BucketKey": { + "id": "BucketKey", + "path": "StagingStack/BucketKey", + "children": { + "Resource": { + "id": "Resource", + "path": "StagingStack/BucketKey/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::KMS::Key", + "aws:cdk:cloudformation:props": { + "keyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": [ + "kms:CancelKeyDeletion", + "kms:Create*", + "kms:Delete*", + "kms:Describe*", + "kms:Disable*", + "kms:Enable*", + "kms:Get*", + "kms:List*", + "kms:Put*", + "kms:Revoke*", + "kms:ScheduleKeyDeletion", + "kms:TagResource", + "kms:UntagResource", + "kms:Update*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_kms.CfnKey", + "version": "0.0.0" + } + }, + "Alias": { + "id": "Alias", + "path": "StagingStack/BucketKey/Alias", + "children": { + "Resource": { + "id": "Resource", + "path": "StagingStack/BucketKey/Alias/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::KMS::Alias", + "aws:cdk:cloudformation:props": { + "aliasName": { + "Fn::Join": [ + "", + [ + "alias/cdkstagingkey/", + { + "Ref": "AWS::AccountId" + }, + "-", + { + "Ref": "AWS::Region" + }, + "-envAgnostic" + ] + ] + }, + "targetKeyId": { + "Fn::GetAtt": [ + "BucketKey7092080A", + "Arn" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_kms.CfnAlias", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_kms.Alias", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_kms.Key", + "version": "0.0.0" + } + }, + "CdkStagingBucket": { + "id": "CdkStagingBucket", + "path": "StagingStack/CdkStagingBucket", + "children": { + "Resource": { + "id": "Resource", + "path": "StagingStack/CdkStagingBucket/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::S3::Bucket", + "aws:cdk:cloudformation:props": { + "bucketEncryption": { + "serverSideEncryptionConfiguration": [ + { + "serverSideEncryptionByDefault": { + "sseAlgorithm": "aws:kms", + "kmsMasterKeyId": { + "Fn::GetAtt": [ + "BucketKey7092080A", + "Arn" + ] + } + } + } + ] + }, + "bucketName": { + "Fn::Join": [ + "", + [ + "cdk-", + { + "Ref": "AWS::AccountId" + }, + "-", + { + "Ref": "AWS::Region" + }, + "-envagnostic" + ] + ] + }, + "lifecycleConfiguration": { + "rules": [ + { + "expirationInDays": 10, + "prefix": "eph-", + "status": "Enabled" + } + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.CfnBucket", + "version": "0.0.0" + } + }, + "Policy": { + "id": "Policy", + "path": "StagingStack/CdkStagingBucket/Policy", + "children": { + "Resource": { + "id": "Resource", + "path": "StagingStack/CdkStagingBucket/Policy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::S3::BucketPolicy", + "aws:cdk:cloudformation:props": { + "bucket": { + "Ref": "CdkStagingBucket1636058C" + }, + "policyDocument": { + "Statement": [ + { + "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": [ + "CdkStagingBucket1636058C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "CdkStagingBucket1636058C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.CfnBucketPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.BucketPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.Bucket", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "integ-tests": { + "id": "integ-tests", + "path": "integ-tests", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "integ-tests/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "integ-tests/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "integ-tests/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "integ-tests/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "integ-tests/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.ts b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.ts new file mode 100644 index 0000000000000..b98dff075ebfa --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.ts @@ -0,0 +1,27 @@ +import * as path from 'path'; +import * as integ from '@aws-cdk/integ-tests-alpha'; +import { App, Stack } from 'aws-cdk-lib'; +import * as lambda from 'aws-cdk-lib/aws-lambda'; +import { AppStagingSynthesizer } from '../lib'; + +const app = new App(); + +const stack = new Stack(app, 'app-scoped-staging-test', { + synthesizer: AppStagingSynthesizer.stackPerEnv({ + appId: 'envAgnostic', + }), +}); + +new lambda.Function(stack, 'lambda', { + code: lambda.AssetCode.fromAsset(path.join(__dirname, 'assets'), { + ephemeral: true, + }), + handler: 'index.handler', + runtime: lambda.Runtime.PYTHON_3_9, +}); + +new integ.IntegTest(app, 'integ-tests', { + testCases: [stack], +}); + +app.synth(); diff --git a/packages/aws-cdk-lib/core/lib/helpers-internal/string-specializer.ts b/packages/aws-cdk-lib/core/lib/helpers-internal/string-specializer.ts index 30d5489eff020..4ae65313381f4 100644 --- a/packages/aws-cdk-lib/core/lib/helpers-internal/string-specializer.ts +++ b/packages/aws-cdk-lib/core/lib/helpers-internal/string-specializer.ts @@ -50,8 +50,8 @@ const ASSET_TOKENS = ['${AWS::Partition}', '${AWS::Region}', '${AWS::AccountId}' const CFN_TOKENS = [Aws.PARTITION, Aws.REGION, Aws.ACCOUNT_ID]; /** - * Replaces CloudFormation Tokens ('Aws.PARTITION') with corresponding - * Asset Tokens ('${AWS::Partition}'). + * Replaces CloudFormation Tokens (i.e. 'Aws.PARTITION') with corresponding + * Asset Tokens (i.e. '${AWS::Partition}'). */ export function translateCfnTokenToAssetToken(arn: string) { for (let i = 0; i < CFN_TOKENS.length; i++) { @@ -61,8 +61,8 @@ export function translateCfnTokenToAssetToken(arn: string) { } /** - * Replaces Asset Tokens ('${AWS::Partition}') with corresponding - * CloudFormation Tokens ('Aws.PARTITION'). + * Replaces Asset Tokens (i.e. '${AWS::Partition}') with corresponding + * CloudFormation Tokens (i.e. 'Aws.PARTITION'). */ export function translateAssetTokenToCfnToken(arn: string) { for (let i = 0; i < ASSET_TOKENS.length; i++) { From b7bed7e38bfc2b9e42b7b850c044d0aec9b87371 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Wed, 12 Apr 2023 12:00:32 +0200 Subject: [PATCH 068/120] Move some code around --- .../lib/app-staging-synthesizer.ts | 2 +- .../lib/default-staging-stack.ts | 61 ------------------- yarn.lock | 9 ++- 3 files changed, 9 insertions(+), 63 deletions(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts index e9e1c733f1767..451ddad186412 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts @@ -16,7 +16,7 @@ import { import { StringSpecializer } from 'aws-cdk-lib/core/lib/helpers-internal'; import * as cxapi from 'aws-cdk-lib/cx-api'; import { BootstrapRole, BootstrapRoles, StagingRoles } from './bootstrap-roles'; -import { IStagingStack as IStagingStack, DefaultStagingStack } from './default-staging-stack'; +import { IStagingStack, DefaultStagingStack } from './default-staging-stack'; import * as s3 from 'aws-cdk-lib/aws-s3'; /** diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts index 347b19a28aa2c..05ed5836f25d4 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts @@ -17,67 +17,6 @@ import { IConstruct } from 'constructs'; import { EPHEMERAL_PREFIX } from './app-staging-synthesizer'; import { BootstrapRole } from './bootstrap-roles'; -/** - * Information returned by the Staging Stack for each file asset. - */ -export interface FileAssetInfo { - /** - * The name of the staging bucket - */ - readonly bucketName: string; - - /** - * The arn to assume (fileAssetPublishingRole) - */ - readonly assumeRoleArn: string; -} - -/** - * Information returned by the Staging Stack for each image asset - */ -export interface ImageAssetInfo { - /** - * The name of the staging repository - */ - readonly repoName: string; - - /** - * The arn to assume (imageAssetPublishingRole) - */ - readonly assumeRoleArn: string; -} - -/** - * Information on how a Staging Stack should look. - */ -export interface IStagingStack extends IConstruct { - /** - * The app-scoped, environment-keyed bucket created in this staging stack. - */ - readonly stagingBucket?: s3.Bucket; - - /** - * The app-scoped, environment-keyed repositories created in this staging stack. - * A repository is created per image asset family. - */ - readonly stagingRepos: Record; - - /** - * The stack to add dependencies to. - */ - readonly dependencyStack: Stack; - - /** - * Return staging resource information for a file asset. - */ - addFile(asset: FileAssetSource): FileAssetInfo; - - /** - * Return staging resource information for a docker asset. - */ - addDockerImage(asset: DockerImageAssetSource): ImageAssetInfo; -} - /** * Default Staging Stack Properties */ diff --git a/yarn.lock b/yarn.lock index 873a273d434bb..ff0ff5b4a2aee 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2164,6 +2164,13 @@ resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== +"@types/fs-extra@^8.1.2": + version "8.1.2" + resolved "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.2.tgz#7125cc2e4bdd9bd2fc83005ffdb1d0ba00cca61f" + integrity sha512-SvSrYXfWSc7R4eqnOzbQF4TZmfpNSM9FrSWLU3EUnWBuyZqNBOrv1B1JA3byUDPUl9z4Ab3jeZG2eDdySlgNMg== + dependencies: + "@types/node" "*" + "@types/fs-extra@^9.0.13": version "9.0.13" resolved "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz#7594fbae04fe7f1918ce8b3d213f74ff44ac1f45" @@ -2221,7 +2228,7 @@ jest-matcher-utils "^27.0.0" pretty-format "^27.0.0" -"@types/json-schema@*", "@types/json-schema@^7.0.7", "@types/json-schema@^7.0.9": +"@types/json-schema@*", "@types/json-schema@^7.0.11", "@types/json-schema@^7.0.7", "@types/json-schema@^7.0.9": version "7.0.11" resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== From 6f3307579d35c5c789811341e643af77c3038962 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 12 Apr 2023 09:57:01 -0400 Subject: [PATCH 069/120] minor changes --- .../app-staging-synthesizer/lib/app-staging-synthesizer.ts | 4 ++-- .../test/app-staging-synthesizer.test.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts index d764429779284..2aeba970e4424 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts @@ -13,11 +13,11 @@ import { StackSynthesizer, Token, } from 'aws-cdk-lib'; +import * as s3 from 'aws-cdk-lib/aws-s3'; import { StringSpecializer, translateCfnTokenToAssetToken } from 'aws-cdk-lib/core/lib/helpers-internal'; import * as cxapi from 'aws-cdk-lib/cx-api'; import { BootstrapRole, BootstrapRoles, StagingRoles } from './bootstrap-roles'; import { IStagingStack as IStagingStack, DefaultStagingStack } from './default-staging-stack'; -import * as s3 from 'aws-cdk-lib/aws-s3'; /** * @internal @@ -380,7 +380,7 @@ class BoundAppStagingSynthesizer extends StackSynthesizer implements IBoundAppSt bucketName: translateCfnTokenToAssetToken(bucketName), bucketPrefix: asset.ephemeral ? EPHEMERAL_PREFIX : this.bucketPrefix, role: { - assumeRoleArn, + assumeRoleArn: translateCfnTokenToAssetToken(assumeRoleArn), }, }); return this.cloudFormationLocationFromFileAsset(location); diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts index 02be2972df4d7..dc929f9d11af9 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts @@ -1,11 +1,11 @@ /* eslint-disable jest/no-commented-out-tests */ import * as fs from 'fs'; import { App, Stack, CfnResource, FileAssetPackaging, Token, Lazy, Duration } from 'aws-cdk-lib'; +import { Template, Match } from 'aws-cdk-lib/assertions'; import * as cxschema from 'aws-cdk-lib/cloud-assembly-schema'; import { evaluateCFN } from 'aws-cdk-lib/core/test/evaluate-cfn'; -import { AppStagingSynthesizer } from '../lib'; -import { Template, Match } from 'aws-cdk-lib/assertions'; import { APP_ID, CFN_CONTEXT, TestAppScopedStagingSynthesizer, isAssetManifest, last } from './util'; +import { AppStagingSynthesizer } from '../lib'; // import { Repository } from 'aws-cdk-lib/aws-ecr'; // import { Bucket } from 'aws-cdk-lib/aws-s3'; From e4b15f432ec53aa7e9e59e46cc551af15e21c585 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Thu, 13 Apr 2023 12:59:00 +0200 Subject: [PATCH 070/120] Reorganize responsibilities between: - AppStagingSynthesizer - BoundAppStagingSynthesizer - StagingStack - PerEnvStagingFactory --- .../lib/app-staging-synthesizer.ts | 386 ++++++++---------- .../lib/bootstrap-roles.ts | 36 +- .../lib/default-staging-stack.ts | 136 +++--- .../app-staging-synthesizer/lib/index.ts | 3 +- .../lib/per-env-staging-factory.ts | 32 ++ .../lib/private/app-global.ts | 33 ++ .../lib/staging-stack.ts | 113 +++++ .../test/bootstrap-roles.test.ts | 12 +- .../test/integ.env-agnostic-synth.ts | 2 +- .../test/integ.synthesizer.ts | 2 +- .../app-staging-synthesizer/test/util.ts | 6 +- .../helpers-internal/string-specializer.ts | 23 +- 12 files changed, 489 insertions(+), 295 deletions(-) create mode 100644 packages/@aws-cdk/app-staging-synthesizer/lib/per-env-staging-factory.ts create mode 100644 packages/@aws-cdk/app-staging-synthesizer/lib/private/app-global.ts create mode 100644 packages/@aws-cdk/app-staging-synthesizer/lib/staging-stack.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts index da8581335239f..99dff20b0f44f 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts @@ -1,5 +1,4 @@ import { - App, AssetManifestBuilder, BOOTSTRAP_QUALIFIER_CONTEXT, DockerImageAssetLocation, @@ -14,218 +13,132 @@ import { Token, } from 'aws-cdk-lib'; import { StringSpecializer, translateCfnTokenToAssetToken } from 'aws-cdk-lib/core/lib/helpers-internal'; -import * as cxapi from 'aws-cdk-lib/cx-api'; -import { BootstrapRole, BootstrapRoles, StagingRoles } from './bootstrap-roles'; -import { IStagingStack, DefaultStagingStack } from './default-staging-stack'; -import * as s3 from 'aws-cdk-lib/aws-s3'; +import { BootstrapRole, BootstrapRoles } from './bootstrap-roles'; +import { DefaultStagingStack, DefaultStagingStackOptions } from './default-staging-stack'; +import { PerEnvironmenStagingFactory } from './per-env-staging-factory'; +import { AppScopedGlobal } from './private/app-global'; +import { IStagingStack, IStagingStackFactory, ObtainStagingResourcesContext } from './staging-stack'; -/** - * @internal - */ -export const EPHEMERAL_PREFIX = 'eph-'; +const AGNOSTIC_STACKS = new AppScopedGlobal(() => new Set()); +const ENV_AWARE_STACKS = new AppScopedGlobal(() => new Set()); /** - * Properties for stackPerEnv static method + * Options that apply to all AppStagingSynthesizer variants */ -export interface StackPerEnvProps { +export interface AppStagingSynthesizerOptions { /** - * App identifier that is unique to the app and used in the resource names of the Staging Stack. - */ - readonly appId: string; - - /** - * Custom roles to bring into the staging stack. + * What roles to use to deploy applications * - * @default - no custom roles - */ - readonly stagingRoles?: StagingRoles; - - /** - * Custom bootstrap roles that have permissions to interact with CloudFormation - * on your behalf. + * These are the roles that have permissions to interact with CloudFormation + * on your behalf. By default these are the standard bootstrapped CDK roles, + * but you can customize them or turn them off and use the CLI credentials + * to deploy. * - * @default - no custom roles + * @default - The standard bootstrapped CDK roles */ - readonly bootstrapRoles?: BootstrapRoles; + readonly deploymentRoles?: BootstrapRoles; /** - * Qualifier to disambiguate multiple environments in the same account + * Qualifier to disambiguate multiple bootstrapped environments in the same account + * + * This qualifier is only used to reference bootstrapped resources. It will not + * be used in the creation of app-specific staging resources: `appId` is used for that + * instead. * * @default - Value of context key '@aws-cdk/core:bootstrapQualifier' if set, otherwise `DEFAULT_QUALIFIER` */ - readonly qualifier?: string; + readonly bootstrapQualifier?: string; +} - readonly bucketPrefix?: string; +/** + * Properties for stackPerEnv static method + */ +export interface DefaultResourcesOptions extends AppStagingSynthesizerOptions, DefaultStagingStackOptions { +} +/** + * Properties for customFactory static method + */ +export interface CustomFactoryOptions extends AppStagingSynthesizerOptions { /** - * Retain all assets in the s3 bucket, even the ones that have been - * marked ephemeral. - * - * @default false + * The factory that will be used to return staging resources for each stack */ - readonly retainEphemeralFileAssets?: boolean; + readonly factory: IStagingStackFactory; /** - * Specify a custom lifecycle rule for ephemeral file assets. If you - * specify this property, you must set `prefix: 'eph-'` as part of the rule. - * This is the only way to identify ephemeral assets. + * Reuse the answer from the factory for stacks in the same environment * - * @default - ephemeral assets will be deleted after 10 days + * @default true */ - readonly ephemeralFileAssetLifecycleRule?: s3.LifecycleRule; + readonly oncePerEnv?: boolean; } /** - * Staging Stack Factory interface. - * - * The function included in this class will be called by the synthesizer - * to create or reference an IStagingStack that has the necessary - * staging resources for the Stack. + * Properties for customResources static method */ -export interface IStagingStackFactory { +export interface CustomResourcesOptions extends AppStagingSynthesizerOptions { /** - * Factory method to be called when binding stack to synthesizer. - * This method produces (either by creating or referencing an existing - * stack) the StagingStack that holds staging resources - * necessary for the bound stack. - * - * @param boundStack - stack to bind the synthesizer to + * Use these exact staging resources for every stack that this synthesizer is used for */ - stagingStackFactory(boundStack: Stack): IStagingStack; + readonly resources: IStagingStack; } /** - * Properties for customFactory static method + * Internal properties for AppStagingSynthesizer */ -export interface CustomFactoryProps extends AppStagingSynthesizerProps { +interface AppStagingSynthesizerProps extends AppStagingSynthesizerOptions { /** - * Include rules that create a new Staging Stack per environment. - * - * @default true + * A factory method that creates an IStagingStack when given the stack the + * synthesizer is binding. */ - // oncePerEnv: boolean; + readonly factory: IStagingStackFactory; } /** - * Internal properties for AppStagingSynthesizer + * App Staging Synthesizer */ -interface AppStagingSynthesizerProps { +export class AppStagingSynthesizer extends StackSynthesizer implements IReusableStackSynthesizer { /** - * A factory method that creates an IStagingStack when given the stack the - * synthesizer is binding. + * Default ARN qualifier */ - readonly stagingStackFactory: IStagingStackFactory; + public static readonly DEFAULT_QUALIFIER = 'hnb659fds'; /** - * Custom bootstrap roles that have permissions to interact with CloudFormation - * on your behalf. - * - * @default - no custom roles + * Default CloudFormation role ARN. */ - readonly bootstrapRoles?: BootstrapRoles; + public static readonly DEFAULT_CLOUDFORMATION_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-cfn-exec-role-${AWS::AccountId}-${AWS::Region}'; /** - * Qualifier to disambiguate multiple environments in the same account - * - * @default - Value of context key '@aws-cdk/core:bootstrapQualifier' if set, otherwise `DEFAULT_QUALIFIER` + * Default deploy role ARN. */ - readonly qualifier?: string; - - readonly bucketPrefix?: string; -} + public static readonly DEFAULT_DEPLOY_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-deploy-role-${AWS::AccountId}-${AWS::Region}'; -/** - * App Staging Synthesizer - */ -export class AppStagingSynthesizer extends StackSynthesizer implements IReusableStackSynthesizer { /** - * Use the Default Staging Stack as the Staging Stack for this Synthesizer, and - * create a new Staging Stack per environment this App is deployed in. + * Default lookup role ARN for missing values. */ - public static stackPerEnv(props: StackPerEnvProps) { - for (const key in props) { - if (props.hasOwnProperty(key)) { - validateNoToken(key as keyof StackPerEnvProps); - } - } + public static readonly DEFAULT_LOOKUP_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-lookup-role-${AWS::AccountId}-${AWS::Region}'; - function validateNoToken(key: A) { - const prop = props[key]; - if (typeof prop === 'string' && Token.isUnresolved(prop)) { - throw new Error(`AppStagingSynthesizer property '${key}' cannot contain tokens; only the following placeholder strings are allowed: ` + [ - '${Qualifier}', - cxapi.EnvironmentPlaceholders.CURRENT_REGION, - cxapi.EnvironmentPlaceholders.CURRENT_ACCOUNT, - cxapi.EnvironmentPlaceholders.CURRENT_PARTITION, - ].join(', ')); - } - } + /** + * Use the Default Staging Resources, creating a single stack per environment this app is deployed in + */ + public static defaultResources(props: DefaultResourcesOptions) { + return AppStagingSynthesizer.customFactory({ + factory: DefaultStagingStack.factory(props), + deploymentRoles: props.deploymentRoles, + oncePerEnv: true, + }); + } - return new AppStagingSynthesizer({ - qualifier: props.qualifier, - bootstrapRoles: props.bootstrapRoles, - bucketPrefix: props.bucketPrefix, - stagingStackFactory: { - stagingStackFactory(boundStack: Stack) { - const app = App.of(boundStack); - if (!App.isApp(app)) { - throw new Error(`Stack ${boundStack.stackName} must be part of an App`); - } - - const qualifier = props.qualifier ?? - boundStack.node.tryGetContext(BOOTSTRAP_QUALIFIER_CONTEXT) ?? - BoundAppStagingSynthesizer.DEFAULT_QUALIFIER; - - const spec = new StringSpecializer(boundStack, qualifier); - const deployActionRole = props.bootstrapRoles?.deploymentActionRole - ?? BootstrapRole.fromRoleArn(BoundAppStagingSynthesizer.DEFAULT_DEPLOY_ROLE_ARN); - const deployActionRoleArn = !deployActionRole.isCliCredentials() ? deployActionRole.renderRoleArn({ spec, tokenType: 'cfn' }) : undefined; - - let stackId = 'StagingStack'; - // Ensure we do not have a scenario where the App includes BOTH - // environment-agnostic stacks and set environment stacks. - const incompatibleEnvErrorMessage = [ - 'AppStagingSynthesizer cannot synthesize CDK Apps with BOTH environment-agnostic stacks and set environment stacks.', - 'Please either specify environments for all stacks or no stacks in the CDK App.', - ].join('\n'); - const addEnvMetadata = (agnostic: boolean) => { - if (app.node.metadata.filter((m) => m.type === 'cdk-env').length === 0) { - app.node.addMetadata('cdk-env', agnostic ? 'agnostic': 'non-agnostic'); - } - }; - - if (!Token.isUnresolved(boundStack.account) && !Token.isUnresolved(boundStack.region)) { - // Stack has specified account and region - stackId = stackId + boundStack.account + boundStack.region; - if (app.node.metadata.filter((m) => m.type === 'cdk-env' && m.data === 'agnostic').length >= 1) { - throw new Error(incompatibleEnvErrorMessage); - } - addEnvMetadata(false); - } else { - // Stack is environment agnostic - if (app.node.metadata.filter((m) => m.type === 'cdk-env' && m.data === 'non-agnostic').length >= 1) { - throw new Error(incompatibleEnvErrorMessage); - } - addEnvMetadata(true); - } - - const stackName = `StagingStack${props.appId}`; - const stagingStack = app.node.tryFindChild(stackId) as IStagingStack ?? new DefaultStagingStack(app, stackId, { - appId: props.appId, - env: { - account: boundStack.account, - region: boundStack.region, - }, - stackName, - fileAssetPublishingRole: props.stagingRoles?.fileAssetPublishingRole, - imageAssetPublishingRole: props.stagingRoles?.dockerAssetPublishingRole, - deployActionRoleArn, - retainEphemeralFileAssets: props.retainEphemeralFileAssets, - ephemeralFileAssetLifecycleRule: props.ephemeralFileAssetLifecycleRule, - }); - boundStack.addDependency(stagingStack.dependencyStack, 'stack depends on the staging stack for staging resources'); - - return stagingStack; + /** + * Use these exact staging resources for every stack that this synthesizer is used for + */ + public static customResources(options: CustomResourcesOptions) { + return AppStagingSynthesizer.customFactory({ + deploymentRoles: options.deploymentRoles, + oncePerEnv: false, + factory: { + obtainStagingResources() { + return options.resources; }, }, }); @@ -238,32 +151,59 @@ export class AppStagingSynthesizer extends StackSynthesizer implements IReusable * By default, `oncePerEnv = true`, which means that a new instance of the IStagingStack * will be created in new environments. Set `oncePerEnv = false` to turn off that behavior. */ - public static customFactory(props: CustomFactoryProps) { - return new AppStagingSynthesizer(props); - } + public static customFactory(props: CustomFactoryOptions) { + const oncePerEnv = props.oncePerEnv ?? true; + const factory = oncePerEnv ? new PerEnvironmenStagingFactory(props.factory) : props.factory; - /** - * Supply a specific stack to be used as the Staging Stack for this App. - */ - public static customDecider(stack: IStagingStack) { return new AppStagingSynthesizer({ - stagingStackFactory: { - stagingStackFactory(_boundStack: Stack) { - return stack; - }, - }, + factory, + bootstrapQualifier: props.bootstrapQualifier, + deploymentRoles: props.deploymentRoles, }); } + private readonly roles: Required; + private constructor(private readonly props: AppStagingSynthesizerProps) { super(); + + this.roles = { + deploymentRole: props.deploymentRoles?.deploymentRole ?? + BootstrapRole.fromRoleArn(AppStagingSynthesizer.DEFAULT_DEPLOY_ROLE_ARN), + cloudFormationExecutionRole: props.deploymentRoles?.cloudFormationExecutionRole ?? + BootstrapRole.fromRoleArn(AppStagingSynthesizer.DEFAULT_CLOUDFORMATION_ROLE_ARN), + lookupRole: this.props.deploymentRoles?.lookupRole ?? + BootstrapRole.fromRoleArn(AppStagingSynthesizer.DEFAULT_LOOKUP_ROLE_ARN), + }; } /** * Returns a version of the synthesizer bound to a stack. */ public reusableBind(stack: Stack): IBoundAppStagingSynthesizer { - return new BoundAppStagingSynthesizer(stack, this.props); + this.checkEnvironmentGnosticism(stack); + const qualifier = this.props.bootstrapQualifier ?? + stack.node.tryGetContext(BOOTSTRAP_QUALIFIER_CONTEXT) ?? + AppStagingSynthesizer.DEFAULT_QUALIFIER; + const spec = new StringSpecializer(stack, qualifier); + + const deployRole = this.roles.deploymentRole._specialize(spec); + + const context: ObtainStagingResourcesContext = { + environmentString: [ + Token.isUnresolved(stack.region) ? 'REGION' : stack.region, + Token.isUnresolved(stack.account) ? 'ACCOUNT' : stack.account, + ].join('-'), + deployRoleArn: deployRole._arnForCloudFormation(), + }; + + return new BoundAppStagingSynthesizer(stack, { + stagingResources: this.props.factory.obtainStagingResources(stack, context), + deployRole, + cloudFormationExecutionRole: this.roles.cloudFormationExecutionRole._specialize(spec), + lookupRole: this.roles.lookupRole._specialize(spec), + qualifier, + }); } /** @@ -293,64 +233,83 @@ export class AppStagingSynthesizer extends StackSynthesizer implements IReusable public addDockerImageAsset(_asset: DockerImageAssetSource): DockerImageAssetLocation { throw new Error('This is a legacy API, call reusableBind instead'); } + + /** + * Check that we're only being used for exclusively gnostic or agnostic stacks. + * + * We can think about whether to loosen this requirement later. + */ + private checkEnvironmentGnosticism(stack: Stack) { + const isAgnostic = Token.isUnresolved(stack.account) || Token.isUnresolved(stack.region); + const agnosticStacks = AGNOSTIC_STACKS.for(stack); + const envAwareStacks = ENV_AWARE_STACKS.for(stack); + + (isAgnostic ? agnosticStacks : envAwareStacks).add(stack); + if (agnosticStacks.size > 0 && envAwareStacks.size > 0) { + + const describeStacks = (xs: Set) => Array.from(xs).map(s => s.node.path).join(', '); + + throw new Error([ + 'It is not safe to use AppStagingSynthesizer for both environment-agnostic and environment-aware stacks at the same time.', + 'Please either specify environments for all stacks or no stacks in the CDK App.', + `Stacks with environment: ${describeStacks(agnosticStacks)}.`, + `Stacks without environment: ${describeStacks(envAwareStacks)}.`, + ].join(' ')); + } + } } -class BoundAppStagingSynthesizer extends StackSynthesizer implements IBoundAppStagingSynthesizer { +/** + * Internal properties for BoundAppStagingSynthesizer + */ +interface BoundAppStagingSynthesizerProps { /** - * Default ARN qualifier + * The bootstrap qualifier */ - public static readonly DEFAULT_QUALIFIER = 'hnb659fds'; + readonly qualifier: string; /** - * Default CloudFormation role ARN. + * The resources we end up using for this synthesizer */ - public static readonly DEFAULT_CLOUDFORMATION_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-cfn-exec-role-${AWS::AccountId}-${AWS::Region}'; + readonly stagingResources: IStagingStack; /** - * Default deploy role ARN. + * The deploy role */ - public static readonly DEFAULT_DEPLOY_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-deploy-role-${AWS::AccountId}-${AWS::Region}'; + readonly deployRole: BootstrapRole; /** - * Default lookup role ARN for missing values. + * CloudFormation Execution Role */ - public static readonly DEFAULT_LOOKUP_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-lookup-role-${AWS::AccountId}-${AWS::Region}'; + readonly cloudFormationExecutionRole: BootstrapRole; + + /** + * Lookup Role + */ + readonly lookupRole: BootstrapRole; +} + - private stagingStack: IStagingStack; - private assetManifest = new AssetManifestBuilder(); +class BoundAppStagingSynthesizer extends StackSynthesizer implements IBoundAppStagingSynthesizer { + private readonly stagingStack: IStagingStack; + private readonly assetManifest = new AssetManifestBuilder(); private readonly lookupRoleArn?: string; private readonly cloudFormationExecutionRoleArn?: string; private readonly deploymentActionRoleArn?: string; private readonly qualifier: string; - private readonly bucketPrefix?: string; - constructor(stack: Stack, props: AppStagingSynthesizerProps) { + constructor(stack: Stack, props: BoundAppStagingSynthesizerProps) { super(); super.bind(stack); - this.bucketPrefix = props.bucketPrefix; - this.qualifier = props.qualifier ?? stack.node.tryGetContext(BOOTSTRAP_QUALIFIER_CONTEXT) ?? BoundAppStagingSynthesizer.DEFAULT_QUALIFIER; - const spec = new StringSpecializer(stack, this.qualifier); - - const lookupRole = props.bootstrapRoles?.lookupRole ?? BootstrapRole.fromRoleArn(BoundAppStagingSynthesizer.DEFAULT_LOOKUP_ROLE_ARN); - this.lookupRoleArn = !lookupRole.isCliCredentials() ? lookupRole.renderRoleArn({ spec }) : undefined; - - const cloudFormationExecutionRole = props.bootstrapRoles?.cloudFormationExecutionRole ?? - BootstrapRole.fromRoleArn(BoundAppStagingSynthesizer.DEFAULT_CLOUDFORMATION_ROLE_ARN); - this.cloudFormationExecutionRoleArn = !cloudFormationExecutionRole.isCliCredentials() ? - cloudFormationExecutionRole.renderRoleArn({ spec }) : undefined; - - const deploymentActionRole = props.bootstrapRoles?.deploymentActionRole ?? - BootstrapRole.fromRoleArn(BoundAppStagingSynthesizer.DEFAULT_DEPLOY_ROLE_ARN); - this.deploymentActionRoleArn = !deploymentActionRole.isCliCredentials() ? deploymentActionRole.renderRoleArn({ spec }) : undefined; - - this.stagingStack = props.stagingStackFactory.stagingStackFactory(stack); + this.qualifier = props.qualifier; + this.stagingStack = props.stagingResources; } - /** * The qualifier used to bootstrap this stack */ public get bootstrapQualifier(): string | undefined { + // Not sure why we need this. return this.qualifier; } @@ -375,14 +334,17 @@ class BoundAppStagingSynthesizer extends StackSynthesizer implements IBoundAppSt * Add a file asset to the manifest. */ public addFileAsset(asset: FileAssetSource): FileAssetLocation { - const { bucketName, assumeRoleArn } = this.stagingStack.addFile(asset); + const { bucketName, assumeRoleArn, prefix, dependencyStack } = this.stagingStack.addFile(asset); const location = this.assetManifest.defaultAddFileAsset(this.boundStack, asset, { bucketName: translateCfnTokenToAssetToken(bucketName), - bucketPrefix: asset.ephemeral ? EPHEMERAL_PREFIX : this.bucketPrefix, - role: { - assumeRoleArn, - }, + bucketPrefix: prefix, + role: assumeRoleArn ? { assumeRoleArn } : undefined, }); + + if (dependencyStack) { + this.boundStack.addDependency(dependencyStack, 'stack depends on the staging stack for staging resources'); + } + return this.cloudFormationLocationFromFileAsset(location); } diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts index 113e23bdc1235..bca9fef0a3819 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts @@ -16,6 +16,7 @@ export class BootstrapRole { * Specify an existing IAM Role to assume */ public static fromRoleArn(arn: string) { + StringSpecializer.validateNoTokens(arn, 'BootstrapRole ARN'); return new BootstrapRole(arn); } @@ -27,25 +28,28 @@ export class BootstrapRole { return this.roleArn === BootstrapRole.CLI_CREDS; } - public renderRoleArn(options: { - spec?: StringSpecializer, - tokenType?: 'asset' | 'cfn', - } = {}) { - if (this.isCliCredentials()) { return undefined; } - if (!options.spec) { return this.roleArn; } + /** + * @internal + */ + public _arnForCloudFormation() { + return this.isCliCredentials() ? undefined : translateAssetTokenToCfnToken(this.roleArn); + } + + /** + * @internal + */ + public _arnForAssetManifest() { + return this.isCliCredentials() ? undefined : translateCfnTokenToAssetToken(this.roleArn); + } - const arn = options.spec.specialize(this.roleArn); - if (options.tokenType === 'asset') { - return translateCfnTokenToAssetToken(arn); - } else if (options.tokenType === 'cfn') { - return translateAssetTokenToCfnToken(arn); - } else { - return arn; - } + /** + * @internal + */ + public _specialize(spec: StringSpecializer) { + return new BootstrapRole(spec.specialize(this.roleArn)); } } - /** * Roles that are bootstrapped to your account. */ @@ -62,7 +66,7 @@ export interface BootstrapRoles { * * @default - use boostrapped role */ - readonly deploymentActionRole?: BootstrapRole; + readonly deploymentRole?: BootstrapRole; /** * Lookup Role diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts index eca94e908de6e..da6fd41aeb92b 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts @@ -13,16 +13,20 @@ import * as ecr from 'aws-cdk-lib/aws-ecr'; import * as iam from 'aws-cdk-lib/aws-iam'; import * as kms from 'aws-cdk-lib/aws-kms'; import * as s3 from 'aws-cdk-lib/aws-s3'; -import { IConstruct } from 'constructs'; -import { EPHEMERAL_PREFIX } from './app-staging-synthesizer'; import { BootstrapRole } from './bootstrap-roles'; +import { FileStagingLocation, IStagingStack, IStagingStackFactory, ImageStagingLocation } from './staging-stack'; + +const EPHEMERAL_PREFIX = 'handover/'; /** - * Default Staging Stack Properties + * User configurable options to the DefaultStagingStack */ -export interface DefaultStagingStackProps extends StackProps { +export interface DefaultStagingStackOptions { /** - * The unique id of the app that the staging stack is scoped to. + * A unique identifier for the application that the staging stack belongs to + * + * This identifier will be used in the name of staging resources + * created for this application, and should be unique across CDK apps. */ readonly appId: string; @@ -36,51 +40,78 @@ export interface DefaultStagingStackProps extends StackProps { /** * Pass in an existing role to be used as the file publishing role. * - * @default - a well-known name unique to this app/env. + * @default - a new role will be created */ readonly fileAssetPublishingRole?: BootstrapRole; /** * Pass in an existing role to be used as the image publishing role. * - * @default - a well-known name unique to this app/env. + * @default - a new role will be created */ readonly imageAssetPublishingRole?: BootstrapRole; - readonly deployActionRoleArn?: string; - /** - * Specify a custom lifecycle rule for ephemeral file assets. If you - * specify this property, you must set `prefix: 'eph-'` as part of the rule. - * This is the only way to identify ephemeral assets. + * The lifetime for handover file assets * - * @default - ephemeral assets will be deleted after 10 days - */ - readonly ephemeralFileAssetLifecycleRule?: s3.LifecycleRule; - - /** - * Retain all assets in the s3 bucket, even the ones that have been - * marked ephemeral. + * Assets that are only necessary at deployment time (for instance, + * CloudFormation templates and Lambda source code bundles) will be + * automatically deleted after this many days. Assets that may be + * read from the staging bucket during your application's run time + * will not be deleted. + * + * Set this to the length of time you wish to be able to roll back to + * previous versions of your application without having to do a new + * `cdk synth` and re-upload of assets. * - * @default false + * @default - Duration.days(30) */ - readonly retainEphemeralFileAssets?: boolean; + readonly handoverFileAssetLifetime?: Duration; +} +/** + * Default Staging Stack Properties + */ +export interface DefaultStagingStackProps extends DefaultStagingStackOptions, StackProps { /** - * Repository lifecycle rules (not fully implemented) + * The ARN of the deploy action role, if given + * + * This role will need permissions to read from to the staging resources. */ - // readonly repositoryLifecycleRules?: StagingRepoLifecycleRule[]; + readonly deployRoleArn?: string; } -// export interface StagingRepoLifecycleRule { -// readonly lifecycleRules: ecr.LifecycleRule[]; -// readonly assets: string[]; -// } - /** * A default Staging Stack */ export class DefaultStagingStack extends Stack implements IStagingStack { + /** + * Return a factory that will create DefaultStagingStacks + */ + public static factory(options: DefaultStagingStackOptions): IStagingStackFactory { + return { + obtainStagingResources(stack, context) { + const app = App.of(stack); + if (!App.isApp(app)) { + throw new Error(`Stack ${stack.stackName} must be part of an App`); + } + + const stackId = `StagingStack-${options.appId}-${context.environmentString}`; + return new DefaultStagingStack(app, stackId, { + ...options, + + // Does not need to contain environment because stack names are unique inside an env anyway + stackName: `StagingStack-${options.appId}`, + env: { + account: stack.account, + region: stack.region, + }, + appId: options.appId, + }); + }, + }; + } + /** * Default asset publishing role name for file (S3) assets. */ @@ -116,12 +147,10 @@ export class DefaultStagingStack extends Stack implements IStagingStack { private readonly fileAssetPublishingRoleId = 'CdkFilePublishingRole'; private readonly imageAssetPublishingRoleArn?: string; private readonly imageAssetPublishingRoleId = 'CdkImagePublishingRole'; - private readonly ephemeralFileAssetLifecycleRule?: s3.LifecycleRule; - private readonly retainEphemeralFileAssets?: boolean; - private readonly deployActionRoleArn?: string; + private readonly deployRoleArn?: string; // private readonly repositoryLifecycleRules: Record; - constructor(scope: App, id: string, props: DefaultStagingStackProps) { + constructor(scope: App, id: string, private readonly props: DefaultStagingStackProps) { super(scope, id, { ...props, synthesizer: new BootstraplessSynthesizer(), @@ -130,9 +159,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { this.appId = props.appId; this.dependencyStack = this; - this.deployActionRoleArn = props.deployActionRoleArn; - this.ephemeralFileAssetLifecycleRule = this.validateEphemeralAssetLifecycleRule(props.ephemeralFileAssetLifecycleRule); - this.retainEphemeralFileAssets = props.retainEphemeralFileAssets; + this.deployRoleArn = props.deployRoleArn; this.stagingBucketName = props.stagingBucketName; this.fileAssetPublishingRoleArn = props.fileAssetPublishingRole ? this.validateStagingRole(props.fileAssetPublishingRole).renderRoleArn() : undefined; @@ -149,14 +176,6 @@ export class DefaultStagingStack extends Stack implements IStagingStack { return stagingRole; } - private validateEphemeralAssetLifecycleRule(rule?: s3.LifecycleRule) { - if (!rule) { return rule; } - if (rule.prefix !== EPHEMERAL_PREFIX) { - throw new Error(`ephemeralAssetLifecycleRule must contain "prefix: '${EPHEMERAL_PREFIX}'" but got "prefix: ${rule.prefix}". This prefix is the only way to identify ephemeral assets.`); - } - return rule; - } - // private processLifecycleRules(rules: StagingRepoLifecycleRule[]) { // const ruleMap: Record = {}; // for (const rule of rules) { @@ -252,10 +271,15 @@ export class DefaultStagingStack extends Stack implements IStagingStack { removalPolicy: RemovalPolicy.RETAIN, encryption: s3.BucketEncryption.KMS, encryptionKey: key, + + // Many AWS account safety checkers will complain when buckets aren't versioned + versioned: true, + // Many AWS account safety checkers will complain when SSL isn't enforced + enforceSSL: true, }); bucket.grantReadWrite(role); - if (this.deployActionRoleArn) { + if (this.deployRoleArn) { bucket.addToResourcePolicy(new iam.PolicyStatement({ actions: [ 's3:GetObject*', @@ -263,17 +287,20 @@ export class DefaultStagingStack extends Stack implements IStagingStack { 's3:List*', ], resources: [bucket.bucketArn, bucket.arnForObjects('*')], - principals: [new iam.ArnPrincipal(this.deployActionRoleArn)], + principals: [new iam.ArnPrincipal(this.deployRoleArn)], })); } - if (this.retainEphemeralFileAssets !== true) { - const rule = this.ephemeralFileAssetLifecycleRule ?? { - prefix: EPHEMERAL_PREFIX, - expiration: Duration.days(10), - }; - bucket.addLifecycleRule(rule); - } + // Objects should never be overwritten, but let's make sure we have a lifecycle policy + // for it anyway. + bucket.addLifecycleRule({ + noncurrentVersionExpiration: Duration.days(365), + }); + + bucket.addLifecycleRule({ + prefix: EPHEMERAL_PREFIX, + expiration: this.props.handoverFileAssetLifetime ?? Duration.days(30), + }); return stagingBucketName; } @@ -301,14 +328,15 @@ export class DefaultStagingStack extends Stack implements IStagingStack { return repoName; } - public addFile(_asset: FileAssetSource): FileAssetInfo { + public addFile(asset: FileAssetSource): FileStagingLocation { return { bucketName: this.getCreateBucket(), assumeRoleArn: this.getFilePublishingRoleArn(), + prefix: asset.ephemeral ? EPHEMERAL_PREFIX : undefined, }; } - public addDockerImage(asset: DockerImageAssetSource): ImageAssetInfo { + public addDockerImage(asset: DockerImageAssetSource): ImageStagingLocation { return { repoName: this.getCreateRepo(asset), assumeRoleArn: this.getImagePublishingRoleArn(), diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/index.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/index.ts index 8c658ccabed61..639efa872b4de 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/index.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/index.ts @@ -1,3 +1,4 @@ export * from './default-staging-stack'; export * from './app-staging-synthesizer'; -export * from './bootstrap-roles'; \ No newline at end of file +export * from './bootstrap-roles'; +export * from './staging-stack'; \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/per-env-staging-factory.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/per-env-staging-factory.ts new file mode 100644 index 0000000000000..422b92896f55d --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/per-env-staging-factory.ts @@ -0,0 +1,32 @@ +import { Stack } from 'aws-cdk-lib'; +import { AppScopedGlobal } from './private/app-global'; +import { IStagingStack, IStagingStackFactory, ObtainStagingResourcesContext } from './staging-stack'; + +/** + * Per-environment cache + * + * This is a global because we might have multiple instances of this class + * in the app, but we want to cache across all of them. + */ +const ENVIRONMENT_CACHE = new AppScopedGlobal(() => new Map); + +/** + * Wraps another IStagingStack factory, and caches the result on a per-environment basis + */ +export class PerEnvironmenStagingFactory implements IStagingStackFactory { + constructor(private readonly wrapped: IStagingStackFactory) { } + + public obtainStagingResources(stack: Stack, context: ObtainStagingResourcesContext): IStagingStack { + const cacheKey = context.environmentString; + + const cache = ENVIRONMENT_CACHE.for(stack); + const existing = cache.get(cacheKey); + if (existing) { + return existing; + } + + const result = this.wrapped.obtainStagingResources(stack, context); + cache.set(cacheKey, result); + return result; + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/private/app-global.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/private/app-global.ts new file mode 100644 index 0000000000000..1dd26c5bc4f8c --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/private/app-global.ts @@ -0,0 +1,33 @@ +import { App } from 'aws-cdk-lib'; +import { IConstruct } from 'constructs'; + +/** + * Hold an App-wide global variable + * + * This is a replacement for a `static` variable, but does the right thing in case people + * instantiate multiple Apps in the same process space (for example, in unit tests or + * people using `cli-lib` in advanced configurations). + * + * This class assumes that the global you're going to be storing is a mutable object. + */ +export class AppScopedGlobal { + private readonly map = new WeakMap(); + + constructor(private readonly factory: () => A) { + } + + public for(ctr: IConstruct): A { + const app = App.of(ctr); + if (!App.isApp(app)) { + throw new Error(`Construct ${ctr.node.path} must be part of an App`); + } + + const existing = this.map.get(app); + if (existing) { + return existing; + } + const instance = this.factory(); + this.map.set(app, instance); + return instance; + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/staging-stack.ts new file mode 100644 index 0000000000000..a319b968abb0e --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/staging-stack.ts @@ -0,0 +1,113 @@ +import { DockerImageAssetSource, FileAssetSource, Stack } from 'aws-cdk-lib'; +import { IConstruct } from 'constructs'; + +/** + * Information returned by the Staging Stack for each file asset. + */ +export interface FileStagingLocation { + /** + * The name of the staging bucket + */ + readonly bucketName: string; + + /** + * A prefix to add to the keys + * + * @default '' + */ + readonly prefix?: string; + + /** + * The ARN to assume to write files to this bucket + * + * @default - Don't assume a role + */ + readonly assumeRoleArn?: string; + + /** + * The stack that creates this bucket (leads to dependencies on it) + * + * @default - Don't add dependencies + */ + readonly dependencyStack?: Stack; +} + +/** + * Information returned by the Staging Stack for each image asset + */ +export interface ImageStagingLocation { + /** + * The name of the staging repository + */ + readonly repoName: string; + + /** + * The arn to assume to write files to this repository + * + * @default - Don't assume a role + */ + readonly assumeRoleArn?: string; + + /** + * The stack that creates this repository (leads to dependencies on it) + * + * @default - Don't add dependencies + */ + readonly dependencyStack?: Stack; +} + +/** + * Information on how a Staging Stack should look. + */ +export interface IStagingStack extends IConstruct { + /** + * Return staging resource information for a file asset. + */ + addFile(asset: FileAssetSource): FileStagingLocation; + + /** + * Return staging resource information for a docker asset. + */ + addDockerImage(asset: DockerImageAssetSource): ImageStagingLocation; +} + +/** + * Staging Stack Factory interface. + * + * The function included in this class will be called by the synthesizer + * to create or reference an IStagingStack that has the necessary + * staging resources for the Stack. + */ +export interface IStagingStackFactory { + /** + * Return an object that will manage staging resources for the given stack + * + * This is called whenever the the `AppStagingSynthesizer` binds to a specific + * stack, and allows selecting where the staging resources go. + * + * This method can choose to either create a new construct (perhaps a stack) + * and return it, or reference an existing construct. + * + * @param stack - stack to return an appropriate IStagingStack for + */ + obtainStagingResources(stack: Stack, context: ObtainStagingResourcesContext): IStagingStack; +} + +/** + * Context parameters for the 'obtainStagingResources' function + */ +export interface ObtainStagingResourcesContext { + /** + * A unique string describing the environment that is guaranteed not to have tokens in it + */ + readonly environmentString: string; + + /** + * The ARN of the deploy action role, if given + * + * This role will need permissions to read from to the staging resources. + * + * @default - Deploy role ARN is unknown + */ + readonly deployRoleArn?: string; +} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts index 2d1f32d15a491..cc1f87b2b9926 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts @@ -9,7 +9,7 @@ describe('Boostrap Roles', () => { test('Can supply existing arns for bootstrapped roles', () => { // GIVEN const app = new App({ - defaultStackSynthesizer: AppStagingSynthesizer.stackPerEnv({ + defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: APP_ID, bootstrapRoles: { cloudFormationExecutionRole: BootstrapRole.fromRoleArn(CLOUDFORMATION_EXECUTION_ROLE), @@ -43,7 +43,7 @@ describe('Boostrap Roles', () => { test('can supply existing arns for staging roles', () => { // GIVEN const app = new App({ - defaultStackSynthesizer: AppStagingSynthesizer.stackPerEnv({ + defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: APP_ID, stagingRoles: { fileAssetPublishingRole: BootstrapRole.fromRoleArn('arn'), @@ -75,7 +75,7 @@ describe('Boostrap Roles', () => { test('bootstrap roles can be specified as current cli credentials instead', () => { // GIVEN const app = new App({ - defaultStackSynthesizer: AppStagingSynthesizer.stackPerEnv({ + defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: APP_ID, bootstrapRoles: { cloudFormationExecutionRole: BootstrapRole.cliCredentials(), @@ -108,7 +108,7 @@ describe('Boostrap Roles', () => { test('staging roles cannot be specified as cli credentials', () => { const app = new App({ - defaultStackSynthesizer: AppStagingSynthesizer.stackPerEnv({ + defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: APP_ID, stagingRoles: { fileAssetPublishingRole: BootstrapRole.cliCredentials(), @@ -121,8 +121,8 @@ describe('Boostrap Roles', () => { test('qualifier is resolved in the synthesizer', () => { const app = new App({ - defaultStackSynthesizer: AppStagingSynthesizer.stackPerEnv({ - qualifier: 'abcdef', + defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ + bootstrapQualifier: 'abcdef', appId: APP_ID, }), }); diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.ts b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.ts index b98dff075ebfa..baeec0bf3a71c 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.ts @@ -7,7 +7,7 @@ import { AppStagingSynthesizer } from '../lib'; const app = new App(); const stack = new Stack(app, 'app-scoped-staging-test', { - synthesizer: AppStagingSynthesizer.stackPerEnv({ + synthesizer: AppStagingSynthesizer.defaultResources({ appId: 'envAgnostic', }), }); diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts index 722adb0b0c233..5067e39eab0ff 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts @@ -7,7 +7,7 @@ import { AppStagingSynthesizer } from '../lib'; const app = new App(); const stack = new Stack(app, 'app-scoped-staging-test', { - synthesizer: AppStagingSynthesizer.stackPerEnv({ + synthesizer: AppStagingSynthesizer.defaultResources({ appId: 'newId3', }), env: { diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/util.ts b/packages/@aws-cdk/app-staging-synthesizer/test/util.ts index 09cc34807d9e8..d947dd8c21c88 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/util.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/util.ts @@ -1,4 +1,4 @@ -import { StackPerEnvProps, AppStagingSynthesizer, BootstrapRole } from '../lib'; +import { DefaultResourcesOptions, AppStagingSynthesizer, BootstrapRole } from '../lib'; import * as cxapi from 'aws-cdk-lib/cx-api'; export const CFN_CONTEXT = { @@ -20,8 +20,8 @@ export function last(xs?: A[]): A | undefined { } export class TestAppScopedStagingSynthesizer { - public static stackPerEnv(props: Partial = {}): AppStagingSynthesizer { - return AppStagingSynthesizer.stackPerEnv({ + public static stackPerEnv(props: Partial = {}): AppStagingSynthesizer { + return AppStagingSynthesizer.defaultResources({ appId: props.appId ?? APP_ID, bootstrapRoles: { cloudFormationExecutionRole: BootstrapRole.fromRoleArn(CLOUDFORMATION_EXECUTION_ROLE), diff --git a/packages/aws-cdk-lib/core/lib/helpers-internal/string-specializer.ts b/packages/aws-cdk-lib/core/lib/helpers-internal/string-specializer.ts index 4ae65313381f4..4e0abd790d0a0 100644 --- a/packages/aws-cdk-lib/core/lib/helpers-internal/string-specializer.ts +++ b/packages/aws-cdk-lib/core/lib/helpers-internal/string-specializer.ts @@ -11,9 +11,22 @@ function replaceAll(s: string, search: string, replace: string) { } export class StringSpecializer { - constructor(private readonly stack: Stack, private readonly qualifier: string) { + /** + * Validate that the given string does not contain tokens + */ + public static validateNoTokens(s: string, what: string) { + if (Token.isUnresolved(s)) { + throw new Error(`${what} may not contain tokens; only the following literal placeholder strings are allowed: ` + [ + '${Qualifier}', + cxapi.EnvironmentPlaceholders.CURRENT_REGION, + cxapi.EnvironmentPlaceholders.CURRENT_ACCOUNT, + cxapi.EnvironmentPlaceholders.CURRENT_PARTITION, + ].join(', ') + `. Got: ${s}`); + } } + constructor(private readonly stack: Stack, private readonly qualifier: string) { } + /** * Function to replace placeholders in the input string as much as possible * @@ -31,6 +44,14 @@ export class StringSpecializer { }); } + /** + * Specialize the given string, make sure it doesn't contain tokens + */ + public specializeNoTokens(s: string, what: string): string { + StringSpecializer.validateNoTokens(s, what); + return this.specialize(s); + } + /** * Specialize only the qualifier */ From cd398267c2a4471d0a70762527e19e85cf63cf6d Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Thu, 13 Apr 2023 14:42:20 +0200 Subject: [PATCH 071/120] Fix build issues --- .../lib/app-staging-synthesizer.ts | 1 + .../lib/default-staging-stack.ts | 162 ++++++++++-------- .../lib/staging-stack.ts | 7 + .../test/app-staging-synthesizer.test.ts | 90 +--------- .../test/bootstrap-roles.test.ts | 22 +-- .../app-staging-synthesizer/test/util.ts | 6 +- yarn.lock | 9 +- 7 files changed, 118 insertions(+), 179 deletions(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts index 75749581ecb0c..3a09d797f79e1 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts @@ -195,6 +195,7 @@ export class AppStagingSynthesizer extends StackSynthesizer implements IReusable Token.isUnresolved(stack.account) ? 'ACCOUNT' : stack.account, ].join('-'), deployRoleArn: deployRole._arnForCloudFormation(), + qualifier, }; return new BoundAppStagingSynthesizer(stack, { diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts index da6fd41aeb92b..45dd675dfa2ff 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts @@ -13,6 +13,7 @@ import * as ecr from 'aws-cdk-lib/aws-ecr'; import * as iam from 'aws-cdk-lib/aws-iam'; import * as kms from 'aws-cdk-lib/aws-kms'; import * as s3 from 'aws-cdk-lib/aws-s3'; +import { StringSpecializer } from 'aws-cdk-lib/core/lib/helpers-internal'; import { BootstrapRole } from './bootstrap-roles'; import { FileStagingLocation, IStagingStack, IStagingStackFactory, ImageStagingLocation } from './staging-stack'; @@ -79,6 +80,13 @@ export interface DefaultStagingStackProps extends DefaultStagingStackOptions, St * This role will need permissions to read from to the staging resources. */ readonly deployRoleArn?: string; + + /** + * The qualifier used to specialize strings + * + * Shouldn't be necessary but who knows what people might do. + */ + readonly qualifier: string; } /** @@ -107,6 +115,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { region: stack.region, }, appId: options.appId, + qualifier: context.qualifier, }); }, }; @@ -115,15 +124,15 @@ export class DefaultStagingStack extends Stack implements IStagingStack { /** * Default asset publishing role name for file (S3) assets. */ - private get DEFAULT_FILE_ASSET_PUBLISHING_ROLE_NAME() { - return `cdk-file-publishing-role-${this.region}-${this.appId}`.slice(0, 63); + private get fileRoleName() { + return `cdk-${this.appId}-file-publishing-role-${this.region}`.slice(0, 63); } /** * Default asset publishing role name for docker (ECR) assets. */ - private get DEFAULT_IMAGE_ASSET_PUBISHING_ROLE_NAME() { - return `cdk-asset-publishing-role-${this.region}-${this.appId}`.slice(0, 63); + private get imageRoleName() { + return `cdk-${this.appId}-asset-publishing-role-${this.region}`.slice(0, 63); } /** @@ -143,10 +152,22 @@ export class DefaultStagingStack extends Stack implements IStagingStack { private readonly appId: string; private readonly stagingBucketName?: string; - private readonly fileAssetPublishingRoleArn?: string; - private readonly fileAssetPublishingRoleId = 'CdkFilePublishingRole'; - private readonly imageAssetPublishingRoleArn?: string; - private readonly imageAssetPublishingRoleId = 'CdkImagePublishingRole'; + + /** + * File publish role ARN in asset manifest format + */ + private readonly providedFileRole?: BootstrapRole; + private fileRole?: iam.IRole; + private fileRoleManifestArn?: string; + + /** + * Image publishing role ARN in asset manifest format + */ + private readonly providedImageRole?: BootstrapRole; + private imageRole?: iam.IRole; + private didImageRole = false; + private imageRoleManifestArn?: string; + private readonly deployRoleArn?: string; // private readonly repositoryLifecycleRules: Record; @@ -156,26 +177,20 @@ export class DefaultStagingStack extends Stack implements IStagingStack { synthesizer: new BootstraplessSynthesizer(), }); - this.appId = props.appId; + this.appId = props.appId.toLocaleLowerCase(); this.dependencyStack = this; this.deployRoleArn = props.deployRoleArn; this.stagingBucketName = props.stagingBucketName; - this.fileAssetPublishingRoleArn = props.fileAssetPublishingRole ? - this.validateStagingRole(props.fileAssetPublishingRole).renderRoleArn() : undefined; - this.imageAssetPublishingRoleArn = props.imageAssetPublishingRole ? - this.validateStagingRole(props.imageAssetPublishingRole).renderRoleArn() : undefined; + const specializer = new StringSpecializer(this, props.qualifier); + + this.providedFileRole = props.fileAssetPublishingRole?._specialize(specializer); + this.providedImageRole = props.imageAssetPublishingRole?._specialize(specializer); + // this.repositoryLifecycleRules = this.processLifecycleRules(props.repositoryLifecycleRules ?? []); this.stagingRepos = {}; } - private validateStagingRole(stagingRole: BootstrapRole) { - if (stagingRole.isCliCredentials()) { - throw new Error('fileAssetPublishingRole and dockerAssetPublishingRole cannot be specified as cliCredentials(). Please supply an arn to reference an existing IAM role.'); - } - return stagingRole; - } - // private processLifecycleRules(rules: StagingRepoLifecycleRule[]) { // const ruleMap: Record = {}; // for (const rule of rules) { @@ -189,80 +204,77 @@ export class DefaultStagingStack extends Stack implements IStagingStack { // return ruleMap; // } - private getFilePublishingRoleArn(): string { - if (this.fileAssetPublishingRoleArn) { - return this.fileAssetPublishingRoleArn; - } - const role = this.node.tryFindChild(this.fileAssetPublishingRoleId) as iam.Role; - if (role === undefined) { - throw new Error('Cannot call getFilePublishingRoleArn before createFilePublishingRole'); + private ensureFilePublishingRole() { + if (this.providedFileRole) { + // Override + this.fileRoleManifestArn = this.providedFileRole._arnForAssetManifest(); + const cfnArn = this.providedFileRole._arnForCloudFormation(); + this.fileRole = cfnArn ? iam.Role.fromRoleArn(this, 'CdkFilePublishingRole', cfnArn) : undefined; + return; } - return Stack.of(this).formatArn({ + + const roleName = this.fileRoleName; + this.fileRole = new iam.Role(this, 'CdkFilePublishingRole', { + roleName, + assumedBy: new iam.AccountPrincipal(this.account), + }); + + this.fileRoleManifestArn = Stack.of(this).formatArn({ partition: '${AWS::Partition}', region: '', // iam is global service: 'iam', resource: 'role', - resourceName: this.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_NAME, + resourceName: roleName, arnFormat: ArnFormat.SLASH_RESOURCE_NAME, }); } - private createFilePublishingRole() { - const roleName = this.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_NAME; - const role = new iam.Role(this, this.fileAssetPublishingRoleId, { + private ensureImagePublishingRole() { + // It may end up setting imageRole to undefined, but at least we tried + if (this.didImageRole) { + return; + } + this.didImageRole = true; + + if (this.providedImageRole) { + // Override + this.imageRoleManifestArn = this.providedImageRole._arnForAssetManifest(); + const cfnArn = this.providedImageRole._arnForCloudFormation(); + this.imageRole = cfnArn ? iam.Role.fromRoleArn(this, 'CdkImagePublishingRole', cfnArn) : undefined; + return; + } + + const roleName = this.imageRoleName; + this.imageRole = new iam.Role(this, 'CdkImagePublishingRole', { roleName, assumedBy: new iam.AccountPrincipal(this.account), }); - return role; - } - - private getImagePublishingRoleArn(): string { - if (this.imageAssetPublishingRoleArn) { - return this.imageAssetPublishingRoleArn; - } - const role = this.node.tryFindChild(this.imageAssetPublishingRoleId) as iam.Role; - if (role === undefined) { - throw new Error('Cannot call getImagePublishingRoleArn before createImagePublishingRole'); - } - return Stack.of(this).formatArn({ + this.imageRoleManifestArn = Stack.of(this).formatArn({ partition: '${AWS::Partition}', region: '', // iam is global service: 'iam', resource: 'role', - resourceName: this.DEFAULT_IMAGE_ASSET_PUBISHING_ROLE_NAME, + resourceName: roleName, arnFormat: ArnFormat.SLASH_RESOURCE_NAME, }); } - private createImagePublishingRole() { - const roleName = this.DEFAULT_IMAGE_ASSET_PUBISHING_ROLE_NAME; - const role = new iam.Role(this, this.imageAssetPublishingRoleId, { - roleName, - assumedBy: new iam.AccountPrincipal(this.account), - }); - return role; - } - private createBucketKey(): kms.IKey { - const bucketKeyId = 'BucketKey'; - const key = this.node.tryFindChild(bucketKeyId) as kms.IKey ?? new kms.Key(this, bucketKeyId, { - alias: `alias/cdkstagingkey/${this.account}-${this.region}-${this.appId}`, + return new kms.Key(this, 'BucketKey', { + alias: `alias/cdk-${this.appId}-staging`, admins: [new iam.AccountPrincipal(this.account)], }); - return key; } private getCreateBucket() { - const stagingBucketName = this.stagingBucketName ?? `cdk-${this.account}-${this.region}-${this.appId.toLocaleLowerCase()}`; + const stagingBucketName = this.stagingBucketName ?? `cdk-${this.appId}-staging-${this.account}-${this.region}`; const bucketId = 'CdkStagingBucket'; const createdBucket = this.node.tryFindChild(bucketId) as s3.Bucket; if (createdBucket) { return stagingBucketName; } - // Create the role that the KMS key depends on - const role = this.createFilePublishingRole(); - // Create the KMS key that the bucket depends on + this.ensureFilePublishingRole(); const key = this.createBucketKey(); // Create the bucket once the dependencies have been created @@ -277,7 +289,10 @@ export class DefaultStagingStack extends Stack implements IStagingStack { // Many AWS account safety checkers will complain when SSL isn't enforced enforceSSL: true, }); - bucket.grantReadWrite(role); + + if (this.fileRole) { + bucket.grantReadWrite(this.fileRole); + } if (this.deployRoleArn) { bucket.addToResourcePolicy(new iam.PolicyStatement({ @@ -314,9 +329,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { } // Create image publishing role if it doesn't exist - this.node.tryFindChild(this.imageAssetPublishingRoleId) as iam.Role ?? this.createImagePublishingRole(); - - // TODO: grant permissions to the role + this.ensureImagePublishingRole(); const repoName = `${asset.assetName}`.replace('.', '-'); // TODO: actually sanitize if (this.stagingRepos[asset.assetName] === undefined) { @@ -324,22 +337,31 @@ export class DefaultStagingStack extends Stack implements IStagingStack { repositoryName: repoName, // lifecycleRules: this.repositoryLifecycleRules[asset.assetName], }); + if (this.imageRole) { + this.stagingRepos[asset.assetName].grantPullPush(this.imageRole); + } } return repoName; } public addFile(asset: FileAssetSource): FileStagingLocation { + // Has side effects so must go first + const bucketName = this.getCreateBucket(); + return { - bucketName: this.getCreateBucket(), - assumeRoleArn: this.getFilePublishingRoleArn(), + bucketName, + assumeRoleArn: this.fileRoleManifestArn, prefix: asset.ephemeral ? EPHEMERAL_PREFIX : undefined, }; } public addDockerImage(asset: DockerImageAssetSource): ImageStagingLocation { + // Has side effects so must go first + const repoName = this.getCreateRepo(asset); + return { - repoName: this.getCreateRepo(asset), - assumeRoleArn: this.getImagePublishingRoleArn(), + repoName, + assumeRoleArn: this.imageRoleManifestArn, }; } } diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/staging-stack.ts index a319b968abb0e..843f176e82a95 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/staging-stack.ts @@ -110,4 +110,11 @@ export interface ObtainStagingResourcesContext { * @default - Deploy role ARN is unknown */ readonly deployRoleArn?: string; + + /** + * The qualifier passed to the synthesizer + * + * The staging stack shouldn't need this, but it might. + */ + readonly qualifier: string; } \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts index dc929f9d11af9..2fa8ccd3bab43 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts @@ -1,7 +1,7 @@ /* eslint-disable jest/no-commented-out-tests */ import * as fs from 'fs'; import { App, Stack, CfnResource, FileAssetPackaging, Token, Lazy, Duration } from 'aws-cdk-lib'; -import { Template, Match } from 'aws-cdk-lib/assertions'; +import { Template } from 'aws-cdk-lib/assertions'; import * as cxschema from 'aws-cdk-lib/cloud-assembly-schema'; import { evaluateCFN } from 'aws-cdk-lib/core/test/evaluate-cfn'; import { APP_ID, CFN_CONTEXT, TestAppScopedStagingSynthesizer, isAssetManifest, last } from './util'; @@ -154,31 +154,6 @@ describe(AppStagingSynthesizer, () => { expect(evalCFN(location1.bucketName)).toEqual(evalCFN(location2.bucketName)); }); - test('can configure bucket prefix', () => { - // GIVEN - app = new App({ - defaultStackSynthesizer: TestAppScopedStagingSynthesizer.stackPerEnv({ - bucketPrefix: 'prefix', - }), - }); - stack = new Stack(app, 'Stack', { - env: { - account: '000000000000', - region: 'us-west-2', - }, - }); - - // WHEN - const location = stack.synthesizer.addFileAsset({ - fileName: __filename, - packaging: FileAssetPackaging.FILE, - sourceHash: 'abcdef', - }); - - // THEN - assets have the same location - expect(evalCFN(location.objectKey)).toEqual('prefixabcdef.js'); - }); - describe('ephemeral assets', () => { test('ephemeral assets have the \'eph-\' prefix', () => { // WHEN @@ -196,9 +171,7 @@ describe(AppStagingSynthesizer, () => { test('ephemeral assets do not get specified bucketPrefix', () => { // GIVEN app = new App({ - defaultStackSynthesizer: TestAppScopedStagingSynthesizer.stackPerEnv({ - bucketPrefix: 'prefix', - }), + defaultStackSynthesizer: TestAppScopedStagingSynthesizer.stackPerEnv({}), }); stack = new Stack(app, 'Stack', { env: { @@ -216,7 +189,7 @@ describe(AppStagingSynthesizer, () => { }); // THEN - asset has bucket prefix - expect(evalCFN(location.objectKey)).toEqual('eph-abcdef.js'); + expect(evalCFN(location.objectKey)).toEqual('eph/abcdef.js'); }); test('s3 bucket has lifecycle rule on ephemeral assets by default', () => { @@ -246,11 +219,7 @@ describe(AppStagingSynthesizer, () => { // GIVEN app = new App({ defaultStackSynthesizer: TestAppScopedStagingSynthesizer.stackPerEnv({ - ephemeralFileAssetLifecycleRule: { - prefix: 'eph-', - objectSizeGreaterThan: 10000, - expiration: Duration.days(1), - }, + handoverFileAssetLifetime: Duration.days(1), }), }); stack = new Stack(app, 'Stack', { @@ -273,61 +242,12 @@ describe(AppStagingSynthesizer, () => { LifecycleConfiguration: { Rules: [{ ExpirationInDays: 1, - Prefix: 'eph-', + Prefix: 'eph/', Status: 'Enabled', - ObjectSizeGreaterThan: 10000, }], }, }); }); - - test('customized lifecycle rule must have correct prefix', () => { - // GIVEN - app = new App({ - defaultStackSynthesizer: TestAppScopedStagingSynthesizer.stackPerEnv({ - ephemeralFileAssetLifecycleRule: { - objectSizeGreaterThan: 10000, - expiration: Duration.days(1), - }, - }), - }); - expect(() => { - new Stack(app, 'Stack', { - env: { - account: '000000000000', - region: 'us-west-2', - }, - }); - }).toThrowError('ephemeralAssetLifecycleRule must contain "prefix: \'eph-\'" but got "prefix: undefined". This prefix is the only way to identify ephemeral assets.'); - }); - - test('lifecycle rule on ephemeral assets can be turned off', () => { - // GIVEN - app = new App({ - defaultStackSynthesizer: TestAppScopedStagingSynthesizer.stackPerEnv({ - retainEphemeralFileAssets: true, - }), - }); - stack = new Stack(app, 'Stack', { - env: { - account: '000000000000', - region: 'us-west-2', - }, - }); - new CfnResource(stack, 'Resource', { - type: 'Some::Resource', - }); - - // WHEN - const asm = app.synth(); - - // THEN - const stagingStackArtifact = asm.getStackArtifact('StagingStack000000000000us-west-2'); - - Template.fromJSON(stagingStackArtifact.template).hasResourceProperties('AWS::S3::Bucket', { - LifecycleConfiguration: Match.absent(), - }); - }); }); // test('add docker image asset', () => { diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts index cc1f87b2b9926..cc0bdfb319569 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts @@ -1,9 +1,9 @@ +import * as fs from 'fs'; import { App, Stack, CfnResource } from 'aws-cdk-lib'; +import * as cxschema from 'aws-cdk-lib/cloud-assembly-schema'; import { isAssetManifest } from 'aws-cdk-lib/pipelines/lib/private/cloud-assembly-internals'; -import { AppStagingSynthesizer, BootstrapRole } from '../lib'; import { APP_ID, CLOUDFORMATION_EXECUTION_ROLE, DEPLOY_ACTION_ROLE, LOOKUP_ROLE } from './util'; -import * as cxschema from 'aws-cdk-lib/cloud-assembly-schema'; -import * as fs from 'fs'; +import { AppStagingSynthesizer, BootstrapRole } from '../lib'; describe('Boostrap Roles', () => { test('Can supply existing arns for bootstrapped roles', () => { @@ -11,10 +11,10 @@ describe('Boostrap Roles', () => { const app = new App({ defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: APP_ID, - bootstrapRoles: { + deploymentRoles: { cloudFormationExecutionRole: BootstrapRole.fromRoleArn(CLOUDFORMATION_EXECUTION_ROLE), lookupRole: BootstrapRole.fromRoleArn(LOOKUP_ROLE), - deploymentActionRole: BootstrapRole.fromRoleArn(DEPLOY_ACTION_ROLE), + deploymentRole: BootstrapRole.fromRoleArn(DEPLOY_ACTION_ROLE), }, }), }); @@ -45,9 +45,7 @@ describe('Boostrap Roles', () => { const app = new App({ defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: APP_ID, - stagingRoles: { - fileAssetPublishingRole: BootstrapRole.fromRoleArn('arn'), - }, + fileAssetPublishingRole: BootstrapRole.fromRoleArn('arn'), }), }); const stack = new Stack(app, 'Stack', { @@ -77,10 +75,10 @@ describe('Boostrap Roles', () => { const app = new App({ defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: APP_ID, - bootstrapRoles: { + deploymentRoles: { cloudFormationExecutionRole: BootstrapRole.cliCredentials(), lookupRole: BootstrapRole.cliCredentials(), - deploymentActionRole: BootstrapRole.cliCredentials(), + deploymentRole: BootstrapRole.cliCredentials(), }, }), }); @@ -110,9 +108,7 @@ describe('Boostrap Roles', () => { const app = new App({ defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: APP_ID, - stagingRoles: { - fileAssetPublishingRole: BootstrapRole.cliCredentials(), - }, + fileAssetPublishingRole: BootstrapRole.cliCredentials(), }), }); diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/util.ts b/packages/@aws-cdk/app-staging-synthesizer/test/util.ts index d947dd8c21c88..ece6e96676f9b 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/util.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/util.ts @@ -1,5 +1,5 @@ -import { DefaultResourcesOptions, AppStagingSynthesizer, BootstrapRole } from '../lib'; import * as cxapi from 'aws-cdk-lib/cx-api'; +import { DefaultResourcesOptions, AppStagingSynthesizer, BootstrapRole } from '../lib'; export const CFN_CONTEXT = { 'AWS::Region': 'the_region', @@ -23,9 +23,9 @@ export class TestAppScopedStagingSynthesizer { public static stackPerEnv(props: Partial = {}): AppStagingSynthesizer { return AppStagingSynthesizer.defaultResources({ appId: props.appId ?? APP_ID, - bootstrapRoles: { + deploymentRoles: { cloudFormationExecutionRole: BootstrapRole.fromRoleArn(CLOUDFORMATION_EXECUTION_ROLE), - deploymentActionRole: BootstrapRole.fromRoleArn(DEPLOY_ACTION_ROLE), + deploymentRole: BootstrapRole.fromRoleArn(DEPLOY_ACTION_ROLE), lookupRole: BootstrapRole.fromRoleArn(LOOKUP_ROLE), }, ...props, diff --git a/yarn.lock b/yarn.lock index ff0ff5b4a2aee..873a273d434bb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2164,13 +2164,6 @@ resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== -"@types/fs-extra@^8.1.2": - version "8.1.2" - resolved "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.2.tgz#7125cc2e4bdd9bd2fc83005ffdb1d0ba00cca61f" - integrity sha512-SvSrYXfWSc7R4eqnOzbQF4TZmfpNSM9FrSWLU3EUnWBuyZqNBOrv1B1JA3byUDPUl9z4Ab3jeZG2eDdySlgNMg== - dependencies: - "@types/node" "*" - "@types/fs-extra@^9.0.13": version "9.0.13" resolved "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz#7594fbae04fe7f1918ce8b3d213f74ff44ac1f45" @@ -2228,7 +2221,7 @@ jest-matcher-utils "^27.0.0" pretty-format "^27.0.0" -"@types/json-schema@*", "@types/json-schema@^7.0.11", "@types/json-schema@^7.0.7", "@types/json-schema@^7.0.9": +"@types/json-schema@*", "@types/json-schema@^7.0.7", "@types/json-schema@^7.0.9": version "7.0.11" resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== From b642943a5eb860ae3e6de828fdb74fdd55c733db Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Thu, 13 Apr 2023 14:48:38 +0200 Subject: [PATCH 072/120] awslint fixes --- .../@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts | 3 +++ .../app-staging-synthesizer/lib/default-staging-stack.ts | 2 ++ 2 files changed, 5 insertions(+) diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts index bca9fef0a3819..d6971c1fa5523 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts @@ -24,6 +24,9 @@ export class BootstrapRole { private constructor(private readonly roleArn: string) {} + /** + * Whether or not this is object was created using BootstrapRole.cliCredentials() + */ public isCliCredentials() { return this.roleArn === BootstrapRole.CLI_CREDS; } diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts index 45dd675dfa2ff..3bba9141b71a3 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts @@ -78,6 +78,8 @@ export interface DefaultStagingStackProps extends DefaultStagingStackOptions, St * The ARN of the deploy action role, if given * * This role will need permissions to read from to the staging resources. + * + * @default - The CLI credentials are assumed, no additional permissions are granted. */ readonly deployRoleArn?: string; From 2a84b5c77fcace38cdf142e201bca3b0ca5c30e8 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Thu, 13 Apr 2023 16:13:18 +0200 Subject: [PATCH 073/120] Fix unit tests --- .../lib/app-staging-synthesizer.ts | 40 ++++++------ .../lib/bootstrap-roles.ts | 2 +- .../lib/default-staging-stack.ts | 14 +++-- .../lib/private/no-tokens.ts | 9 +++ .../test/app-staging-synthesizer.test.ts | 63 ++++++++----------- .../test/bootstrap-roles.test.ts | 15 +---- .../app-staging-synthesizer/test/util.ts | 2 +- .../stack-synthesizers/stack-synthesizer.ts | 6 +- 8 files changed, 70 insertions(+), 81 deletions(-) create mode 100644 packages/@aws-cdk/app-staging-synthesizer/lib/private/no-tokens.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts index 3a09d797f79e1..6c55f47fa36dd 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts @@ -17,6 +17,7 @@ import { BootstrapRole, BootstrapRoles } from './bootstrap-roles'; import { DefaultStagingStack, DefaultStagingStackOptions } from './default-staging-stack'; import { PerEnvironmenStagingFactory } from './per-env-staging-factory'; import { AppScopedGlobal } from './private/app-global'; +import { validateNoTokens } from './private/no-tokens'; import { IStagingStack, IStagingStackFactory, ObtainStagingResourcesContext } from './staging-stack'; const AGNOSTIC_STACKS = new AppScopedGlobal(() => new Set()); @@ -121,10 +122,13 @@ export class AppStagingSynthesizer extends StackSynthesizer implements IReusable /** * Use the Default Staging Resources, creating a single stack per environment this app is deployed in */ - public static defaultResources(props: DefaultResourcesOptions) { + public static defaultResources(options: DefaultResourcesOptions) { + validateNoTokens(options, 'AppStagingSynthesizer'); + return AppStagingSynthesizer.customFactory({ - factory: DefaultStagingStack.factory(props), - deploymentRoles: props.deploymentRoles, + factory: DefaultStagingStack.factory(options), + deploymentRoles: options.deploymentRoles, + bootstrapQualifier: options.bootstrapQualifier, oncePerEnv: true, }); } @@ -135,6 +139,7 @@ export class AppStagingSynthesizer extends StackSynthesizer implements IReusable public static customResources(options: CustomResourcesOptions) { return AppStagingSynthesizer.customFactory({ deploymentRoles: options.deploymentRoles, + bootstrapQualifier: options.bootstrapQualifier, oncePerEnv: false, factory: { obtainStagingResources() { @@ -151,14 +156,14 @@ export class AppStagingSynthesizer extends StackSynthesizer implements IReusable * By default, `oncePerEnv = true`, which means that a new instance of the IStagingStack * will be created in new environments. Set `oncePerEnv = false` to turn off that behavior. */ - public static customFactory(props: CustomFactoryOptions) { - const oncePerEnv = props.oncePerEnv ?? true; - const factory = oncePerEnv ? new PerEnvironmenStagingFactory(props.factory) : props.factory; + public static customFactory(options: CustomFactoryOptions) { + const oncePerEnv = options.oncePerEnv ?? true; + const factory = oncePerEnv ? new PerEnvironmenStagingFactory(options.factory) : options.factory; return new AppStagingSynthesizer({ factory, - bootstrapQualifier: props.bootstrapQualifier, - deploymentRoles: props.deploymentRoles, + bootstrapQualifier: options.bootstrapQualifier, + deploymentRoles: options.deploymentRoles, }); } @@ -191,8 +196,8 @@ export class AppStagingSynthesizer extends StackSynthesizer implements IReusable const context: ObtainStagingResourcesContext = { environmentString: [ - Token.isUnresolved(stack.region) ? 'REGION' : stack.region, Token.isUnresolved(stack.account) ? 'ACCOUNT' : stack.account, + Token.isUnresolved(stack.region) ? 'REGION' : stack.region, ].join('-'), deployRoleArn: deployRole._arnForCloudFormation(), qualifier, @@ -294,12 +299,9 @@ interface BoundAppStagingSynthesizerProps { class BoundAppStagingSynthesizer extends StackSynthesizer implements IBoundAppStagingSynthesizer { private readonly stagingStack: IStagingStack; private readonly assetManifest = new AssetManifestBuilder(); - private readonly lookupRoleArn?: string; - private readonly cloudFormationExecutionRoleArn?: string; - private readonly deploymentActionRoleArn?: string; private readonly qualifier: string; - constructor(stack: Stack, props: BoundAppStagingSynthesizerProps) { + constructor(stack: Stack, private readonly props: BoundAppStagingSynthesizerProps) { super(); super.bind(stack); @@ -315,19 +317,19 @@ class BoundAppStagingSynthesizer extends StackSynthesizer implements IBoundAppSt } public synthesize(session: ISynthesisSession): void { - const templateAssetSource = this.synthesizeTemplate(session, this.lookupRoleArn); + const templateAssetSource = this.synthesizeTemplate(session, this.props.lookupRole?._arnForCloudAssembly()); const templateAsset = this.addFileAsset(templateAssetSource); const assetManifestId = this.assetManifest.emitManifest(this.boundStack, session); + const lookupRoleArn = this.props.lookupRole?._arnForCloudAssembly(); + this.emitArtifact(session, { - assumeRoleArn: this.deploymentActionRoleArn, + assumeRoleArn: this.props.deployRole?._arnForCloudAssembly(), additionalDependencies: [assetManifestId], stackTemplateAssetObjectUrl: templateAsset.s3ObjectUrlWithPlaceholders, - cloudFormationExecutionRoleArn: this.cloudFormationExecutionRoleArn, - lookupRole: this.lookupRoleArn ? { - arn: this.lookupRoleArn, - }: undefined, + cloudFormationExecutionRoleArn: this.props.cloudFormationExecutionRole?._arnForCloudAssembly(), + lookupRole: lookupRoleArn ? { arn: lookupRoleArn } : undefined, }); } diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts index d6971c1fa5523..53cf226f3b26b 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts @@ -41,7 +41,7 @@ export class BootstrapRole { /** * @internal */ - public _arnForAssetManifest() { + public _arnForCloudAssembly() { return this.isCliCredentials() ? undefined : translateCfnTokenToAssetToken(this.roleArn); } diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts index 3bba9141b71a3..0ea7bf28f8500 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts @@ -17,7 +17,7 @@ import { StringSpecializer } from 'aws-cdk-lib/core/lib/helpers-internal'; import { BootstrapRole } from './bootstrap-roles'; import { FileStagingLocation, IStagingStack, IStagingStackFactory, ImageStagingLocation } from './staging-stack'; -const EPHEMERAL_PREFIX = 'handover/'; +const EPHEMERAL_PREFIX = 'handoff/'; /** * User configurable options to the DefaultStagingStack @@ -53,7 +53,7 @@ export interface DefaultStagingStackOptions { readonly imageAssetPublishingRole?: BootstrapRole; /** - * The lifetime for handover file assets + * The lifetime for handoff file assets * * Assets that are only necessary at deployment time (for instance, * CloudFormation templates and Lambda source code bundles) will be @@ -67,7 +67,7 @@ export interface DefaultStagingStackOptions { * * @default - Duration.days(30) */ - readonly handoverFileAssetLifetime?: Duration; + readonly handoffFileAssetLifetime?: Duration; } /** @@ -209,7 +209,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { private ensureFilePublishingRole() { if (this.providedFileRole) { // Override - this.fileRoleManifestArn = this.providedFileRole._arnForAssetManifest(); + this.fileRoleManifestArn = this.providedFileRole._arnForCloudAssembly(); const cfnArn = this.providedFileRole._arnForCloudFormation(); this.fileRole = cfnArn ? iam.Role.fromRoleArn(this, 'CdkFilePublishingRole', cfnArn) : undefined; return; @@ -240,7 +240,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { if (this.providedImageRole) { // Override - this.imageRoleManifestArn = this.providedImageRole._arnForAssetManifest(); + this.imageRoleManifestArn = this.providedImageRole._arnForCloudAssembly(); const cfnArn = this.providedImageRole._arnForCloudFormation(); this.imageRole = cfnArn ? iam.Role.fromRoleArn(this, 'CdkImagePublishingRole', cfnArn) : undefined; return; @@ -316,7 +316,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { bucket.addLifecycleRule({ prefix: EPHEMERAL_PREFIX, - expiration: this.props.handoverFileAssetLifetime ?? Duration.days(30), + expiration: this.props.handoffFileAssetLifetime ?? Duration.days(30), }); return stagingBucketName; @@ -354,6 +354,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { bucketName, assumeRoleArn: this.fileRoleManifestArn, prefix: asset.ephemeral ? EPHEMERAL_PREFIX : undefined, + dependencyStack: this, }; } @@ -364,6 +365,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { return { repoName, assumeRoleArn: this.imageRoleManifestArn, + dependencyStack: this, }; } } diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/private/no-tokens.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/private/no-tokens.ts new file mode 100644 index 0000000000000..294866b818e86 --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/private/no-tokens.ts @@ -0,0 +1,9 @@ +import { StringSpecializer } from 'aws-cdk-lib/core/lib/helpers-internal'; + +export function validateNoTokens(props: A, context: string) { + for (const [key, value] of Object.entries(props)) { + if (typeof value === 'string') { + StringSpecializer.validateNoTokens(value, `${context} property '${key}'`); + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts index 2fa8ccd3bab43..7bdac4053b56f 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts @@ -1,7 +1,7 @@ /* eslint-disable jest/no-commented-out-tests */ import * as fs from 'fs'; import { App, Stack, CfnResource, FileAssetPackaging, Token, Lazy, Duration } from 'aws-cdk-lib'; -import { Template } from 'aws-cdk-lib/assertions'; +import { Match, Template } from 'aws-cdk-lib/assertions'; import * as cxschema from 'aws-cdk-lib/cloud-assembly-schema'; import { evaluateCFN } from 'aws-cdk-lib/core/test/evaluate-cfn'; import { APP_ID, CFN_CONTEXT, TestAppScopedStagingSynthesizer, isAssetManifest, last } from './util'; @@ -38,7 +38,7 @@ describe(AppStagingSynthesizer, () => { const stackArtifact = asm.getStackArtifact('Stack'); const templateObjectKey = last(stackArtifact.stackTemplateAssetObjectUrl?.split('/')); - expect(stackArtifact.stackTemplateAssetObjectUrl).toEqual(`s3://cdk-000000000000-us-east-1-${APP_ID.toLocaleLowerCase()}/${templateObjectKey}`); + expect(stackArtifact.stackTemplateAssetObjectUrl).toEqual(`s3://cdk-${APP_ID}-staging-000000000000-us-east-1/${templateObjectKey}`); // THEN - the template is in the asset manifest const manifestArtifact = asm.artifacts.filter(isAssetManifest)[0]; @@ -51,10 +51,10 @@ describe(AppStagingSynthesizer, () => { source: { path: 'Stack.template.json', packaging: 'file' }, destinations: { '000000000000-us-east-1': { - bucketName: `cdk-000000000000-us-east-1-${APP_ID.toLocaleLowerCase()}`, + bucketName: `cdk-${APP_ID}-staging-000000000000-us-east-1`, objectKey: templateObjectKey, region: 'us-east-1', - assumeRoleArn: `arn:\${AWS::Partition}:iam::000000000000:role/cdk-file-publishing-role-us-east-1-${APP_ID}`, + assumeRoleArn: `arn:\${AWS::Partition}:iam::000000000000:role/cdk-${APP_ID}-file-publishing-role-us-east-1`, }, }, }); @@ -85,7 +85,7 @@ describe(AppStagingSynthesizer, () => { const stackArtifact = asm.getStackArtifact('Stack2'); const templateObjectKey = last(stackArtifact.stackTemplateAssetObjectUrl?.split('/')); - expect(stackArtifact.stackTemplateAssetObjectUrl).toEqual(`s3://cdk-${accountToken}-${regionToken}-${APP_ID.toLocaleLowerCase()}/${templateObjectKey}`); + expect(stackArtifact.stackTemplateAssetObjectUrl).toEqual(`s3://cdk-${APP_ID}-staging-${accountToken}-${regionToken}/${templateObjectKey}`); // THEN - the template is in the asset manifest const manifestArtifact = asm.artifacts.filter(isAssetManifest)[0]; @@ -98,10 +98,10 @@ describe(AppStagingSynthesizer, () => { source: { path: 'Stack2.template.json', packaging: 'file' }, destinations: { '111111111111-us-east-2': { - bucketName: `cdk-111111111111-us-east-2-${APP_ID.toLocaleLowerCase()}`, + bucketName: `cdk-${APP_ID}-staging-111111111111-us-east-2`, objectKey: templateObjectKey, region: 'us-east-2', - assumeRoleArn: `arn:\${AWS::Partition}:iam::111111111111:role/cdk-file-publishing-role-us-east-2-${APP_ID}`, + assumeRoleArn: `arn:\${AWS::Partition}:iam::111111111111:role/cdk-${APP_ID}-file-publishing-role-us-east-2`, }, }, }); @@ -118,7 +118,7 @@ describe(AppStagingSynthesizer, () => { // THEN - we have a stack dependency on the staging stack expect(stack.dependencies.length).toEqual(1); const depStack = stack.dependencies[0]; - expect(depStack.stackName).toEqual(`StagingStack${APP_ID}`); + expect(depStack.stackName).toEqual(`StagingStack-${APP_ID}`); }); test('add file asset', () => { @@ -130,8 +130,8 @@ describe(AppStagingSynthesizer, () => { }); // THEN - we have a fixed asset location - expect(evalCFN(location.bucketName)).toEqual(`cdk-000000000000-us-east-1-${APP_ID.toLocaleLowerCase()}`); - expect(evalCFN(location.httpUrl)).toEqual(`https://s3.us-east-1.domain.aws/cdk-000000000000-us-east-1-${APP_ID.toLocaleLowerCase()}/abcdef.js`); + expect(evalCFN(location.bucketName)).toEqual(`cdk-${APP_ID}-staging-000000000000-us-east-1`); + expect(evalCFN(location.httpUrl)).toEqual(`https://s3.us-east-1.domain.aws/cdk-${APP_ID}-staging-000000000000-us-east-1/abcdef.js`); // THEN - object key contains source hash somewhere expect(location.objectKey.indexOf('abcdef')).toBeGreaterThan(-1); @@ -155,7 +155,7 @@ describe(AppStagingSynthesizer, () => { }); describe('ephemeral assets', () => { - test('ephemeral assets have the \'eph-\' prefix', () => { + test('ephemeral assets have the \'handoff/\' prefix', () => { // WHEN const location = stack.synthesizer.addFileAsset({ fileName: __filename, @@ -165,7 +165,7 @@ describe(AppStagingSynthesizer, () => { }); // THEN - asset has bucket prefix - expect(evalCFN(location.objectKey)).toEqual('eph-abcdef.js'); + expect(evalCFN(location.objectKey)).toEqual('handoff/abcdef.js'); }); test('ephemeral assets do not get specified bucketPrefix', () => { @@ -189,7 +189,7 @@ describe(AppStagingSynthesizer, () => { }); // THEN - asset has bucket prefix - expect(evalCFN(location.objectKey)).toEqual('eph/abcdef.js'); + expect(evalCFN(location.objectKey)).toEqual('handoff/abcdef.js'); }); test('s3 bucket has lifecycle rule on ephemeral assets by default', () => { @@ -202,15 +202,15 @@ describe(AppStagingSynthesizer, () => { const asm = app.synth(); // THEN - const stagingStackArtifact = asm.getStackArtifact('StagingStack000000000000us-east-1'); + const stagingStackArtifact = asm.getStackArtifact(`StagingStack-${APP_ID}-000000000000-us-east-1`); Template.fromJSON(stagingStackArtifact.template).hasResourceProperties('AWS::S3::Bucket', { LifecycleConfiguration: { - Rules: [{ - ExpirationInDays: 10, - Prefix: 'eph-', + Rules: Match.arrayWith([{ + ExpirationInDays: 30, + Prefix: 'handoff/', Status: 'Enabled', - }], + }]), }, }); }); @@ -219,7 +219,7 @@ describe(AppStagingSynthesizer, () => { // GIVEN app = new App({ defaultStackSynthesizer: TestAppScopedStagingSynthesizer.stackPerEnv({ - handoverFileAssetLifetime: Duration.days(1), + handoffFileAssetLifetime: Duration.days(1), }), }); stack = new Stack(app, 'Stack', { @@ -236,15 +236,15 @@ describe(AppStagingSynthesizer, () => { const asm = app.synth(); // THEN - const stagingStackArtifact = asm.getStackArtifact('StagingStack000000000000us-west-2'); + const stagingStackArtifact = asm.getStackArtifact(`StagingStack-${APP_ID}-000000000000-us-west-2`); Template.fromJSON(stagingStackArtifact.template).hasResourceProperties('AWS::S3::Bucket', { LifecycleConfiguration: { - Rules: [{ + Rules: Match.arrayWith([{ ExpirationInDays: 1, - Prefix: 'eph/', + Prefix: 'handoff/', Status: 'Enabled', - }], + }]), }, }); }); @@ -314,20 +314,7 @@ describe(AppStagingSynthesizer, () => { // GIVEN - App with Stack with specific environment // THEN - Expect environment agnostic stack to fail - expect(() => new Stack(app, 'NoEnvStack')).toThrowError('AppStagingSynthesizer cannot synthesize CDK Apps with BOTH environment-agnostic stacks and set environment stacks.\nPlease either specify environments for all stacks or no stacks in the CDK App.'); - }); - - test('metadata for cdk-env is only added once on the app', () => { - // GIVEN - Additional App with specific environment - new Stack(app, 'EnvStack', { - env: { - account: '000000000000', - region: 'us-west-2', - }, - }); - - // THEN - We only add the env metadata once - expect(app.node.metadata.filter((m) => m.type === 'cdk-env').length).toEqual(1); + expect(() => new Stack(app, 'NoEnvStack')).toThrowError(/It is not safe to use AppStagingSynthesizer/); }); }); @@ -336,7 +323,7 @@ describe(AppStagingSynthesizer, () => { defaultStackSynthesizer: TestAppScopedStagingSynthesizer.stackPerEnv({ appId: Lazy.string({ produce: () => 'appId' }), }), - })).toThrowError(/AppStagingSynthesizer property 'appId' cannot contain tokens;/); + })).toThrowError(/AppStagingSynthesizer property 'appId' may not contain tokens;/); }); /** diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts index cc0bdfb319569..1f4410052853b 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts @@ -45,7 +45,7 @@ describe('Boostrap Roles', () => { const app = new App({ defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: APP_ID, - fileAssetPublishingRole: BootstrapRole.fromRoleArn('arn'), + fileAssetPublishingRole: BootstrapRole.fromRoleArn('arn:aws:iam::123456789012:role/S3Access'), }), }); const stack = new Stack(app, 'Stack', { @@ -67,7 +67,7 @@ describe('Boostrap Roles', () => { expect(manifestArtifact).toBeDefined(); const manifest: cxschema.AssetManifest = JSON.parse(fs.readFileSync(manifestArtifact.file, { encoding: 'utf-8' })); const firstFile: any = (manifest.files ? manifest.files[Object.keys(manifest.files)[0]] : undefined) ?? {}; - expect(firstFile.destinations['000000000000-us-east-1'].assumeRoleArn).toEqual('arn'); + expect(firstFile.destinations['000000000000-us-east-1'].assumeRoleArn).toEqual('arn:aws:iam::123456789012:role/S3Access'); }); test('bootstrap roles can be specified as current cli credentials instead', () => { @@ -104,17 +104,6 @@ describe('Boostrap Roles', () => { expect(stackArtifact.assumeRoleArn).toBeUndefined(); }); - test('staging roles cannot be specified as cli credentials', () => { - const app = new App({ - defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ - appId: APP_ID, - fileAssetPublishingRole: BootstrapRole.cliCredentials(), - }), - }); - - expect(() => new Stack(app, 'Stack')).toThrowError('fileAssetPublishingRole and dockerAssetPublishingRole cannot be specified as cliCredentials(). Please supply an arn to reference an existing IAM role.'); - }); - test('qualifier is resolved in the synthesizer', () => { const app = new App({ defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/util.ts b/packages/@aws-cdk/app-staging-synthesizer/test/util.ts index ece6e96676f9b..b84b3382e3c01 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/util.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/util.ts @@ -6,7 +6,7 @@ export const CFN_CONTEXT = { 'AWS::AccountId': 'the_account', 'AWS::URLSuffix': 'domain.aws', }; -export const APP_ID = 'appId'; +export const APP_ID = 'appid'; export const CLOUDFORMATION_EXECUTION_ROLE = 'role'; export const DEPLOY_ACTION_ROLE = 'role'; export const LOOKUP_ROLE = 'role'; diff --git a/packages/aws-cdk-lib/core/lib/stack-synthesizers/stack-synthesizer.ts b/packages/aws-cdk-lib/core/lib/stack-synthesizers/stack-synthesizer.ts index 8d544130ca004..fff85006eb39e 100644 --- a/packages/aws-cdk-lib/core/lib/stack-synthesizers/stack-synthesizer.ts +++ b/packages/aws-cdk-lib/core/lib/stack-synthesizers/stack-synthesizer.ts @@ -1,14 +1,14 @@ import * as fs from 'fs'; import * as path from 'path'; -import * as cxschema from '../../../cloud-assembly-schema'; -import * as cxapi from '../../../cx-api'; -import { resolvedOr } from '../helpers-internal/string-specializer'; import { addStackArtifactToAssembly, contentHash } from './_shared'; import { IStackSynthesizer, ISynthesisSession } from './types'; +import * as cxschema from '../../../cloud-assembly-schema'; +import * as cxapi from '../../../cx-api'; import { DockerImageAssetLocation, DockerImageAssetSource, FileAssetLocation, FileAssetSource, FileAssetPackaging } from '../assets'; import { Fn } from '../cfn-fn'; import { CfnParameter } from '../cfn-parameter'; import { CfnRule } from '../cfn-rule'; +import { resolvedOr } from '../helpers-internal/string-specializer'; import { Stack } from '../stack'; /** From 2331ef4b24fb64d20db0c78462b5ea715d4d5452 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 13 Apr 2023 11:41:08 -0400 Subject: [PATCH 074/120] asset dependencies --- .../lib/app-staging-synthesizer.ts | 2 +- .../asset-manifest-builder.ts | 2 ++ .../aws-cdk-lib/cx-api/lib/cloud-assembly.ts | 4 +-- .../cx-api/test/cloud-assembly.test.ts | 7 +++++ .../test/fixtures/asset-depends/manifest.json | 29 +++++++++++++++++++ .../test/fixtures/asset-depends/template.json | 7 +++++ 6 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 packages/aws-cdk-lib/cx-api/test/fixtures/asset-depends/manifest.json create mode 100644 packages/aws-cdk-lib/cx-api/test/fixtures/asset-depends/template.json diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts index 2aeba970e4424..6e7290894c430 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts @@ -358,7 +358,7 @@ class BoundAppStagingSynthesizer extends StackSynthesizer implements IBoundAppSt const templateAssetSource = this.synthesizeTemplate(session, this.lookupRoleArn); const templateAsset = this.addFileAsset(templateAssetSource); - const assetManifestId = this.assetManifest.emitManifest(this.boundStack, session); + const assetManifestId = this.assetManifest.emitManifest(this.boundStack, session, {}, [this.stagingStack.dependencyStack.artifactId]); this.emitArtifact(session, { assumeRoleArn: this.deploymentActionRoleArn, diff --git a/packages/aws-cdk-lib/core/lib/stack-synthesizers/asset-manifest-builder.ts b/packages/aws-cdk-lib/core/lib/stack-synthesizers/asset-manifest-builder.ts index 067826fa30bf4..0d3c8ffb1608a 100644 --- a/packages/aws-cdk-lib/core/lib/stack-synthesizers/asset-manifest-builder.ts +++ b/packages/aws-cdk-lib/core/lib/stack-synthesizers/asset-manifest-builder.ts @@ -131,6 +131,7 @@ export class AssetManifestBuilder { stack: Stack, session: ISynthesisSession, options: cxschema.AssetManifestOptions = {}, + dependencies: string[] = [], ): string { const artifactId = `${stack.artifactId}.assets`; const manifestFile = `${artifactId}.json`; @@ -150,6 +151,7 @@ export class AssetManifestBuilder { file: manifestFile, ...options, }, + dependencies: dependencies.length > 0 ? dependencies : undefined, }); return artifactId; diff --git a/packages/aws-cdk-lib/cx-api/lib/cloud-assembly.ts b/packages/aws-cdk-lib/cx-api/lib/cloud-assembly.ts index 61fc3cae92b77..1dde580b3ec87 100644 --- a/packages/aws-cdk-lib/cx-api/lib/cloud-assembly.ts +++ b/packages/aws-cdk-lib/cx-api/lib/cloud-assembly.ts @@ -1,13 +1,13 @@ import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; -import * as cxschema from '../../cloud-assembly-schema'; -import { LoadManifestOptions } from '../../cloud-assembly-schema'; import { CloudFormationStackArtifact } from './artifacts/cloudformation-artifact'; import { NestedCloudAssemblyArtifact } from './artifacts/nested-cloud-assembly-artifact'; import { TreeCloudArtifact } from './artifacts/tree-cloud-artifact'; import { CloudArtifact } from './cloud-artifact'; import { topologicalSort } from './toposort'; +import * as cxschema from '../../cloud-assembly-schema'; +import { LoadManifestOptions } from '../../cloud-assembly-schema'; /** * The name of the root manifest file of the assembly. diff --git a/packages/aws-cdk-lib/cx-api/test/cloud-assembly.test.ts b/packages/aws-cdk-lib/cx-api/test/cloud-assembly.test.ts index 804d1e43e16b6..b3965026a98fb 100644 --- a/packages/aws-cdk-lib/cx-api/test/cloud-assembly.test.ts +++ b/packages/aws-cdk-lib/cx-api/test/cloud-assembly.test.ts @@ -168,6 +168,13 @@ test('can read assembly with asset manifest', () => { expect(assembly.artifacts).toHaveLength(2); }); +test('can toposort assembly with asset dependency', () => { + const assembly = new CloudAssembly(path.join(FIXTURES, 'asset-depends')); + expect(assembly.stacks).toHaveLength(2); + expect(assembly.artifacts).toHaveLength(3); + expect(assembly.artifacts[0].id).toEqual('StagingStack'); +}); + test('getStackArtifact retrieves a stack by artifact id from a nested assembly', () => { const assembly = new CloudAssembly(path.join(FIXTURES, 'nested-assemblies')); diff --git a/packages/aws-cdk-lib/cx-api/test/fixtures/asset-depends/manifest.json b/packages/aws-cdk-lib/cx-api/test/fixtures/asset-depends/manifest.json new file mode 100644 index 0000000000000..3873aca91ac7b --- /dev/null +++ b/packages/aws-cdk-lib/cx-api/test/fixtures/asset-depends/manifest.json @@ -0,0 +1,29 @@ +{ + "version": "0.0.0", + "artifacts": { + "MyStackName": { + "type": "aws:cloudformation:stack", + "environment": "aws://37736633/us-region-1", + "properties": { + "templateFile": "template.json" + }, + "dependencies": ["AssetManifest"], + "metadata": { + } + }, + "AssetManifest": { + "type": "cdk:asset-manifest", + "properties": { + "file": "asset.json" + }, + "dependencies": ["StagingStack"] + }, + "StagingStack": { + "type": "aws:cloudformation:stack", + "environment": "aws://1111/us-region-1", + "properties": { + "templateFile": "template.json" + } + } + } +} diff --git a/packages/aws-cdk-lib/cx-api/test/fixtures/asset-depends/template.json b/packages/aws-cdk-lib/cx-api/test/fixtures/asset-depends/template.json new file mode 100644 index 0000000000000..284fd64cffc21 --- /dev/null +++ b/packages/aws-cdk-lib/cx-api/test/fixtures/asset-depends/template.json @@ -0,0 +1,7 @@ +{ + "Resources": { + "MyBucket": { + "Type": "AWS::S3::Bucket" + } + } +} \ No newline at end of file From b20cb04164b0c328984e2371dc325fb4dcba54fd Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Fri, 14 Apr 2023 08:49:43 -0400 Subject: [PATCH 075/120] wip of workgraph code. does not work yet, many errors --- .../aws-cdk-lib/cx-api/lib/cloud-assembly.ts | 2 +- packages/aws-cdk/lib/cdk-toolkit.ts | 5 +- packages/aws-cdk/lib/deploy.ts | 103 ++++++++++-------- packages/aws-cdk/lib/util/work-graph.ts | 75 +++++++++++++ .../test/cloud-assembly-trees/manifest.json | 4 + packages/aws-cdk/test/deploy.test.ts | 21 +++- 6 files changed, 158 insertions(+), 52 deletions(-) create mode 100644 packages/aws-cdk/lib/util/work-graph.ts create mode 100644 packages/aws-cdk/test/cloud-assembly-trees/manifest.json diff --git a/packages/aws-cdk-lib/cx-api/lib/cloud-assembly.ts b/packages/aws-cdk-lib/cx-api/lib/cloud-assembly.ts index 1dde580b3ec87..a0b85075c60a5 100644 --- a/packages/aws-cdk-lib/cx-api/lib/cloud-assembly.ts +++ b/packages/aws-cdk-lib/cx-api/lib/cloud-assembly.ts @@ -228,7 +228,7 @@ export class CloudAssembly { } } - return topologicalSort(result, x => x.id, x => x._dependencyIDs); + return topologicalSort(result, x => x.id, x => x._dependencyIDs); // TODO: remove redundant toposort } } diff --git a/packages/aws-cdk/lib/cdk-toolkit.ts b/packages/aws-cdk/lib/cdk-toolkit.ts index a0e5d8af06d61..a4fa10cbd5006 100644 --- a/packages/aws-cdk/lib/cdk-toolkit.ts +++ b/packages/aws-cdk/lib/cdk-toolkit.ts @@ -186,7 +186,8 @@ export class CdkToolkit { } const stacks = stackCollection.stackArtifacts; - const assetBuildTime = options.assetBuildTime ?? AssetBuildTime.ALL_BEFORE_DEPLOY; + const cloudArtifacts = stackCollection.assembly.assembly.artifacts; + const assetBuildTime = options.assetBuildTime ?? AssetBuildTime.ALL_BEFORE_DEPLOY; // TODO: deal with this const stackOutputs: { [key: string]: any } = { }; const outputsFile = options.outputsFile; @@ -336,7 +337,7 @@ export class CdkToolkit { } try { - await deployStacks(stacks, { concurrency, deployStack }); + await deployStacks(cloudArtifacts, { concurrency, deployStack }); } catch (e) { error('\n ❌ Deployment failed: %s', e); throw e; diff --git a/packages/aws-cdk/lib/deploy.ts b/packages/aws-cdk/lib/deploy.ts index 76bd1b579664f..1bb89637fd06b 100644 --- a/packages/aws-cdk/lib/deploy.ts +++ b/packages/aws-cdk/lib/deploy.ts @@ -1,69 +1,76 @@ import * as cxapi from '@aws-cdk/cx-api'; -import PQueue from 'p-queue'; +// import PQueue from 'p-queue'; +import { WorkGraph } from './util/work-graph'; type Options = { concurrency: number; deployStack: (stack: cxapi.CloudFormationStackArtifact) => Promise; }; -type DeploymentState = 'pending' | 'queued' | 'deploying' | 'completed' | 'failed' | 'skipped'; +// type DeploymentState = 'pending' | 'queued' | 'deploying' | 'completed' | 'failed' | 'skipped'; -export const deployStacks = async (stacks: cxapi.CloudFormationStackArtifact[], { concurrency, deployStack }: Options): Promise => { - const queue = new PQueue({ concurrency }); - const deploymentStates = stacks.reduce((acc, stack) => ({ ...acc, [stack.id]: 'pending' as const }), {} as Record); +export const deployStacks = async (artifacts: cxapi.CloudArtifact[], { concurrency, deployStack }: Options): Promise => { + // const queue = new PQueue({ concurrency }); + // const deploymentStates = stacks.reduce((acc, stack) => ({ ...acc, [stack.id]: 'pending' as const }), {} as Record); + // eslint-disable-next-line no-console + const graph = WorkGraph.fromCloudArtifacts(artifacts); + // eslint-disable-next-line no-console + console.log(graph.toString()); + // eslint-disable-next-line no-console + console.log(concurrency, deployStack.name); - const isStackUnblocked = (stack: cxapi.CloudFormationStackArtifact) => - stack.dependencies - .map(({ id }) => id) - .filter((id) => !id.endsWith('.assets')) - .every((id) => !deploymentStates[id] || deploymentStates[id] === 'completed'); // Dependency not selected or already finished + // const isStackUnblocked = (stack: cxapi.CloudFormationStackArtifact) => + // stack.dependencies + // .map(({ id }) => id) + // .filter((id) => !id.endsWith('.assets')) + // .every((id) => !deploymentStates[id] || deploymentStates[id] === 'completed'); // Dependency not selected or already finished - const hasAnyStackFailed = (states: Record) => Object.values(states).includes('failed'); + // const hasAnyStackFailed = (states: Record) => Object.values(states).includes('failed'); - const deploymentErrors: Error[] = []; + // const deploymentErrors: Error[] = []; - const enqueueStackDeploys = () => { - stacks.forEach(async (stack) => { - if (deploymentStates[stack.id] === 'pending' && isStackUnblocked(stack)) { - deploymentStates[stack.id] = 'queued'; + // const enqueueStackDeploys = () => { + // stacks.forEach(async (stack) => { + // if (deploymentStates[stack.id] === 'pending' && isStackUnblocked(stack)) { + // deploymentStates[stack.id] = 'queued'; - await queue.add(async () => { - // Do not start new deployments if any has already failed - if (hasAnyStackFailed(deploymentStates)) { - deploymentStates[stack.id] = 'skipped'; - return; - } + // await queue.add(async () => { + // // Do not start new deployments if any has already failed + // if (hasAnyStackFailed(deploymentStates)) { + // deploymentStates[stack.id] = 'skipped'; + // return; + // } - deploymentStates[stack.id] = 'deploying'; + // deploymentStates[stack.id] = 'deploying'; - await deployStack(stack).catch((err) => { - // By recording the failure immediately as the queued task exits, we prevent the next - // queued task from starting (its 'hasAnyStackFailed' will return 'true'). - deploymentStates[stack.id] = 'failed'; - throw err; - }); + // await deployStack(stack).catch((err) => { + // // By recording the failure immediately as the queued task exits, we prevent the next + // // queued task from starting (its 'hasAnyStackFailed' will return 'true'). + // deploymentStates[stack.id] = 'failed'; + // throw err; + // }); - deploymentStates[stack.id] = 'completed'; - enqueueStackDeploys(); - }).catch((err) => { - deploymentStates[stack.id] = 'failed'; - deploymentErrors.push(err); - }); - } - }); - }; + // deploymentStates[stack.id] = 'completed'; + // enqueueStackDeploys(); + // }).catch((err) => { + // deploymentStates[stack.id] = 'failed'; + // deploymentErrors.push(err); + // }); + // } + // }); + // }; - enqueueStackDeploys(); + // enqueueStackDeploys(); - await queue.onIdle(); + // await queue.onIdle(); - if (deploymentErrors.length) { - throw Error(`Stack Deployments Failed: ${deploymentErrors}`); - } + // if (deploymentErrors.length) { + // throw Error(`Stack Deployments Failed: ${deploymentErrors}`); + // } - // We shouldn't be able to get here, but check it anyway - const neverUnblocked = Object.entries(deploymentStates).filter(([_, s]) => s === 'pending').map(([n, _]) => n); - if (neverUnblocked.length > 0) { - throw new Error(`The following stacks never became unblocked: ${neverUnblocked.join(', ')}. Please report this at https://github.com/aws/aws-cdk/issues`); - } + // // We shouldn't be able to get here, but check it anyway + // const neverUnblocked = Object.entries(deploymentStates).filter(([_, s]) => s === 'pending').map(([n, _]) => n); + // if (neverUnblocked.length > 0) { + // throw new Error(`The following stacks never became unblocked: ${neverUnblocked.join(', ')}. Please report this at https://github.com/aws/aws-cdk/issues`); + // } }; diff --git a/packages/aws-cdk/lib/util/work-graph.ts b/packages/aws-cdk/lib/util/work-graph.ts new file mode 100644 index 0000000000000..0565d9e140507 --- /dev/null +++ b/packages/aws-cdk/lib/util/work-graph.ts @@ -0,0 +1,75 @@ +import * as cxapi from '@aws-cdk/cx-api'; + +enum WorkType { + STACK_DEPLOY = 'stack-deploy', + ASSET_BUILD = 'asset-build', + ASSET_PUBLISH = 'asset-publish', +}; + +interface WorkNode { + readonly id: string; + readonly type: WorkType; + readonly dependencies: string[]; +} + +export class WorkGraph { + public static fromCloudArtifacts(artifacts: cxapi.CloudArtifact[]) { + const graph = new WorkGraph(); + // eslint-disable-next-line no-console + console.log('artifacts', artifacts); + for (const artifact of artifacts) { + if (cxapi.AssetManifestArtifact.isAssetManifestArtifact(artifact)) { + const buildNode = { + id: `${artifact.id}-build`, + type: WorkType.ASSET_BUILD, + dependencies: getDepIds(artifact.dependencies), + }; + graph.addNode(buildNode); + graph.addNode({ + id: `${artifact.id}-publish`, + type: WorkType.ASSET_PUBLISH, + dependencies: [buildNode.id], + }); + } else { + graph.addNode({ + id: artifact.id, + type: WorkType.STACK_DEPLOY, + dependencies: getDepIds(artifact.dependencies), + }); + } + } + + return graph; + + function getDepIds(deps: cxapi.CloudArtifact[]): string[] { + const ids = []; + for (const artifact of deps) { + if (cxapi.AssetManifestArtifact.isAssetManifestArtifact(artifact)) { + // Depend on only the publish step. The publish step will depend on the build step on its own. + ids.push(`${artifact.id}-publish`); + } else { + ids.push(artifact.id); + } + } + return ids; + } + } + + private readonly nodes: Record; + + public constructor(nodes: Record = {}) { + this.nodes = nodes; + } + + public addNode(node: WorkNode) { + this.nodes[node.id] = node; + } + + public toString() { + const n = []; + for (const [id, node] of Object.entries(this.nodes)) { + n.push([id, node.type, node.dependencies]); + } + return n; + } +} diff --git a/packages/aws-cdk/test/cloud-assembly-trees/manifest.json b/packages/aws-cdk/test/cloud-assembly-trees/manifest.json new file mode 100644 index 0000000000000..3f3eca6fe11c8 --- /dev/null +++ b/packages/aws-cdk/test/cloud-assembly-trees/manifest.json @@ -0,0 +1,4 @@ +{ + "version": "31.0.0", + "templateFile": "" +} \ No newline at end of file diff --git a/packages/aws-cdk/test/deploy.test.ts b/packages/aws-cdk/test/deploy.test.ts index a161867b8db48..d395b41c6bba9 100644 --- a/packages/aws-cdk/test/deploy.test.ts +++ b/packages/aws-cdk/test/deploy.test.ts @@ -1,6 +1,9 @@ /* eslint-disable import/order */ import * as cxapi from '@aws-cdk/cx-api'; +import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import { deployStacks } from '../lib/deploy'; +import * as path from 'path'; +import { App } from 'aws-cdk-lib'; type Stack = cxapi.CloudFormationStackArtifact; @@ -34,6 +37,22 @@ describe('DeployStacks', () => { deployedStacks.splice(0); }); + test('kaizen test', async () => { + const app = new App(); + const assembly: cxapi.CloudAssembly = app.synth(); + const stack = new cxapi.CloudFormationStackArtifact(assembly, 'stack', { + type: cxschema.ArtifactType.AWS_CLOUDFORMATION_STACK, + dependencies: [], + }); + const asset = new cxapi.AssetManifestArtifact(assembly, 'asset', { + type: cxschema.ArtifactType.ASSET_MANIFEST, + dependencies: [stack.id], + }); + await expect(deployStacks([stack, asset], { concurrency: 1, deployStack })).resolves.toBeUndefined(); + + expect(deployedStacks).toStrictEqual(''); + }); + // Success test.each([ // Concurrency 1 @@ -197,4 +216,4 @@ describe('DeployStacks', () => { expect(deployedStacks).toStrictEqual(expectedStacks); }); -}); \ No newline at end of file +}); From ffdfa8ffb32f972b09472b783a3f44acbd192359 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 19 Apr 2023 18:05:28 -0400 Subject: [PATCH 076/120] bigtime changes to the cli that are wip --- packages/aws-cdk-lib/core/lib/assets.ts | 2 +- .../lib/api/cloudformation-deployments.ts | 72 +++-------- packages/aws-cdk/lib/cdk-toolkit.ts | 58 ++++----- packages/aws-cdk/lib/deploy.ts | 74 ++++++++++-- packages/aws-cdk/lib/util/work-graph.ts | 114 ++++++++++++++++-- packages/aws-cdk/package.json | 1 - .../api/cloudformation-deployments.test.ts | 52 ++++---- packages/aws-cdk/test/deploy.test.ts | 32 ++--- 8 files changed, 251 insertions(+), 154 deletions(-) diff --git a/packages/aws-cdk-lib/core/lib/assets.ts b/packages/aws-cdk-lib/core/lib/assets.ts index ece80bcc33377..6d1f81aceeecd 100644 --- a/packages/aws-cdk-lib/core/lib/assets.ts +++ b/packages/aws-cdk-lib/core/lib/assets.ts @@ -264,7 +264,7 @@ export interface DockerImageAssetSource { */ readonly assetName?: string; - /* + /** * Cache from options to pass to the `docker build` command. * * @default - no cache from args are passed diff --git a/packages/aws-cdk/lib/api/cloudformation-deployments.ts b/packages/aws-cdk/lib/api/cloudformation-deployments.ts index 84bd0b0bf310c..baa4827adb75c 100644 --- a/packages/aws-cdk/lib/api/cloudformation-deployments.ts +++ b/packages/aws-cdk/lib/api/cloudformation-deployments.ts @@ -263,7 +263,7 @@ export interface DeployStackOptions { readonly assetParallelism?: boolean; } -export interface BuildStackAssetsOptions { +interface AssetOptions { /** * Stack with assets to build. */ @@ -282,21 +282,16 @@ export interface BuildStackAssetsOptions { * @default - Current role */ readonly roleArn?: string; +} +export interface BuildStackAssetsOptions extends AssetOptions { /** * Options to pass on to `buildAsests()` function */ readonly buildOptions?: BuildAssetsOptions; } -interface PublishStackAssetsOptions { - /** - * Whether to build assets before publishing. - * - * @default true To remain backward compatible. - */ - readonly buildAssets?: boolean; - +interface PublishStackAssetsOptions extends AssetOptions { /** * Options to pass on to `publishAsests()` function */ @@ -415,16 +410,6 @@ export class CloudFormationDeployments { const toolkitInfo = await ToolkitInfo.lookup(resolvedEnvironment, stackSdk, options.toolkitStackName); - // Publish any assets before doing the actual deploy (do not publish any assets on import operation) - if (options.resourcesToImport === undefined) { - await this.publishStackAssets(options.stack, toolkitInfo, { - buildAssets: options.buildAssets ?? true, - publishOptions: { - parallel: options.assetParallelism, - }, - }); - } - // Do a verification of the bootstrap stack version await this.validateBootstrapStackVersion( options.stack.stackName, @@ -533,48 +518,31 @@ export class CloudFormationDeployments { }; } - /** - * Build a stack's assets. - */ - public async buildStackAssets(options: BuildStackAssetsOptions) { + private async prepareAndValidateAssets(asset: cxapi.AssetManifestArtifact, options: AssetOptions) { const { stackSdk, resolvedEnvironment } = await this.prepareSdkFor(options.stack, options.roleArn); const toolkitInfo = await ToolkitInfo.lookup(resolvedEnvironment, stackSdk, options.toolkitStackName); const stackEnv = await this.sdkProvider.resolveEnvironment(options.stack.environment); - const assetArtifacts = options.stack.dependencies.filter(cxapi.AssetManifestArtifact.isAssetManifestArtifact); - for (const assetArtifact of assetArtifacts) { - await this.validateBootstrapStackVersion( - options.stack.stackName, - assetArtifact.requiresBootstrapStackVersion, - assetArtifact.bootstrapStackVersionSsmParameter, - toolkitInfo); + await this.validateBootstrapStackVersion( + options.stack.stackName, + asset.requiresBootstrapStackVersion, + asset.bootstrapStackVersionSsmParameter, + toolkitInfo); - const manifest = AssetManifest.fromFile(assetArtifact.file); - await buildAssets(manifest, this.sdkProvider, stackEnv, options.buildOptions); - } - } + const manifest = AssetManifest.fromFile(asset.file); - /** - * Publish all asset manifests that are referenced by the given stack - */ - private async publishStackAssets(stack: cxapi.CloudFormationStackArtifact, toolkitInfo: ToolkitInfo, options: PublishStackAssetsOptions = {}) { - const stackEnv = await this.sdkProvider.resolveEnvironment(stack.environment); - const assetArtifacts = stack.dependencies.filter(cxapi.AssetManifestArtifact.isAssetManifestArtifact); + return { manifest, stackEnv }; + } - for (const assetArtifact of assetArtifacts) { - await this.validateBootstrapStackVersion( - stack.stackName, - assetArtifact.requiresBootstrapStackVersion, - assetArtifact.bootstrapStackVersionSsmParameter, - toolkitInfo); + public async buildAssets(asset: cxapi.AssetManifestArtifact, options: BuildStackAssetsOptions) { + const { manifest, stackEnv } = await this.prepareAndValidateAssets(asset, options); + await buildAssets(manifest, this.sdkProvider, stackEnv, options.buildOptions); + } - const manifest = AssetManifest.fromFile(assetArtifact.file); - await publishAssets(manifest, this.sdkProvider, stackEnv, { - ...options.publishOptions, - buildAssets: options.buildAssets ?? true, - }); - } + public async publishAssets(asset: cxapi.AssetManifestArtifact, options: PublishStackAssetsOptions) { + const { manifest, stackEnv } = await this.prepareAndValidateAssets(asset, options); + await publishAssets(manifest, this.sdkProvider, stackEnv, options.publishOptions); } /** diff --git a/packages/aws-cdk/lib/cdk-toolkit.ts b/packages/aws-cdk/lib/cdk-toolkit.ts index a4fa10cbd5006..b78ad2aa05937 100644 --- a/packages/aws-cdk/lib/cdk-toolkit.ts +++ b/packages/aws-cdk/lib/cdk-toolkit.ts @@ -15,8 +15,7 @@ import { HotswapMode } from './api/hotswap/common'; import { findCloudWatchLogGroups } from './api/logs/find-cloudwatch-logs'; import { CloudWatchLogEventMonitor } from './api/logs/logs-monitor'; import { StackActivityProgress } from './api/util/cloudformation/stack-activity-monitor'; -import { buildAllStackAssets } from './build'; -import { deployStacks } from './deploy'; +import { deployArtifacts } from './deploy'; import { printSecurityDiff, printStackDiff, RequireApproval } from './diff'; import { ResourceImporter } from './import'; import { data, debug, error, highlight, print, success, warning } from './logging'; @@ -24,6 +23,7 @@ import { deserializeStructure, serializeStructure } from './serialize'; import { Configuration, PROJECT_CONFIG } from './settings'; import { numberFromBool, partition } from './util'; import { validateSnsTopicArn } from './util/validate-notification-arn'; +import { WorkNode } from './util/work-graph'; import { environmentsFromDescriptors, globEnvironmentsFromStacks, looksLikeGlob } from '../lib/api/cxapp/environments'; export interface CdkToolkitProps { @@ -192,17 +192,29 @@ export class CdkToolkit { const stackOutputs: { [key: string]: any } = { }; const outputsFile = options.outputsFile; - if (assetBuildTime === AssetBuildTime.ALL_BEFORE_DEPLOY) { - // Prebuild all assets - try { - await buildAllStackAssets(stackCollection.stackArtifacts, { - buildStackAssets: (a) => this.buildAllAssetsForSingleStack(a, options), - }); - } catch (e) { - error('\n ❌ Building assets failed: %s', e); - throw e; - } - } + const buildAsset = async (assetNode: WorkNode) => { + print('%s: building assets...\n', chalk.bold(assetNode.stack.displayName)); + await this.props.cloudFormation.buildAssets(assetNode.artifact as cxapi.AssetManifestArtifact, { + stack: assetNode.stack, + roleArn: options.roleArn, + toolkitStackName: options.toolkitStackName, + buildOptions: { + parallel: options.assetParallelism, + }, + }); + print('\n%s: assets built\n', chalk.bold(assetNode.stack.displayName)); + }; + + const publishAsset = async (assetNode: WorkNode) => { + print('%s: publishing assets...\n', chalk.bold(assetNode.stack.displayName)); + await this.props.cloudFormation.publishAssets(assetNode.artifact as cxapi.AssetManifestArtifact, { + stack: assetNode.stack, + roleArn: options.roleArn, + toolkitStackName: options.toolkitStackName, + // publish assets options + }); + print('\n%s: assets published\n', chalk.bold(assetNode.stack.displayName)); + }; const deployStack = async (stack: cxapi.CloudFormationStackArtifact) => { if (stackCollection.stackCount !== 1) { highlight(stack.displayName); } @@ -337,7 +349,7 @@ export class CdkToolkit { } try { - await deployStacks(cloudArtifacts, { concurrency, deployStack }); + await deployArtifacts(cloudArtifacts, { concurrency, deployStack, buildAsset, publishAsset }); } catch (e) { error('\n ❌ Deployment failed: %s', e); throw e; @@ -783,24 +795,6 @@ export class CdkToolkit { // just continue - deploy will show the error } } - - private async buildAllAssetsForSingleStack(stack: cxapi.CloudFormationStackArtifact, options: Pick): Promise { - // Check whether the stack has an asset manifest before trying to build and publish. - if (!stack.dependencies.some(cxapi.AssetManifestArtifact.isAssetManifestArtifact)) { - return; - } - - print('%s: building assets...\n', chalk.bold(stack.displayName)); - await this.props.cloudFormation.buildStackAssets({ - stack, - roleArn: options.roleArn, - toolkitStackName: options.toolkitStackName, - buildOptions: { - parallel: options.assetParallelism, - }, - }); - print('\n%s: assets built\n', chalk.bold(stack.displayName)); - } } export interface DiffOptions { diff --git a/packages/aws-cdk/lib/deploy.ts b/packages/aws-cdk/lib/deploy.ts index 1bb89637fd06b..f6c60fe197d1b 100644 --- a/packages/aws-cdk/lib/deploy.ts +++ b/packages/aws-cdk/lib/deploy.ts @@ -1,23 +1,73 @@ import * as cxapi from '@aws-cdk/cx-api'; -// import PQueue from 'p-queue'; -import { WorkGraph } from './util/work-graph'; +import { WorkGraph, WorkNode, WorkType } from './util/work-graph'; type Options = { concurrency: number; deployStack: (stack: cxapi.CloudFormationStackArtifact) => Promise; + buildAsset: (assetNode: WorkNode) => Promise; + publishAsset: (assetNode: WorkNode) => Promise; }; -// type DeploymentState = 'pending' | 'queued' | 'deploying' | 'completed' | 'failed' | 'skipped'; - -export const deployStacks = async (artifacts: cxapi.CloudArtifact[], { concurrency, deployStack }: Options): Promise => { - // const queue = new PQueue({ concurrency }); - // const deploymentStates = stacks.reduce((acc, stack) => ({ ...acc, [stack.id]: 'pending' as const }), {} as Record); - // eslint-disable-next-line no-console +export const deployArtifacts = async (artifacts: cxapi.CloudArtifact[], { + concurrency, + deployStack, + buildAsset, + publishAsset, +}: Options): Promise => { const graph = WorkGraph.fromCloudArtifacts(artifacts); - // eslint-disable-next-line no-console - console.log(graph.toString()); - // eslint-disable-next-line no-console - console.log(concurrency, deployStack.name); + + await forAllArtifacts(concurrency, async (x: WorkNode) => { + // Execute this function with as much parallelism as possible + switch (x.type) { + case WorkType.STACK_DEPLOY: + await deployStack(x.artifact as cxapi.CloudFormationStackArtifact).catch((err) => { + // By recording the failure immediately as the queued task exits, we prevent the next + // queued task from starting (its 'hasAnyStackFailed' will return 'true'). + graph.failed(x); + throw err; + }); + break; + case WorkType.ASSET_BUILD: + await buildAsset(x).catch((err) => { + graph.failed(x); + throw err; + }); + break; + case WorkType.ASSET_PUBLISH: + await publishAsset(x).catch((err) => { + graph.failed(x); + throw err; + }); + break; + } + }); + + function forAllArtifacts(n: number, fn: (x: WorkNode) => Promise): Promise { + return new Promise((ok) => { + let active = 0; + + start(); + + function start() { + while (graph.peek() && active < n) { + startOne(graph.next()!); + } + + if (!graph.peek() && active === 0) { + ok(); + } + } + + function startOne(x: WorkNode) { + active++; + void fn(x).then(() => { + active--; + graph.deployed(x); + start(); + }); + } + }); + } // const isStackUnblocked = (stack: cxapi.CloudFormationStackArtifact) => // stack.dependencies diff --git a/packages/aws-cdk/lib/util/work-graph.ts b/packages/aws-cdk/lib/util/work-graph.ts index 0565d9e140507..8116d975bc312 100644 --- a/packages/aws-cdk/lib/util/work-graph.ts +++ b/packages/aws-cdk/lib/util/work-graph.ts @@ -1,46 +1,92 @@ +/* eslint-disable no-console */ import * as cxapi from '@aws-cdk/cx-api'; -enum WorkType { +export enum WorkType { STACK_DEPLOY = 'stack-deploy', ASSET_BUILD = 'asset-build', ASSET_PUBLISH = 'asset-publish', }; -interface WorkNode { +export enum DeploymentState { + PENDING = 'pending', + QUEUED = 'queued', + DEPLOYING = 'deploying', + COMPLETED = 'completed', + FAILED = 'failed', + SKIPPED = 'skipped', +}; + +export interface WorkNode { readonly id: string; readonly type: WorkType; readonly dependencies: string[]; + readonly artifact: cxapi.CloudArtifact; + readonly stack: cxapi.CloudFormationStackArtifact; + deploymentState: DeploymentState; } export class WorkGraph { public static fromCloudArtifacts(artifacts: cxapi.CloudArtifact[]) { const graph = new WorkGraph(); - // eslint-disable-next-line no-console - console.log('artifacts', artifacts); + + // Associated stack will be lazily added for Assets + const graphNodes: Omit[] = []; + const associatedStacks: Record = {}; + for (const artifact of artifacts) { if (cxapi.AssetManifestArtifact.isAssetManifestArtifact(artifact)) { const buildNode = { id: `${artifact.id}-build`, type: WorkType.ASSET_BUILD, dependencies: getDepIds(artifact.dependencies), + artifact, + deploymentState: 'pending' as DeploymentState, }; - graph.addNode(buildNode); - graph.addNode({ + graphNodes.push(buildNode); + graphNodes.push({ id: `${artifact.id}-publish`, type: WorkType.ASSET_PUBLISH, dependencies: [buildNode.id], + artifact, + deploymentState: DeploymentState.PENDING, }); - } else { - graph.addNode({ + } else if (artifact instanceof cxapi.CloudFormationStackArtifact) { // TODO: not sure if we can instanceof here + graphNodes.push({ id: artifact.id, type: WorkType.STACK_DEPLOY, dependencies: getDepIds(artifact.dependencies), + artifact, + deploymentState: DeploymentState.PENDING, }); + updateAssociatedStacks(artifact); } } + // post-process stacks associated with each nodes because we only know + // we have this information after all artifacts have been processed. + const finalGraphNodes: WorkNode[] = []; + for (const node of graphNodes) { + const stack = associatedStacks[node.id]; + if (!stack) { + throw new Error(`No stack associated with ${node.id} artifact. Something in the source code or asset manifest is wrong.`); + } + finalGraphNodes.push({ + ...node, + stack, + }); + } + graph.addNodes(...finalGraphNodes); return graph; + function updateAssociatedStacks(stack: cxapi.CloudFormationStackArtifact) { + const assetArtifacts = stack.dependencies.filter(cxapi.AssetManifestArtifact.isAssetManifestArtifact); + for (const art of assetArtifacts) { + associatedStacks[`${art.id}-publish`] = stack; + associatedStacks[`${art.id}-build`] = stack; + } + associatedStacks[stack.id] = stack; + } + function getDepIds(deps: cxapi.CloudArtifact[]): string[] { const ids = []; for (const artifact of deps) { @@ -56,13 +102,41 @@ export class WorkGraph { } private readonly nodes: Record; + private readonly readyPool: Array = []; - public constructor(nodes: Record = {}) { + private constructor(nodes: Record = {}) { this.nodes = nodes; } - public addNode(node: WorkNode) { - this.nodes[node.id] = node; + public addNodes(...nodes: WorkNode[]) { + for (const node of nodes) { + this.nodes[node.id] = node; + } + } + + public peek(): boolean { + return this.readyPool.length > 0; + } + + public next(): WorkNode | undefined { + this.updateReadyPool(); + if (this.readyPool.length > 0) { + const node = this.readyPool.shift()!; + // we experienced a failed deployment elsewhere + if (node.deploymentState !== DeploymentState.QUEUED) { return undefined; } + node.deploymentState = DeploymentState.DEPLOYING; + return node; + } + return undefined; + } + + public deployed(node: WorkNode) { + node.deploymentState = DeploymentState.COMPLETED; + } + + public failed(node: WorkNode) { + node.deploymentState = DeploymentState.FAILED; + this.skipRest(); } public toString() { @@ -72,4 +146,22 @@ export class WorkGraph { } return n; } + + private updateReadyPool() { + for (const node of Object.values(this.nodes)) { + if (node.deploymentState === DeploymentState.PENDING && + (node.dependencies.length === 0 || node.dependencies.every((id) => this.nodes[id].deploymentState === 'completed'))) { + node.deploymentState = DeploymentState.QUEUED; + this.readyPool.push(node); + } + } + } + + private skipRest() { + for (const node of Object.values(this.nodes)) { + if ([DeploymentState.QUEUED, DeploymentState.PENDING].includes(node.deploymentState)) { + node.deploymentState = DeploymentState.SKIPPED; + } + } + } } diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index f03bcd6f6bd4b..b98f44cb67e1d 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -110,7 +110,6 @@ "glob": "^7.2.3", "json-diff": "^0.10.0", "minimatch": ">=3.1", - "p-queue": "^6.6.2", "promptly": "^3.2.0", "proxy-agent": "^5.0.0", "semver": "^7.3.8", diff --git a/packages/aws-cdk/test/api/cloudformation-deployments.test.ts b/packages/aws-cdk/test/api/cloudformation-deployments.test.ts index baae814a09aef..dc7d7693da37e 100644 --- a/packages/aws-cdk/test/api/cloudformation-deployments.test.ts +++ b/packages/aws-cdk/test/api/cloudformation-deployments.test.ts @@ -15,7 +15,7 @@ import { deployStack } from '../../lib/api/deploy-stack'; import { HotswapMode } from '../../lib/api/hotswap/common'; import { EcrRepositoryInfo, ToolkitInfo } from '../../lib/api/toolkit-info'; import { CloudFormationStack } from '../../lib/api/util/cloudformation'; -import { buildAssets, publishAssets } from '../../lib/util/asset-publishing'; +import { /*buildAssets,*/ publishAssets } from '../../lib/util/asset-publishing'; import { testStack } from '../util'; import { mockBootstrapStack, MockSdkProvider } from '../util/mock-sdk'; @@ -884,31 +884,31 @@ test('readCurrentTemplateWithNestedStacks() succesfully ignores stacks without m }); }); -test('building assets', async () => { - // GIVEN - const stack = testStackWithAssetManifest(); - - // WHEN - await deployments.buildStackAssets({ - stack, - }); - - // THEN - const expectedAssetManifest = expect.objectContaining({ - directory: stack.assembly.directory, - manifest: expect.objectContaining({ - files: expect.objectContaining({ - fake: expect.anything(), - }), - }), - }); - const expectedEnvironment = expect.objectContaining({ - account: 'account', - name: 'aws://account/region', - region: 'region', - }); - expect(buildAssets).toBeCalledWith(expectedAssetManifest, sdkProvider, expectedEnvironment, undefined); -}); +// test('building assets', async () => { +// // GIVEN +// const stack = testStackWithAssetManifest(); + +// // WHEN +// await deployments.buildStackAssets({ +// stack, +// }); + +// // THEN +// const expectedAssetManifest = expect.objectContaining({ +// directory: stack.assembly.directory, +// manifest: expect.objectContaining({ +// files: expect.objectContaining({ +// fake: expect.anything(), +// }), +// }), +// }); +// const expectedEnvironment = expect.objectContaining({ +// account: 'account', +// name: 'aws://account/region', +// region: 'region', +// }); +// expect(buildAssets).toBeCalledWith(expectedAssetManifest, sdkProvider, expectedEnvironment, undefined); +// }); function pushStackResourceSummaries(stackName: string, ...items: CloudFormation.StackResourceSummary[]) { if (!currentCfnStackResources[stackName]) { diff --git a/packages/aws-cdk/test/deploy.test.ts b/packages/aws-cdk/test/deploy.test.ts index d395b41c6bba9..73bd7d552c719 100644 --- a/packages/aws-cdk/test/deploy.test.ts +++ b/packages/aws-cdk/test/deploy.test.ts @@ -1,11 +1,10 @@ /* eslint-disable import/order */ import * as cxapi from '@aws-cdk/cx-api'; -import * as cxschema from '@aws-cdk/cloud-assembly-schema'; -import { deployStacks } from '../lib/deploy'; -import * as path from 'path'; -import { App } from 'aws-cdk-lib'; +import { deployArtifacts } from '../lib/deploy'; +type Artifact = cxapi.CloudArtifact; type Stack = cxapi.CloudFormationStackArtifact; +type Asset = cxapi.AssetManifestArtifact; const sleep = async (duration: number) => new Promise((resolve) => setTimeout(() => resolve(), duration)); @@ -38,19 +37,14 @@ describe('DeployStacks', () => { }); test('kaizen test', async () => { - const app = new App(); - const assembly: cxapi.CloudAssembly = app.synth(); - const stack = new cxapi.CloudFormationStackArtifact(assembly, 'stack', { - type: cxschema.ArtifactType.AWS_CLOUDFORMATION_STACK, - dependencies: [], - }); - const asset = new cxapi.AssetManifestArtifact(assembly, 'asset', { - type: cxschema.ArtifactType.ASSET_MANIFEST, - dependencies: [stack.id], - }); - await expect(deployStacks([stack, asset], { concurrency: 1, deployStack })).resolves.toBeUndefined(); - - expect(deployedStacks).toStrictEqual(''); + const ASSET_MANIFEST_ARTIFACT_SYM = Symbol.for('@aws-cdk/cx-api.AssetManifestArtifact'); + const asset = { id: 'AssetA', dependencies: [], [ASSET_MANIFEST_ARTIFACT_SYM]: true } as unknown as Asset; + const assetB = { id: 'AssetB', dependencies: [], [ASSET_MANIFEST_ARTIFACT_SYM]: true } as unknown as Asset; + const stack = { id: 'StackA', dependencies: [asset] } as unknown as Stack; + const stackB = { id: 'StackB', dependencies: [assetB, stack] } as unknown as Stack; + await expect(deployArtifacts([stack, asset, stackB, assetB] as Artifact[], { concurrency: 1, deployStack })).resolves.toBeUndefined(); + + expect(deployedStacks).toStrictEqual('a'); }); // Success @@ -143,7 +137,7 @@ describe('DeployStacks', () => { expected: ['B'], }, ])('Success - Concurrency: $concurrency - $scenario', async ({ concurrency, expected, toDeploy }) => { - await expect(deployStacks(toDeploy as unknown as Stack[], { concurrency, deployStack })).resolves.toBeUndefined(); + await expect(deployArtifacts(toDeploy as unknown as Stack[], { concurrency, deployStack })).resolves.toBeUndefined(); expect(deployedStacks).toStrictEqual(expected); }); @@ -212,7 +206,7 @@ describe('DeployStacks', () => { expectedStacks: ['A', 'B'], }, ])('Failure - Concurrency: $concurrency - $scenario', async ({ concurrency, expectedError, toDeploy, expectedStacks }) => { - await expect(deployStacks(toDeploy as unknown as Stack[], { concurrency, deployStack })).rejects.toThrowError(expectedError); + await expect(deployArtifacts(toDeploy as unknown as Stack[], { concurrency, deployStack })).rejects.toThrowError(expectedError); expect(deployedStacks).toStrictEqual(expectedStacks); }); From a0570d2cb218de8499f0252f1bb6ae01a50a9321 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 20 Apr 2023 14:33:25 -0400 Subject: [PATCH 077/120] get to no compilation errors --- packages/aws-cdk/lib/cdk-toolkit.ts | 4 +++- .../test/api/cloudformation-deployments.test.ts | 1 + packages/aws-cdk/test/cdk-toolkit.test.ts | 4 ++-- packages/aws-cdk/test/deploy.test.ts | 17 ++++++++++++++--- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/packages/aws-cdk/lib/cdk-toolkit.ts b/packages/aws-cdk/lib/cdk-toolkit.ts index b78ad2aa05937..0a4041fd9056b 100644 --- a/packages/aws-cdk/lib/cdk-toolkit.ts +++ b/packages/aws-cdk/lib/cdk-toolkit.ts @@ -211,7 +211,9 @@ export class CdkToolkit { stack: assetNode.stack, roleArn: options.roleArn, toolkitStackName: options.toolkitStackName, - // publish assets options + publishOptions: { + parallel: options.assetParallelism, + }, }); print('\n%s: assets published\n', chalk.bold(assetNode.stack.displayName)); }; diff --git a/packages/aws-cdk/test/api/cloudformation-deployments.test.ts b/packages/aws-cdk/test/api/cloudformation-deployments.test.ts index dc7d7693da37e..a86208fa587a2 100644 --- a/packages/aws-cdk/test/api/cloudformation-deployments.test.ts +++ b/packages/aws-cdk/test/api/cloudformation-deployments.test.ts @@ -884,6 +884,7 @@ test('readCurrentTemplateWithNestedStacks() succesfully ignores stacks without m }); }); +// eslint-disable-next-line jest/no-commented-out-tests // test('building assets', async () => { // // GIVEN // const stack = testStackWithAssetManifest(); diff --git a/packages/aws-cdk/test/cdk-toolkit.test.ts b/packages/aws-cdk/test/cdk-toolkit.test.ts index aa5d65c75b964..5b7999f00c9ea 100644 --- a/packages/aws-cdk/test/cdk-toolkit.test.ts +++ b/packages/aws-cdk/test/cdk-toolkit.test.ts @@ -601,7 +601,7 @@ describe('deploy', () => { // WHEN // Not the best test but following this through to the asset publishing library fails - await withMocked(fakeCloudFormation, 'buildStackAssets', async (mockBuildStackAssets) => { + await withMocked(fakeCloudFormation, 'buildAssets', async (mockBuildStackAssets) => { await toolkit.deploy({ selector: { patterns: ['Test-Stack-Asset'] }, assetParallelism: false, @@ -632,7 +632,7 @@ describe('deploy', () => { // WHEN // Not the best test but following this through to the asset publishing library fails - await withMocked(fakeCloudFormation, 'buildStackAssets', async (mockBuildStackAssets) => { + await withMocked(fakeCloudFormation, 'buildAssets', async (mockBuildStackAssets) => { await toolkit.deploy({ selector: { patterns: ['Test-Stack-Asset'] }, assetBuildTime: AssetBuildTime.JUST_IN_TIME, diff --git a/packages/aws-cdk/test/deploy.test.ts b/packages/aws-cdk/test/deploy.test.ts index 73bd7d552c719..9f89eb03b2f02 100644 --- a/packages/aws-cdk/test/deploy.test.ts +++ b/packages/aws-cdk/test/deploy.test.ts @@ -1,6 +1,7 @@ /* eslint-disable import/order */ import * as cxapi from '@aws-cdk/cx-api'; import { deployArtifacts } from '../lib/deploy'; +import { WorkNode } from '../lib/util/work-graph'; type Artifact = cxapi.CloudArtifact; type Stack = cxapi.CloudFormationStackArtifact; @@ -31,6 +32,14 @@ describe('DeployStacks', () => { deployedStacks.push(id); }; + const builtAssets: string[] = []; + const buildAsset = async({ id }: WorkNode) => { + builtAssets.push(id); + }; + const publishedAssets: string[] = []; + const publishAsset = async({ id }: WorkNode) => { + publishedAssets.push(id); + }; beforeEach(() => { deployedStacks.splice(0); @@ -42,7 +51,8 @@ describe('DeployStacks', () => { const assetB = { id: 'AssetB', dependencies: [], [ASSET_MANIFEST_ARTIFACT_SYM]: true } as unknown as Asset; const stack = { id: 'StackA', dependencies: [asset] } as unknown as Stack; const stackB = { id: 'StackB', dependencies: [assetB, stack] } as unknown as Stack; - await expect(deployArtifacts([stack, asset, stackB, assetB] as Artifact[], { concurrency: 1, deployStack })).resolves.toBeUndefined(); + // eslint-disable-next-line max-len + await expect(deployArtifacts([stack, asset, stackB, assetB] as Artifact[], { concurrency: 1, deployStack, buildAsset, publishAsset })).resolves.toBeUndefined(); expect(deployedStacks).toStrictEqual('a'); }); @@ -137,7 +147,7 @@ describe('DeployStacks', () => { expected: ['B'], }, ])('Success - Concurrency: $concurrency - $scenario', async ({ concurrency, expected, toDeploy }) => { - await expect(deployArtifacts(toDeploy as unknown as Stack[], { concurrency, deployStack })).resolves.toBeUndefined(); + await expect(deployArtifacts(toDeploy as unknown as Stack[], { concurrency, deployStack, buildAsset, publishAsset })).resolves.toBeUndefined(); expect(deployedStacks).toStrictEqual(expected); }); @@ -206,7 +216,8 @@ describe('DeployStacks', () => { expectedStacks: ['A', 'B'], }, ])('Failure - Concurrency: $concurrency - $scenario', async ({ concurrency, expectedError, toDeploy, expectedStacks }) => { - await expect(deployArtifacts(toDeploy as unknown as Stack[], { concurrency, deployStack })).rejects.toThrowError(expectedError); + // eslint-disable-next-line max-len + await expect(deployArtifacts(toDeploy as unknown as Stack[], { concurrency, deployStack, buildAsset, publishAsset })).rejects.toThrowError(expectedError); expect(deployedStacks).toStrictEqual(expectedStacks); }); From 06dd548c70388631f945c508184dfe9494f7ae6a Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Fri, 21 Apr 2023 11:57:05 -0400 Subject: [PATCH 078/120] all tests in deploy.test succeed --- .../lib/artifacts/cloudformation-artifact.ts | 35 +++++ packages/aws-cdk/lib/deploy.ts | 41 +++--- packages/aws-cdk/lib/util/work-graph.ts | 37 +++++- packages/aws-cdk/test/deploy.test.ts | 123 ++++++++++-------- 4 files changed, 165 insertions(+), 71 deletions(-) diff --git a/packages/aws-cdk-lib/cx-api/lib/artifacts/cloudformation-artifact.ts b/packages/aws-cdk-lib/cx-api/lib/artifacts/cloudformation-artifact.ts index 0ca21e10ea772..7cf279c96d924 100644 --- a/packages/aws-cdk-lib/cx-api/lib/artifacts/cloudformation-artifact.ts +++ b/packages/aws-cdk-lib/cx-api/lib/artifacts/cloudformation-artifact.ts @@ -5,7 +5,30 @@ import { CloudArtifact } from '../cloud-artifact'; import type { CloudAssembly } from '../cloud-assembly'; import { Environment, EnvironmentUtils } from '../environment'; +const CLOUDFORMATION_STACK_ARTIFACT_SYM = Symbol.for('@aws-cdk/cx-api.CloudFormationStackArtifact'); + export class CloudFormationStackArtifact extends CloudArtifact { + /** + * Checks if `art` is an instance of this class. + * + * Use this method instead of `instanceof` to properly detect `CloudFormationStackArtifact` + * instances, even when the construct library is symlinked. + * + * Explanation: in JavaScript, multiple copies of the `cx-api` library on + * disk are seen as independent, completely different libraries. As a + * consequence, the class `CloudFormationStackArtifact` in each copy of the `cx-api` library + * is seen as a different class, and an instance of one class will not test as + * `instanceof` the other class. `npm install` will not create installations + * like this, but users may manually symlink construct libraries together or + * use a monorepo tool: in those cases, multiple copies of the `cx-api` + * library can be accidentally installed, and `instanceof` will behave + * unpredictably. It is safest to avoid using `instanceof`, and using + * this type-testing method instead. + */ + public static isCloudFormationStackArtifact(art: any): art is CloudFormationStackArtifact { + return art && typeof art === 'object' && art[CLOUDFORMATION_STACK_ARTIFACT_SYM]; + } + /** * The file name of the template. */ @@ -183,3 +206,15 @@ export class CloudFormationStackArtifact extends CloudArtifact { return ret; } } + +/** + * Mark all instances of 'CloudFormationStackArtifact' + * + * Why not put this in the constructor? Because this is a class property, + * not an instance property. It applies to all instances of the class. + */ +Object.defineProperty(CloudFormationStackArtifact.prototype, CLOUDFORMATION_STACK_ARTIFACT_SYM, { + value: true, + enumerable: false, + writable: false, +}); diff --git a/packages/aws-cdk/lib/deploy.ts b/packages/aws-cdk/lib/deploy.ts index f6c60fe197d1b..448e4e05fcbd8 100644 --- a/packages/aws-cdk/lib/deploy.ts +++ b/packages/aws-cdk/lib/deploy.ts @@ -15,55 +15,62 @@ export const deployArtifacts = async (artifacts: cxapi.CloudArtifact[], { publishAsset, }: Options): Promise => { const graph = WorkGraph.fromCloudArtifacts(artifacts); + console.log(graph.toString()); await forAllArtifacts(concurrency, async (x: WorkNode) => { + console.log('fn called'); // Execute this function with as much parallelism as possible switch (x.type) { case WorkType.STACK_DEPLOY: - await deployStack(x.artifact as cxapi.CloudFormationStackArtifact).catch((err) => { - // By recording the failure immediately as the queued task exits, we prevent the next - // queued task from starting (its 'hasAnyStackFailed' will return 'true'). - graph.failed(x); - throw err; - }); + await deployStack(x.artifact as cxapi.CloudFormationStackArtifact); break; case WorkType.ASSET_BUILD: - await buildAsset(x).catch((err) => { - graph.failed(x); - throw err; - }); + await buildAsset(x); break; case WorkType.ASSET_PUBLISH: - await publishAsset(x).catch((err) => { - graph.failed(x); - throw err; - }); + await publishAsset(x); break; } }); function forAllArtifacts(n: number, fn: (x: WorkNode) => Promise): Promise { - return new Promise((ok) => { + console.log('forallartiacts'); + return new Promise((ok, fail) => { let active = 0; start(); function start() { - while (graph.peek() && active < n) { + console.log('start'); + while (graph.hasNext() && active < n) { + console.log('startingone'); startOne(graph.next()!); } - if (!graph.peek() && active === 0) { + if (graph.done() && active === 0) { ok(); } + + // wait for other active deploys to finish before failing + if (graph.hasFailed() && active === 0) { + fail(graph.error); + } } function startOne(x: WorkNode) { + console.log('startOne'); active++; void fn(x).then(() => { + console.log('fn finised'); active--; graph.deployed(x); start(); + }).catch((err) => { + active--; + // By recording the failure immediately as the queued task exits, we prevent the next + // queued task from starting. + graph.failed(x, err); + start(); }); } }); diff --git a/packages/aws-cdk/lib/util/work-graph.ts b/packages/aws-cdk/lib/util/work-graph.ts index 8116d975bc312..341343d6a652e 100644 --- a/packages/aws-cdk/lib/util/work-graph.ts +++ b/packages/aws-cdk/lib/util/work-graph.ts @@ -50,7 +50,7 @@ export class WorkGraph { artifact, deploymentState: DeploymentState.PENDING, }); - } else if (artifact instanceof cxapi.CloudFormationStackArtifact) { // TODO: not sure if we can instanceof here + } else if (cxapi.CloudFormationStackArtifact.isCloudFormationStackArtifact(artifact)) { graphNodes.push({ id: artifact.id, type: WorkType.STACK_DEPLOY, @@ -75,7 +75,26 @@ export class WorkGraph { stack, }); } + graph.addNodes(...finalGraphNodes); + + // Ensure all dependencies actually exist. This protects against scenarios such as the following: + // StackA depends on StackB, but StackB is not selected to deploy. The dependency is redundant + // and will be dropped. + for (const node of Object.values(graph.nodes)) { + if (node.type !== WorkType.STACK_DEPLOY) { continue; } + const removeDeps = []; + for (const dep of node.dependencies) { + if (graph.nodes[dep] === undefined) { + removeDeps.push(dep); + } + } + removeDeps.forEach((d) => { + const i = node.dependencies.indexOf(d); + node.dependencies.splice(i, 1); + }); + } + return graph; function updateAssociatedStacks(stack: cxapi.CloudFormationStackArtifact) { @@ -103,6 +122,7 @@ export class WorkGraph { private readonly nodes: Record; private readonly readyPool: Array = []; + public error?: Error; private constructor(nodes: Record = {}) { this.nodes = nodes; @@ -114,7 +134,16 @@ export class WorkGraph { } } - public peek(): boolean { + public done(): boolean { + return Object.values(this.nodes).every((n) => [DeploymentState.COMPLETED, DeploymentState.DEPLOYING].includes(n.deploymentState)); + } + + public hasFailed(): boolean { + return Object.values(this.nodes).some((n) => n.deploymentState === DeploymentState.FAILED); + } + + public hasNext(): boolean { + this.updateReadyPool(); return this.readyPool.length > 0; } @@ -134,7 +163,9 @@ export class WorkGraph { node.deploymentState = DeploymentState.COMPLETED; } - public failed(node: WorkNode) { + public failed(node: WorkNode, error?: Error) { + console.log('managing failure'); + this.error = error; node.deploymentState = DeploymentState.FAILED; this.skipRest(); } diff --git a/packages/aws-cdk/test/deploy.test.ts b/packages/aws-cdk/test/deploy.test.ts index 9f89eb03b2f02..7432ac6e79ea0 100644 --- a/packages/aws-cdk/test/deploy.test.ts +++ b/packages/aws-cdk/test/deploy.test.ts @@ -3,13 +3,16 @@ import * as cxapi from '@aws-cdk/cx-api'; import { deployArtifacts } from '../lib/deploy'; import { WorkNode } from '../lib/util/work-graph'; +const ASSET_MANIFEST_ARTIFACT_SYM = Symbol.for('@aws-cdk/cx-api.AssetManifestArtifact'); +const CLOUDFORMATION_STACK_ARTIFACT_SYM = Symbol.for('@aws-cdk/cx-api.CloudFormationStackArtifact'); + type Artifact = cxapi.CloudArtifact; type Stack = cxapi.CloudFormationStackArtifact; type Asset = cxapi.AssetManifestArtifact; const sleep = async (duration: number) => new Promise((resolve) => setTimeout(() => resolve(), duration)); -// Not great to have actual sleeps in the tests, but they mostly just exist to give 'p-queue' +// Not great to have actual sleeps in the tests, but they mostly just exist to give the async workflow // a chance to start new tasks. const SLOW = 200; @@ -21,6 +24,7 @@ const SLOW = 200; describe('DeployStacks', () => { const deployedStacks: string[] = []; const deployStack = async ({ id, displayName, name }: Stack) => { + console.log(id, displayName); const errorMessage = displayName; const timeout = Number(name) || 0; @@ -46,104 +50,103 @@ describe('DeployStacks', () => { }); test('kaizen test', async () => { - const ASSET_MANIFEST_ARTIFACT_SYM = Symbol.for('@aws-cdk/cx-api.AssetManifestArtifact'); - const asset = { id: 'AssetA', dependencies: [], [ASSET_MANIFEST_ARTIFACT_SYM]: true } as unknown as Asset; - const assetB = { id: 'AssetB', dependencies: [], [ASSET_MANIFEST_ARTIFACT_SYM]: true } as unknown as Asset; - const stack = { id: 'StackA', dependencies: [asset] } as unknown as Stack; - const stackB = { id: 'StackB', dependencies: [assetB, stack] } as unknown as Stack; + const asset = createAssetArtifact({ id: 'AssetA', dependencies: [] }); + const assetB = createAssetArtifact({ id: 'AssetB', dependencies: [] }); + const stack = createStackArtifact({ id: 'StackA', dependencies: [asset] }); + const stackB = createStackArtifact({ id: 'StackB', dependencies: [assetB, stack] }); // eslint-disable-next-line max-len await expect(deployArtifacts([stack, asset, stackB, assetB] as Artifact[], { concurrency: 1, deployStack, buildAsset, publishAsset })).resolves.toBeUndefined(); - expect(deployedStacks).toStrictEqual('a'); + expect(deployedStacks).toStrictEqual(['StackA', 'StackB']); }); // Success test.each([ // Concurrency 1 { scenario: 'No Stacks', concurrency: 1, toDeploy: [], expected: [] }, - { scenario: 'A', concurrency: 1, toDeploy: [{ id: 'A', dependencies: [] }], expected: ['A'] }, - { scenario: 'A, B', concurrency: 1, toDeploy: [{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [] }], expected: ['A', 'B'] }, - { scenario: 'A -> B', concurrency: 1, toDeploy: [{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [{ id: 'A' }] }], expected: ['A', 'B'] }, - { scenario: '[unsorted] A -> B', concurrency: 1, toDeploy: [{ id: 'B', dependencies: [{ id: 'A' }] }, { id: 'A', dependencies: [] }], expected: ['A', 'B'] }, - { scenario: 'A -> B -> C', concurrency: 1, toDeploy: [{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [{ id: 'A' }] }, { id: 'C', dependencies: [{ id: 'B' }] }], expected: ['A', 'B', 'C'] }, - { scenario: 'A -> B, A -> C', concurrency: 1, toDeploy: [{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [{ id: 'A' }] }, { id: 'C', dependencies: [{ id: 'A' }] }], expected: ['A', 'B', 'C'] }, + { scenario: 'A', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [] }]), expected: ['A'] }, + { scenario: 'A, B', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [] }]), expected: ['A', 'B'] }, + { scenario: 'A -> B', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [{ id: 'A' }] }]), expected: ['A', 'B'] }, + { scenario: '[unsorted] A -> B', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'B', dependencies: [{ id: 'A' }] }, { id: 'A', dependencies: [] }]), expected: ['A', 'B'] }, + { scenario: 'A -> B -> C', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [{ id: 'A' }] }, { id: 'C', dependencies: [{ id: 'B' }] }]), expected: ['A', 'B', 'C'] }, + { scenario: 'A -> B, A -> C', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [{ id: 'A' }] }, { id: 'C', dependencies: [{ id: 'A' }] }]), expected: ['A', 'B', 'C'] }, { scenario: 'A (slow), B', concurrency: 1, - toDeploy: [ + toDeploy: createStackArtifacts([ { id: 'A', dependencies: [], name: SLOW }, { id: 'B', dependencies: [] }, - ], + ]), expected: ['A', 'B'], }, { scenario: 'A -> B, C -> D', concurrency: 1, - toDeploy: [ + toDeploy: createStackArtifacts([ { id: 'A', dependencies: [] }, { id: 'B', dependencies: [{ id: 'A' }] }, { id: 'C', dependencies: [] }, { id: 'D', dependencies: [{ id: 'C' }] }, - ], + ]), expected: ['A', 'C', 'B', 'D'], }, { scenario: 'A (slow) -> B, C -> D', concurrency: 1, - toDeploy: [ + toDeploy: createStackArtifacts([ { id: 'A', dependencies: [], name: SLOW }, { id: 'B', dependencies: [{ id: 'A' }] }, { id: 'C', dependencies: [] }, { id: 'D', dependencies: [{ id: 'C' }] }, - ], + ]), expected: ['A', 'C', 'B', 'D'], }, // Concurrency 2 { scenario: 'No Stacks', concurrency: 2, toDeploy: [], expected: [] }, - { scenario: 'A', concurrency: 2, toDeploy: [{ id: 'A', dependencies: [] }], expected: ['A'] }, - { scenario: 'A, B', concurrency: 2, toDeploy: [{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [] }], expected: ['A', 'B'] }, - { scenario: 'A -> B', concurrency: 2, toDeploy: [{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [{ id: 'A' }] }], expected: ['A', 'B'] }, - { scenario: '[unsorted] A -> B', concurrency: 2, toDeploy: [{ id: 'B', dependencies: [{ id: 'A' }] }, { id: 'A', dependencies: [] }], expected: ['A', 'B'] }, - { scenario: 'A -> B -> C', concurrency: 2, toDeploy: [{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [{ id: 'A' }] }, { id: 'C', dependencies: [{ id: 'B' }] }], expected: ['A', 'B', 'C'] }, - { scenario: 'A -> B, A -> C', concurrency: 2, toDeploy: [{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [{ id: 'A' }] }, { id: 'C', dependencies: [{ id: 'A' }] }], expected: ['A', 'B', 'C'] }, + { scenario: 'A', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [] }]), expected: ['A'] }, + { scenario: 'A, B', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [] }]), expected: ['A', 'B'] }, + { scenario: 'A -> B', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [{ id: 'A' }] }]), expected: ['A', 'B'] }, + { scenario: '[unsorted] A -> B', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'B', dependencies: [{ id: 'A' }] }, { id: 'A', dependencies: [] }]), expected: ['A', 'B'] }, + { scenario: 'A -> B -> C', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [{ id: 'A' }] }, { id: 'C', dependencies: [{ id: 'B' }] }]), expected: ['A', 'B', 'C'] }, + { scenario: 'A -> B, A -> C', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [{ id: 'A' }] }, { id: 'C', dependencies: [{ id: 'A' }] }]), expected: ['A', 'B', 'C'] }, { scenario: 'A, B', concurrency: 2, - toDeploy: [ + toDeploy: createStackArtifacts([ { id: 'A', dependencies: [], name: SLOW }, { id: 'B', dependencies: [] }, - ], + ]), expected: ['B', 'A'], }, { scenario: 'A -> B, C -> D', concurrency: 2, - toDeploy: [ + toDeploy: createStackArtifacts([ { id: 'A', dependencies: [] }, { id: 'B', dependencies: [{ id: 'A' }] }, { id: 'C', dependencies: [] }, { id: 'D', dependencies: [{ id: 'C' }] }, - ], + ]), expected: ['A', 'C', 'B', 'D'], }, { scenario: 'A (slow) -> B, C -> D', concurrency: 2, - toDeploy: [ + toDeploy: createStackArtifacts([ { id: 'A', dependencies: [], name: SLOW }, { id: 'B', dependencies: [{ id: 'A' }] }, { id: 'C', dependencies: [] }, { id: 'D', dependencies: [{ id: 'C' }] }, - ], + ]), expected: ['C', 'D', 'A', 'B'], }, { scenario: 'A -> B, A not selected', concurrency: 1, - toDeploy: [ + toDeploy: createStackArtifacts([ { id: 'B', dependencies: [{ id: 'A' }] }, - ], + ]), expected: ['B'], }, ])('Success - Concurrency: $concurrency - $scenario', async ({ concurrency, expected, toDeploy }) => { @@ -155,63 +158,63 @@ describe('DeployStacks', () => { // Failure test.each([ // Concurrency 1 - { scenario: 'A (error)', concurrency: 1, toDeploy: [{ id: 'A', dependencies: [], displayName: 'A' }], expectedError: 'A', expectedStacks: [] }, - { scenario: 'A (error), B', concurrency: 1, toDeploy: [{ id: 'A', dependencies: [], displayName: 'A' }, { id: 'B', dependencies: [] }], expectedError: 'A', expectedStacks: [] }, - { scenario: 'A, B (error)', concurrency: 1, toDeploy: [{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [], displayName: 'B' }], expectedError: 'B', expectedStacks: ['A'] }, - { scenario: 'A (error) -> B', concurrency: 1, toDeploy: [{ id: 'A', dependencies: [], displayName: 'A' }, { id: 'B', dependencies: [{ id: 'A' }] }], expectedError: 'A', expectedStacks: [] }, - { scenario: '[unsorted] A (error) -> B', concurrency: 1, toDeploy: [{ id: 'B', dependencies: [{ id: 'A' }] }, { id: 'A', dependencies: [], displayName: 'A' }], expectedError: 'A', expectedStacks: [] }, + { scenario: 'A (error)', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [], displayName: 'A' }]), expectedError: 'A', expectedStacks: [] }, + { scenario: 'A (error), B', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [], displayName: 'A' }, { id: 'B', dependencies: [] }]), expectedError: 'A', expectedStacks: [] }, + { scenario: 'A, B (error)', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [], displayName: 'B' }]), expectedError: 'B', expectedStacks: ['A'] }, + { scenario: 'A (error) -> B', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [], displayName: 'A' }, { id: 'B', dependencies: [{ id: 'A' }] }]), expectedError: 'A', expectedStacks: [] }, + { scenario: '[unsorted] A (error) -> B', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'B', dependencies: [{ id: 'A' }] }, { id: 'A', dependencies: [], displayName: 'A' }]), expectedError: 'A', expectedStacks: [] }, { scenario: 'A (error) -> B, C -> D', concurrency: 1, - toDeploy: [ + toDeploy: createStackArtifacts([ { id: 'A', dependencies: [], displayName: 'A' }, { id: 'B', dependencies: [{ id: 'A' }] }, { id: 'C', dependencies: [] }, { id: 'D', dependencies: [{ id: 'C' }] }, - ], + ]), expectedError: 'A', expectedStacks: [], }, { scenario: 'A -> B, C (error) -> D', concurrency: 1, - toDeploy: [ + toDeploy: createStackArtifacts([ { id: 'A', dependencies: [] }, { id: 'B', dependencies: [{ id: 'A' }] }, { id: 'C', dependencies: [], displayName: 'C', name: SLOW }, { id: 'D', dependencies: [{ id: 'C' }] }, - ], + ]), expectedError: 'C', expectedStacks: ['A'], }, // Concurrency 2 - { scenario: 'A (error)', concurrency: 2, toDeploy: [{ id: 'A', dependencies: [], displayName: 'A' }], expectedError: 'A', expectedStacks: [] }, - { scenario: 'A (error), B', concurrency: 2, toDeploy: [{ id: 'A', dependencies: [], displayName: 'A' }, { id: 'B', dependencies: [] }], expectedError: 'A', expectedStacks: ['B'] }, - { scenario: 'A, B (error)', concurrency: 2, toDeploy: [{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [], displayName: 'B' }], expectedError: 'B', expectedStacks: ['A'] }, - { scenario: 'A (error) -> B', concurrency: 2, toDeploy: [{ id: 'A', dependencies: [], displayName: 'A' }, { id: 'B', dependencies: [{ id: 'A' }] }], expectedError: 'A', expectedStacks: [] }, - { scenario: '[unsorted] A (error) -> B', concurrency: 2, toDeploy: [{ id: 'B', dependencies: [{ id: 'A' }] }, { id: 'A', dependencies: [], displayName: 'A' }], expectedError: 'A', expectedStacks: [] }, + { scenario: 'A (error)', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [], displayName: 'A' }]), expectedError: 'A', expectedStacks: [] }, + { scenario: 'A (error), B', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [], displayName: 'A' }, { id: 'B', dependencies: [] }]), expectedError: 'A', expectedStacks: ['B'] }, + { scenario: 'A, B (error)', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [], displayName: 'B' }]), expectedError: 'B', expectedStacks: ['A'] }, + { scenario: 'A (error) -> B', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [], displayName: 'A' }, { id: 'B', dependencies: [{ id: 'A' }] }]), expectedError: 'A', expectedStacks: [] }, + { scenario: '[unsorted] A (error) -> B', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'B', dependencies: [{ id: 'A' }] }, { id: 'A', dependencies: [], displayName: 'A' }]), expectedError: 'A', expectedStacks: [] }, { scenario: 'A (error) -> B, C -> D', concurrency: 2, - toDeploy: [ + toDeploy: createStackArtifacts([ { id: 'A', dependencies: [], displayName: 'A' }, { id: 'B', dependencies: [{ id: 'A' }] }, { id: 'C', dependencies: [] }, { id: 'D', dependencies: [{ id: 'C' }] }, - ], + ]), expectedError: 'A', expectedStacks: ['C'], }, { scenario: 'A -> B, C (error) -> D', concurrency: 2, - toDeploy: [ + toDeploy: createStackArtifacts([ { id: 'A', dependencies: [] }, { id: 'B', dependencies: [{ id: 'A' }] }, { id: 'C', dependencies: [], displayName: 'C', name: SLOW }, { id: 'D', dependencies: [{ id: 'C' }] }, - ], + ]), expectedError: 'C', expectedStacks: ['A', 'B'], }, @@ -222,3 +225,21 @@ describe('DeployStacks', () => { expect(deployedStacks).toStrictEqual(expectedStacks); }); }); + +function createAssetArtifact(input: Record): Asset { + return { + ...input, + [ASSET_MANIFEST_ARTIFACT_SYM]: true, + } as unknown as Asset; +} + +function createStackArtifact(input: Record): Stack { + return { + ...input, + [CLOUDFORMATION_STACK_ARTIFACT_SYM]: true, + } as unknown as Stack; +} + +function createStackArtifacts(inputs: Record[]): Stack[] { + return inputs.map((i) => createStackArtifact(i)); +} \ No newline at end of file From 16e46dffab022735112ea393036b2ed534785d7e Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Fri, 21 Apr 2023 17:13:10 -0400 Subject: [PATCH 079/120] major overhaul of test to make it make sense --- packages/aws-cdk/lib/util/work-graph.ts | 6 + packages/aws-cdk/test/deploy.test.ts | 402 ++++++++++++++++-------- 2 files changed, 276 insertions(+), 132 deletions(-) diff --git a/packages/aws-cdk/lib/util/work-graph.ts b/packages/aws-cdk/lib/util/work-graph.ts index 341343d6a652e..66711eec05290 100644 --- a/packages/aws-cdk/lib/util/work-graph.ts +++ b/packages/aws-cdk/lib/util/work-graph.ts @@ -186,6 +186,12 @@ export class WorkGraph { this.readyPool.push(node); } } + for (let i = 0; i < this.readyPool.length; i++) { + const node = this.readyPool[i]; + if (node.deploymentState !== DeploymentState.QUEUED) { + this.readyPool.splice(i, 1); + } + } } private skipRest() { diff --git a/packages/aws-cdk/test/deploy.test.ts b/packages/aws-cdk/test/deploy.test.ts index 7432ac6e79ea0..6a83c66a78abf 100644 --- a/packages/aws-cdk/test/deploy.test.ts +++ b/packages/aws-cdk/test/deploy.test.ts @@ -21,10 +21,9 @@ const SLOW = 200; * - stack.name = deployment duration * - stack.displayName = error message */ -describe('DeployStacks', () => { - const deployedStacks: string[] = []; +describe('DeployAssets', () => { + const actionedAssets: string[] = []; const deployStack = async ({ id, displayName, name }: Stack) => { - console.log(id, displayName); const errorMessage = displayName; const timeout = Number(name) || 0; @@ -34,212 +33,351 @@ describe('DeployStacks', () => { throw Error(errorMessage); } - deployedStacks.push(id); + actionedAssets.push(id); }; - const builtAssets: string[] = []; const buildAsset = async({ id }: WorkNode) => { - builtAssets.push(id); + actionedAssets.push(id); }; - const publishedAssets: string[] = []; const publishAsset = async({ id }: WorkNode) => { - publishedAssets.push(id); + actionedAssets.push(id); }; beforeEach(() => { - deployedStacks.splice(0); - }); - - test('kaizen test', async () => { - const asset = createAssetArtifact({ id: 'AssetA', dependencies: [] }); - const assetB = createAssetArtifact({ id: 'AssetB', dependencies: [] }); - const stack = createStackArtifact({ id: 'StackA', dependencies: [asset] }); - const stackB = createStackArtifact({ id: 'StackB', dependencies: [assetB, stack] }); - // eslint-disable-next-line max-len - await expect(deployArtifacts([stack, asset, stackB, assetB] as Artifact[], { concurrency: 1, deployStack, buildAsset, publishAsset })).resolves.toBeUndefined(); - - expect(deployedStacks).toStrictEqual(['StackA', 'StackB']); + actionedAssets.splice(0); }); // Success test.each([ // Concurrency 1 { scenario: 'No Stacks', concurrency: 1, toDeploy: [], expected: [] }, - { scenario: 'A', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [] }]), expected: ['A'] }, - { scenario: 'A, B', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [] }]), expected: ['A', 'B'] }, - { scenario: 'A -> B', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [{ id: 'A' }] }]), expected: ['A', 'B'] }, - { scenario: '[unsorted] A -> B', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'B', dependencies: [{ id: 'A' }] }, { id: 'A', dependencies: [] }]), expected: ['A', 'B'] }, - { scenario: 'A -> B -> C', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [{ id: 'A' }] }, { id: 'C', dependencies: [{ id: 'B' }] }]), expected: ['A', 'B', 'C'] }, - { scenario: 'A -> B, A -> C', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [{ id: 'A' }] }, { id: 'C', dependencies: [{ id: 'A' }] }]), expected: ['A', 'B', 'C'] }, + { scenario: 'A', concurrency: 1, toDeploy: createArtifacts([{ id: 'A', type: 'stack' }]), expected: ['A'] }, + { scenario: 'A, B', concurrency: 1, toDeploy: createArtifacts([{ id: 'A', type: 'stack' }, { id: 'B', type: 'stack' }]), expected: ['A', 'B'] }, + { scenario: 'A -> B', concurrency: 1, toDeploy: createArtifacts([{ id: 'A', type: 'stack' }, { id: 'B', type: 'stack', stackDependencies: ['A'] }]), expected: ['A', 'B'] }, + { scenario: '[unsorted] A -> B', concurrency: 1, toDeploy: createArtifacts([{ id: 'B', type: 'stack', stackDependencies: ['A'] }, { id: 'A', type: 'stack' }]), expected: ['A', 'B'] }, + { scenario: 'A -> B -> C', concurrency: 1, toDeploy: createArtifacts([{ id: 'A', type: 'stack' }, { id: 'B', type: 'stack', stackDependencies: ['A'] }, { id: 'C', type: 'stack', stackDependencies: ['B'] }]), expected: ['A', 'B', 'C'] }, + { scenario: 'A -> B, A -> C', concurrency: 1, toDeploy: createArtifacts([{ id: 'A', type: 'stack' }, { id: 'B', type: 'stack', stackDependencies: ['A'] }, { id: 'C', type: 'stack', stackDependencies: ['A'] }]), expected: ['A', 'B', 'C'] }, { scenario: 'A (slow), B', concurrency: 1, - toDeploy: createStackArtifacts([ - { id: 'A', dependencies: [], name: SLOW }, - { id: 'B', dependencies: [] }, + toDeploy: createArtifacts([ + { id: 'A', type: 'stack', name: SLOW }, + { id: 'B', type: 'stack' }, ]), expected: ['A', 'B'], }, { scenario: 'A -> B, C -> D', concurrency: 1, - toDeploy: createStackArtifacts([ - { id: 'A', dependencies: [] }, - { id: 'B', dependencies: [{ id: 'A' }] }, - { id: 'C', dependencies: [] }, - { id: 'D', dependencies: [{ id: 'C' }] }, + toDeploy: createArtifacts([ + { id: 'A', type: 'stack' }, + { id: 'B', type: 'stack', stackDependencies: ['A'] }, + { id: 'C', type: 'stack' }, + { id: 'D', type: 'stack', stackDependencies: ['C'] }, ]), expected: ['A', 'C', 'B', 'D'], }, { scenario: 'A (slow) -> B, C -> D', concurrency: 1, - toDeploy: createStackArtifacts([ - { id: 'A', dependencies: [], name: SLOW }, - { id: 'B', dependencies: [{ id: 'A' }] }, - { id: 'C', dependencies: [] }, - { id: 'D', dependencies: [{ id: 'C' }] }, + toDeploy: createArtifacts([ + { id: 'A', type: 'stack', name: SLOW }, + { id: 'B', type: 'stack', stackDependencies: ['A'] }, + { id: 'C', type: 'stack' }, + { id: 'D', type: 'stack', stackDependencies: ['C'] }, ]), expected: ['A', 'C', 'B', 'D'], }, + // With Assets + { + scenario: 'A -> a', + concurrency: 1, + toDeploy: createArtifacts([ + { id: 'A', type: 'stack', assetDependencies: ['a'] }, + { id: 'a', type: 'asset' }, + ]), + expected: ['a-build', 'a-publish', 'A'], + }, + { + scenario: 'A -> [a, B]', + concurrency: 1, + toDeploy: createArtifacts([ + { id: 'A', type: 'stack', stackDependencies: ['B'], assetDependencies: ['a'] }, + { id: 'B', type: 'stack' }, + { id: 'a', type: 'asset' }, + ]), + expected: ['B', 'a-build', 'a-publish', 'A'], + }, + { + scenario: 'A -> a, B -> b', + concurrency: 1, + toDeploy: createArtifacts([ + { id: 'A', type: 'stack', assetDependencies: ['a'] }, + { id: 'B', type: 'stack', assetDependencies: ['b'] }, + { id: 'a', type: 'asset' }, + { id: 'b', type: 'asset' }, + ]), + expected: ['a-build', 'b-build', 'a-publish', 'b-publish', 'A', 'B'], + }, + { + scenario: 'A, B -> b -> A', + concurrency: 1, + toDeploy: createArtifacts([ + { id: 'A', type: 'stack' }, + { id: 'B', type: 'stack', assetDependencies: ['b'] }, + { id: 'b', type: 'asset', stackDependencies: ['A'] }, + ]), + expected: ['A', 'b-build', 'b-publish', 'B'], + }, // Concurrency 2 { scenario: 'No Stacks', concurrency: 2, toDeploy: [], expected: [] }, - { scenario: 'A', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [] }]), expected: ['A'] }, - { scenario: 'A, B', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [] }]), expected: ['A', 'B'] }, - { scenario: 'A -> B', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [{ id: 'A' }] }]), expected: ['A', 'B'] }, - { scenario: '[unsorted] A -> B', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'B', dependencies: [{ id: 'A' }] }, { id: 'A', dependencies: [] }]), expected: ['A', 'B'] }, - { scenario: 'A -> B -> C', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [{ id: 'A' }] }, { id: 'C', dependencies: [{ id: 'B' }] }]), expected: ['A', 'B', 'C'] }, - { scenario: 'A -> B, A -> C', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [{ id: 'A' }] }, { id: 'C', dependencies: [{ id: 'A' }] }]), expected: ['A', 'B', 'C'] }, + { scenario: 'A', concurrency: 2, toDeploy: createArtifacts([{ id: 'A', type: 'stack' }]), expected: ['A'] }, + { scenario: 'A, B', concurrency: 2, toDeploy: createArtifacts([{ id: 'A', type: 'stack' }, { id: 'B', type: 'stack' }]), expected: ['A', 'B'] }, + { scenario: 'A -> B', concurrency: 2, toDeploy: createArtifacts([{ id: 'A', type: 'stack' }, { id: 'B', type: 'stack', stackDependencies: ['A'] }]), expected: ['A', 'B'] }, + { scenario: '[unsorted] A -> B', concurrency: 2, toDeploy: createArtifacts([{ id: 'B', type: 'stack', stackDependencies: ['A'] }, { id: 'A', type: 'stack' }]), expected: ['A', 'B'] }, + { scenario: 'A -> B -> C', concurrency: 2, toDeploy: createArtifacts([{ id: 'A', type: 'stack' }, { id: 'B', type: 'stack', stackDependencies: ['A'] }, { id: 'C', type: 'stack', stackDependencies: ['B'] }]), expected: ['A', 'B', 'C'] }, + { scenario: 'A -> B, A -> C', concurrency: 2, toDeploy: createArtifacts([{ id: 'A', type: 'stack' }, { id: 'B', type: 'stack', stackDependencies: ['A'] }, { id: 'C', type: 'stack', stackDependencies: ['A'] }]), expected: ['A', 'B', 'C'] }, { scenario: 'A, B', concurrency: 2, - toDeploy: createStackArtifacts([ - { id: 'A', dependencies: [], name: SLOW }, - { id: 'B', dependencies: [] }, + toDeploy: createArtifacts([ + { id: 'A', type: 'stack', name: SLOW }, + { id: 'B', type: 'stack' }, ]), expected: ['B', 'A'], }, { scenario: 'A -> B, C -> D', concurrency: 2, - toDeploy: createStackArtifacts([ - { id: 'A', dependencies: [] }, - { id: 'B', dependencies: [{ id: 'A' }] }, - { id: 'C', dependencies: [] }, - { id: 'D', dependencies: [{ id: 'C' }] }, + toDeploy: createArtifacts([ + { id: 'A', type: 'stack' }, + { id: 'B', type: 'stack', stackDependencies: ['A'] }, + { id: 'C', type: 'stack' }, + { id: 'D', type: 'stack', stackDependencies: ['C'] }, ]), expected: ['A', 'C', 'B', 'D'], }, { scenario: 'A (slow) -> B, C -> D', concurrency: 2, - toDeploy: createStackArtifacts([ - { id: 'A', dependencies: [], name: SLOW }, - { id: 'B', dependencies: [{ id: 'A' }] }, - { id: 'C', dependencies: [] }, - { id: 'D', dependencies: [{ id: 'C' }] }, + toDeploy: createArtifacts([ + { id: 'A', type: 'stack', name: SLOW }, + { id: 'B', type: 'stack', stackDependencies: ['A'] }, + { id: 'C', type: 'stack' }, + { id: 'D', type: 'stack', stackDependencies: ['C'] }, ]), expected: ['C', 'D', 'A', 'B'], }, { scenario: 'A -> B, A not selected', concurrency: 1, - toDeploy: createStackArtifacts([ - { id: 'B', dependencies: [{ id: 'A' }] }, + toDeploy: createArtifacts([ + { id: 'B', type: 'stack', stackDependencies: ['A'] }, ]), expected: ['B'], }, - ])('Success - Concurrency: $concurrency - $scenario', async ({ concurrency, expected, toDeploy }) => { - await expect(deployArtifacts(toDeploy as unknown as Stack[], { concurrency, deployStack, buildAsset, publishAsset })).resolves.toBeUndefined(); - - expect(deployedStacks).toStrictEqual(expected); - }); - - // Failure - test.each([ - // Concurrency 1 - { scenario: 'A (error)', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [], displayName: 'A' }]), expectedError: 'A', expectedStacks: [] }, - { scenario: 'A (error), B', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [], displayName: 'A' }, { id: 'B', dependencies: [] }]), expectedError: 'A', expectedStacks: [] }, - { scenario: 'A, B (error)', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [], displayName: 'B' }]), expectedError: 'B', expectedStacks: ['A'] }, - { scenario: 'A (error) -> B', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [], displayName: 'A' }, { id: 'B', dependencies: [{ id: 'A' }] }]), expectedError: 'A', expectedStacks: [] }, - { scenario: '[unsorted] A (error) -> B', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'B', dependencies: [{ id: 'A' }] }, { id: 'A', dependencies: [], displayName: 'A' }]), expectedError: 'A', expectedStacks: [] }, + // With Assets { - scenario: 'A (error) -> B, C -> D', - concurrency: 1, - toDeploy: createStackArtifacts([ - { id: 'A', dependencies: [], displayName: 'A' }, - { id: 'B', dependencies: [{ id: 'A' }] }, - { id: 'C', dependencies: [] }, - { id: 'D', dependencies: [{ id: 'C' }] }, + scenario: 'A -> a', + concurrency: 2, + toDeploy: createArtifacts([ + { id: 'A', type: 'stack', assetDependencies: ['a'] }, + { id: 'a', type: 'asset' }, ]), - expectedError: 'A', - expectedStacks: [], + expected: ['a-build', 'a-publish', 'A'], }, { - scenario: 'A -> B, C (error) -> D', - concurrency: 1, - toDeploy: createStackArtifacts([ - { id: 'A', dependencies: [] }, - { id: 'B', dependencies: [{ id: 'A' }] }, - { id: 'C', dependencies: [], displayName: 'C', name: SLOW }, - { id: 'D', dependencies: [{ id: 'C' }] }, + scenario: 'A -> [a, B]', + concurrency: 2, + toDeploy: createArtifacts([ + { id: 'A', type: 'stack', stackDependencies: ['B'], assetDependencies: ['a'] }, + { id: 'B', type: 'stack', name: SLOW }, + { id: 'a', type: 'asset' }, ]), - expectedError: 'C', - expectedStacks: ['A'], + expected: ['a-build', 'a-publish', 'B', 'A'], + }, + { + scenario: 'A -> a, B -> b', + concurrency: 2, + toDeploy: createArtifacts([ + { id: 'A', type: 'stack', assetDependencies: ['a'] }, + { id: 'B', type: 'stack', assetDependencies: ['b'] }, + { id: 'a', type: 'asset' }, + { id: 'b', type: 'asset' }, + ]), + expected: ['a-build', 'b-build', 'a-publish', 'b-publish', 'A', 'B'], }, - - // Concurrency 2 - { scenario: 'A (error)', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [], displayName: 'A' }]), expectedError: 'A', expectedStacks: [] }, - { scenario: 'A (error), B', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [], displayName: 'A' }, { id: 'B', dependencies: [] }]), expectedError: 'A', expectedStacks: ['B'] }, - { scenario: 'A, B (error)', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [], displayName: 'B' }]), expectedError: 'B', expectedStacks: ['A'] }, - { scenario: 'A (error) -> B', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [], displayName: 'A' }, { id: 'B', dependencies: [{ id: 'A' }] }]), expectedError: 'A', expectedStacks: [] }, - { scenario: '[unsorted] A (error) -> B', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'B', dependencies: [{ id: 'A' }] }, { id: 'A', dependencies: [], displayName: 'A' }]), expectedError: 'A', expectedStacks: [] }, { - scenario: 'A (error) -> B, C -> D', + scenario: 'A, B -> b -> A', concurrency: 2, - toDeploy: createStackArtifacts([ - { id: 'A', dependencies: [], displayName: 'A' }, - { id: 'B', dependencies: [{ id: 'A' }] }, - { id: 'C', dependencies: [] }, - { id: 'D', dependencies: [{ id: 'C' }] }, + toDeploy: createArtifacts([ + { id: 'A', type: 'stack' }, + { id: 'B', type: 'stack', assetDependencies: ['b'] }, + { id: 'b', type: 'asset', stackDependencies: ['A'] }, ]), - expectedError: 'A', - expectedStacks: ['C'], + expected: ['A', 'b-build', 'b-publish', 'B'], }, { - scenario: 'A -> B, C (error) -> D', + scenario: 'A, B -> [b, c], b -> A', concurrency: 2, - toDeploy: createStackArtifacts([ - { id: 'A', dependencies: [] }, - { id: 'B', dependencies: [{ id: 'A' }] }, - { id: 'C', dependencies: [], displayName: 'C', name: SLOW }, - { id: 'D', dependencies: [{ id: 'C' }] }, + toDeploy: createArtifacts([ + { id: 'A', type: 'stack', name: SLOW }, + { id: 'B', type: 'stack', assetDependencies: ['b', 'c'] }, + { id: 'b', type: 'asset', stackDependencies: ['A'] }, + { id: 'c', type: 'asset' }, ]), - expectedError: 'C', - expectedStacks: ['A', 'B'], + expected: ['c-build', 'c-publish', 'A', 'b-build', 'b-publish', 'B'], }, - ])('Failure - Concurrency: $concurrency - $scenario', async ({ concurrency, expectedError, toDeploy, expectedStacks }) => { - // eslint-disable-next-line max-len - await expect(deployArtifacts(toDeploy as unknown as Stack[], { concurrency, deployStack, buildAsset, publishAsset })).rejects.toThrowError(expectedError); + ])('Success - Concurrency: $concurrency - $scenario', async ({ concurrency, expected, toDeploy }) => { + await expect(deployArtifacts(toDeploy as unknown as Stack[], { concurrency, deployStack, buildAsset, publishAsset })).resolves.toBeUndefined(); - expect(deployedStacks).toStrictEqual(expectedStacks); + expect(actionedAssets).toStrictEqual(expected); }); + + // Failure + // test.each([ + // // Concurrency 1 + // { scenario: 'A (error)', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [], displayName: 'A' }]), expectedError: 'A', expectedStacks: [] }, + // { scenario: 'A (error), B', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [], displayName: 'A' }, { id: 'B', dependencies: [] }]), expectedError: 'A', expectedStacks: [] }, + // { scenario: 'A, B (error)', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [], displayName: 'B' }]), expectedError: 'B', expectedStacks: ['A'] }, + // { scenario: 'A (error) -> B', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [], displayName: 'A' }, { id: 'B', dependencies: [{ id: 'A' }] }]), expectedError: 'A', expectedStacks: [] }, + // { scenario: '[unsorted] A (error) -> B', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'B', dependencies: [{ id: 'A' }] }, { id: 'A', dependencies: [], displayName: 'A' }]), expectedError: 'A', expectedStacks: [] }, + // { + // scenario: 'A (error) -> B, C -> D', + // concurrency: 1, + // toDeploy: createStackArtifacts([ + // { id: 'A', dependencies: [], displayName: 'A' }, + // { id: 'B', dependencies: [{ id: 'A' }] }, + // { id: 'C', dependencies: [] }, + // { id: 'D', dependencies: [{ id: 'C' }] }, + // ]), + // expectedError: 'A', + // expectedStacks: [], + // }, + // { + // scenario: 'A -> B, C (error) -> D', + // concurrency: 1, + // toDeploy: createStackArtifacts([ + // { id: 'A', dependencies: [] }, + // { id: 'B', dependencies: [{ id: 'A' }] }, + // { id: 'C', dependencies: [], displayName: 'C', name: SLOW }, + // { id: 'D', dependencies: [{ id: 'C' }] }, + // ]), + // expectedError: 'C', + // expectedStacks: ['A'], + // }, + + // // Concurrency 2 + // { scenario: 'A (error)', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [], displayName: 'A' }]), expectedError: 'A', expectedStacks: [] }, + // { scenario: 'A (error), B', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [], displayName: 'A' }, { id: 'B', dependencies: [] }]), expectedError: 'A', expectedStacks: ['B'] }, + // { scenario: 'A, B (error)', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [], displayName: 'B' }]), expectedError: 'B', expectedStacks: ['A'] }, + // { scenario: 'A (error) -> B', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [], displayName: 'A' }, { id: 'B', dependencies: [{ id: 'A' }] }]), expectedError: 'A', expectedStacks: [] }, + // { scenario: '[unsorted] A (error) -> B', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'B', dependencies: [{ id: 'A' }] }, { id: 'A', dependencies: [], displayName: 'A' }]), expectedError: 'A', expectedStacks: [] }, + // { + // scenario: 'A (error) -> B, C -> D', + // concurrency: 2, + // toDeploy: createStackArtifacts([ + // { id: 'A', dependencies: [], displayName: 'A' }, + // { id: 'B', dependencies: [{ id: 'A' }] }, + // { id: 'C', dependencies: [] }, + // { id: 'D', dependencies: [{ id: 'C' }] }, + // ]), + // expectedError: 'A', + // expectedStacks: ['C'], + // }, + // { + // scenario: 'A -> B, C (error) -> D', + // concurrency: 2, + // toDeploy: createStackArtifacts([ + // { id: 'A', dependencies: [] }, + // { id: 'B', dependencies: [{ id: 'A' }] }, + // { id: 'C', dependencies: [], displayName: 'C', name: SLOW }, + // { id: 'D', dependencies: [{ id: 'C' }] }, + // ]), + // expectedError: 'C', + // expectedStacks: ['A', 'B'], + // }, + // ])('Failure - Concurrency: $concurrency - $scenario', async ({ concurrency, expectedError, toDeploy, expectedStacks }) => { + // // eslint-disable-next-line max-len + // await expect(deployArtifacts(toDeploy as unknown as Stack[], { concurrency, deployStack, buildAsset, publishAsset })).rejects.toThrowError(expectedError); + + // expect(deployedStacks).toStrictEqual(expectedStacks); + // }); + + // Success with Asset Artifacts + // test.each([ + // { + // scenario: 'A -> [a, B]', + // concurrency: 1, + // toDeploy: createArtifacts([ + // { id: 'A', type: 'stack', stackDependencies: ['B'], assetDependencies: ['a'] }, + // { id: 'B', type: 'stack' }, + // { id: 'a', type: 'asset' }, + // ]), + // }, + // { + // scenario: 'A -> a, B -> b', + // concurrency: 1, + // toDeploy: [ + // createStackArtifact({ id: 'A', dependencies: ['a'] }), + // createStackArtifact({ id: 'B', dependencies: ['b'] }), + // createAssetArtifact({ id: 'a', dependencies: [] }), + // createAssetArtifact({ id: 'b', dependencies: [] }), + // ], + // }, + // { + // scenario: 'A, B -> b -> A', + // concurrency: 1, + // toDeploy: [ + // createStackArtifact({ id: 'A', dependencies: [] }), + // createStackArtifact({ id: 'B', dependencies: ['b'] }), + // createAssetArtifact({ id: 'b', dependencies: ['A'] }), + // ], + // }, + // ])('Success - Concurrency: $concurrency - $scenario', async ({ concurrency, toDeploy, expectedStacks }) => { + // const asset = createAssetArtifact({ id: 'AssetA', dependencies: [] }); + // const assetB = createAssetArtifact({ id: 'AssetB', dependencies: [] }); + // const stack = createStackArtifact({ id: 'StackA', dependencies: [asset] }); + // const stackB = createStackArtifact({ id: 'StackB', dependencies: [assetB, stack] }); + // // eslint-disable-next-line max-len + // await expect(deployArtifacts([stack, asset, stackB, assetB] as Artifact[], { concurrency, deployStack, buildAsset, publishAsset })).resolves.toBeUndefined(); + + // expect(deployedStacks).toStrictEqual(['StackA', 'StackB']); + // expect(publishedAssets).toStrictEqual(['AssetA-publish', 'AssetB-publish']); + // expect(builtAssets).toStrictEqual(['AssetA-build', 'AssetB-build']); + // }); }); -function createAssetArtifact(input: Record): Asset { - return { - ...input, - [ASSET_MANIFEST_ARTIFACT_SYM]: true, - } as unknown as Asset; +interface TestArtifact { + stackDependencies?: string[]; + assetDependencies?: string[]; + id: string; + type: 'stack' | 'asset'; + name?: number; } -function createStackArtifact(input: Record): Stack { - return { - ...input, - [CLOUDFORMATION_STACK_ARTIFACT_SYM]: true, - } as unknown as Stack; +function createArtifact(artifact: TestArtifact): Artifact { + const stackDeps: Artifact[] = artifact.stackDependencies?.map((id) => createArtifact({ id, type: 'stack' })) ?? []; + const assetDeps: Artifact[] = artifact.assetDependencies?.map((id) => createArtifact({ id, type: 'asset' })) ?? []; + + const art = { + id: artifact.id, + dependencies: stackDeps.concat(assetDeps), + name: artifact.name, + }; + if (artifact.type === 'stack') { + return { + ...art, + [CLOUDFORMATION_STACK_ARTIFACT_SYM]: true, + } as unknown as Stack; + } else { + return { + ...art, + [ASSET_MANIFEST_ARTIFACT_SYM]: true, + } as unknown as Asset; + } } -function createStackArtifacts(inputs: Record[]): Stack[] { - return inputs.map((i) => createStackArtifact(i)); -} \ No newline at end of file +function createArtifacts(artifacts: TestArtifact[]): Artifact[] { + return artifacts.map((art) => createArtifact(art)); +} From c3268f73a987884a78ccbd9afadde77b8fc1f60d Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Mon, 24 Apr 2023 11:20:55 -0400 Subject: [PATCH 080/120] fix rest of the tests in display.ts --- .../stack-synthesizers/default-synthesizer.ts | 4 +- packages/aws-cdk/lib/util/work-graph.ts | 2 +- packages/aws-cdk/test/deploy.test.ts | 179 +++++++----------- 3 files changed, 72 insertions(+), 113 deletions(-) diff --git a/packages/aws-cdk-lib/core/lib/stack-synthesizers/default-synthesizer.ts b/packages/aws-cdk-lib/core/lib/stack-synthesizers/default-synthesizer.ts index e7eaee813239e..15c9ddf064276 100644 --- a/packages/aws-cdk-lib/core/lib/stack-synthesizers/default-synthesizer.ts +++ b/packages/aws-cdk-lib/core/lib/stack-synthesizers/default-synthesizer.ts @@ -1,10 +1,10 @@ -import * as cxapi from '../../../cx-api'; -import { StringSpecializer } from '../helpers-internal'; import { assertBound } from './_shared'; import { AssetManifestBuilder } from './asset-manifest-builder'; import { StackSynthesizer } from './stack-synthesizer'; import { ISynthesisSession, IReusableStackSynthesizer, IBoundStackSynthesizer } from './types'; +import * as cxapi from '../../../cx-api'; import { DockerImageAssetLocation, DockerImageAssetSource, FileAssetLocation, FileAssetSource } from '../assets'; +import { StringSpecializer } from '../helpers-internal'; import { Stack } from '../stack'; import { Token } from '../token'; diff --git a/packages/aws-cdk/lib/util/work-graph.ts b/packages/aws-cdk/lib/util/work-graph.ts index 66711eec05290..b540cfbfca1db 100644 --- a/packages/aws-cdk/lib/util/work-graph.ts +++ b/packages/aws-cdk/lib/util/work-graph.ts @@ -40,7 +40,7 @@ export class WorkGraph { type: WorkType.ASSET_BUILD, dependencies: getDepIds(artifact.dependencies), artifact, - deploymentState: 'pending' as DeploymentState, + deploymentState: DeploymentState.PENDING, }; graphNodes.push(buildNode); graphNodes.push({ diff --git a/packages/aws-cdk/test/deploy.test.ts b/packages/aws-cdk/test/deploy.test.ts index 6a83c66a78abf..ce12012527174 100644 --- a/packages/aws-cdk/test/deploy.test.ts +++ b/packages/aws-cdk/test/deploy.test.ts @@ -103,7 +103,7 @@ describe('DeployAssets', () => { toDeploy: createArtifacts([ { id: 'A', type: 'stack', stackDependencies: ['B'], assetDependencies: ['a'] }, { id: 'B', type: 'stack' }, - { id: 'a', type: 'asset' }, + { id: 'a', type: 'asset', name: SLOW }, ]), expected: ['B', 'a-build', 'a-publish', 'A'], }, @@ -235,117 +235,74 @@ describe('DeployAssets', () => { }); // Failure - // test.each([ - // // Concurrency 1 - // { scenario: 'A (error)', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [], displayName: 'A' }]), expectedError: 'A', expectedStacks: [] }, - // { scenario: 'A (error), B', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [], displayName: 'A' }, { id: 'B', dependencies: [] }]), expectedError: 'A', expectedStacks: [] }, - // { scenario: 'A, B (error)', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [], displayName: 'B' }]), expectedError: 'B', expectedStacks: ['A'] }, - // { scenario: 'A (error) -> B', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [], displayName: 'A' }, { id: 'B', dependencies: [{ id: 'A' }] }]), expectedError: 'A', expectedStacks: [] }, - // { scenario: '[unsorted] A (error) -> B', concurrency: 1, toDeploy: createStackArtifacts([{ id: 'B', dependencies: [{ id: 'A' }] }, { id: 'A', dependencies: [], displayName: 'A' }]), expectedError: 'A', expectedStacks: [] }, - // { - // scenario: 'A (error) -> B, C -> D', - // concurrency: 1, - // toDeploy: createStackArtifacts([ - // { id: 'A', dependencies: [], displayName: 'A' }, - // { id: 'B', dependencies: [{ id: 'A' }] }, - // { id: 'C', dependencies: [] }, - // { id: 'D', dependencies: [{ id: 'C' }] }, - // ]), - // expectedError: 'A', - // expectedStacks: [], - // }, - // { - // scenario: 'A -> B, C (error) -> D', - // concurrency: 1, - // toDeploy: createStackArtifacts([ - // { id: 'A', dependencies: [] }, - // { id: 'B', dependencies: [{ id: 'A' }] }, - // { id: 'C', dependencies: [], displayName: 'C', name: SLOW }, - // { id: 'D', dependencies: [{ id: 'C' }] }, - // ]), - // expectedError: 'C', - // expectedStacks: ['A'], - // }, - - // // Concurrency 2 - // { scenario: 'A (error)', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [], displayName: 'A' }]), expectedError: 'A', expectedStacks: [] }, - // { scenario: 'A (error), B', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [], displayName: 'A' }, { id: 'B', dependencies: [] }]), expectedError: 'A', expectedStacks: ['B'] }, - // { scenario: 'A, B (error)', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [], displayName: 'B' }]), expectedError: 'B', expectedStacks: ['A'] }, - // { scenario: 'A (error) -> B', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'A', dependencies: [], displayName: 'A' }, { id: 'B', dependencies: [{ id: 'A' }] }]), expectedError: 'A', expectedStacks: [] }, - // { scenario: '[unsorted] A (error) -> B', concurrency: 2, toDeploy: createStackArtifacts([{ id: 'B', dependencies: [{ id: 'A' }] }, { id: 'A', dependencies: [], displayName: 'A' }]), expectedError: 'A', expectedStacks: [] }, - // { - // scenario: 'A (error) -> B, C -> D', - // concurrency: 2, - // toDeploy: createStackArtifacts([ - // { id: 'A', dependencies: [], displayName: 'A' }, - // { id: 'B', dependencies: [{ id: 'A' }] }, - // { id: 'C', dependencies: [] }, - // { id: 'D', dependencies: [{ id: 'C' }] }, - // ]), - // expectedError: 'A', - // expectedStacks: ['C'], - // }, - // { - // scenario: 'A -> B, C (error) -> D', - // concurrency: 2, - // toDeploy: createStackArtifacts([ - // { id: 'A', dependencies: [] }, - // { id: 'B', dependencies: [{ id: 'A' }] }, - // { id: 'C', dependencies: [], displayName: 'C', name: SLOW }, - // { id: 'D', dependencies: [{ id: 'C' }] }, - // ]), - // expectedError: 'C', - // expectedStacks: ['A', 'B'], - // }, - // ])('Failure - Concurrency: $concurrency - $scenario', async ({ concurrency, expectedError, toDeploy, expectedStacks }) => { - // // eslint-disable-next-line max-len - // await expect(deployArtifacts(toDeploy as unknown as Stack[], { concurrency, deployStack, buildAsset, publishAsset })).rejects.toThrowError(expectedError); - - // expect(deployedStacks).toStrictEqual(expectedStacks); - // }); + test.each([ + // Concurrency 1 + { scenario: 'A (error)', concurrency: 1, toDeploy: createArtifacts([{ id: 'A', type: 'stack', displayName: 'A' }]), expectedError: 'A', expectedStacks: [] }, + { scenario: 'A (error), B', concurrency: 1, toDeploy: createArtifacts([{ id: 'A', type: 'stack', displayName: 'A' }, { id: 'B', type: 'stack' }]), expectedError: 'A', expectedStacks: [] }, + { scenario: 'A, B (error)', concurrency: 1, toDeploy: createArtifacts([{ id: 'A', type: 'stack' }, { id: 'B', type: 'stack', displayName: 'B' }]), expectedError: 'B', expectedStacks: ['A'] }, + { scenario: 'A (error) -> B', concurrency: 1, toDeploy: createArtifacts([{ id: 'A', type: 'stack', displayName: 'A' }, { id: 'B', type: 'stack', stackDependencies: ['A'] }]), expectedError: 'A', expectedStacks: [] }, + { scenario: '[unsorted] A (error) -> B', concurrency: 1, toDeploy: createArtifacts([{ id: 'B', type: 'stack', stackDependencies: ['A'] }, { id: 'A', type: 'stack', displayName: 'A' }]), expectedError: 'A', expectedStacks: [] }, + { + scenario: 'A (error) -> B, C -> D', + concurrency: 1, + toDeploy: createArtifacts([ + { id: 'A', type: 'stack', displayName: 'A' }, + { id: 'B', type: 'stack', stackDependencies: ['A'] }, + { id: 'C', type: 'stack' }, + { id: 'D', type: 'stack', stackDependencies: ['C'] }, + ]), + expectedError: 'A', + expectedStacks: [], + }, + { + scenario: 'A -> B, C (error) -> D', + concurrency: 1, + toDeploy: createArtifacts([ + { id: 'A', type: 'stack' }, + { id: 'B', type: 'stack', stackDependencies: ['A'] }, + { id: 'C', type: 'stack', displayName: 'C', name: SLOW }, + { id: 'D', type: 'stack', stackDependencies: ['C'] }, + ]), + expectedError: 'C', + expectedStacks: ['A'], + }, - // Success with Asset Artifacts - // test.each([ - // { - // scenario: 'A -> [a, B]', - // concurrency: 1, - // toDeploy: createArtifacts([ - // { id: 'A', type: 'stack', stackDependencies: ['B'], assetDependencies: ['a'] }, - // { id: 'B', type: 'stack' }, - // { id: 'a', type: 'asset' }, - // ]), - // }, - // { - // scenario: 'A -> a, B -> b', - // concurrency: 1, - // toDeploy: [ - // createStackArtifact({ id: 'A', dependencies: ['a'] }), - // createStackArtifact({ id: 'B', dependencies: ['b'] }), - // createAssetArtifact({ id: 'a', dependencies: [] }), - // createAssetArtifact({ id: 'b', dependencies: [] }), - // ], - // }, - // { - // scenario: 'A, B -> b -> A', - // concurrency: 1, - // toDeploy: [ - // createStackArtifact({ id: 'A', dependencies: [] }), - // createStackArtifact({ id: 'B', dependencies: ['b'] }), - // createAssetArtifact({ id: 'b', dependencies: ['A'] }), - // ], - // }, - // ])('Success - Concurrency: $concurrency - $scenario', async ({ concurrency, toDeploy, expectedStacks }) => { - // const asset = createAssetArtifact({ id: 'AssetA', dependencies: [] }); - // const assetB = createAssetArtifact({ id: 'AssetB', dependencies: [] }); - // const stack = createStackArtifact({ id: 'StackA', dependencies: [asset] }); - // const stackB = createStackArtifact({ id: 'StackB', dependencies: [assetB, stack] }); - // // eslint-disable-next-line max-len - // await expect(deployArtifacts([stack, asset, stackB, assetB] as Artifact[], { concurrency, deployStack, buildAsset, publishAsset })).resolves.toBeUndefined(); + // Concurrency 2 + { scenario: 'A (error)', concurrency: 2, toDeploy: createArtifacts([{ id: 'A', type: 'stack', displayName: 'A' }]), expectedError: 'A', expectedStacks: [] }, + { scenario: 'A (error), B', concurrency: 2, toDeploy: createArtifacts([{ id: 'A', type: 'stack', displayName: 'A' }, { id: 'B', type: 'stack' }]), expectedError: 'A', expectedStacks: ['B'] }, + { scenario: 'A, B (error)', concurrency: 2, toDeploy: createArtifacts([{ id: 'A', type: 'stack' }, { id: 'B', type: 'stack', displayName: 'B' }]), expectedError: 'B', expectedStacks: ['A'] }, + { scenario: 'A (error) -> B', concurrency: 2, toDeploy: createArtifacts([{ id: 'A', type: 'stack', displayName: 'A' }, { id: 'B', type: 'stack', stackDependencies: ['A'] }]), expectedError: 'A', expectedStacks: [] }, + { scenario: '[unsorted] A (error) -> B', concurrency: 2, toDeploy: createArtifacts([{ id: 'B', type: 'stack', stackDependencies: ['A'] }, { id: 'A', type: 'stack', displayName: 'A' }]), expectedError: 'A', expectedStacks: [] }, + { + scenario: 'A (error) -> B, C -> D', + concurrency: 2, + toDeploy: createArtifacts([ + { id: 'A', type: 'stack', displayName: 'A' }, + { id: 'B', type: 'stack', stackDependencies: ['A'] }, + { id: 'C', type: 'stack' }, + { id: 'D', type: 'stack', stackDependencies: ['C'] }, + ]), + expectedError: 'A', + expectedStacks: ['C'], + }, + { + scenario: 'A -> B, C (error) -> D', + concurrency: 2, + toDeploy: createArtifacts([ + { id: 'A', type: 'stack' }, + { id: 'B', type: 'stack', stackDependencies: ['A'] }, + { id: 'C', type: 'stack', displayName: 'C', name: SLOW }, + { id: 'D', type: 'stack', stackDependencies: ['C'] }, + ]), + expectedError: 'C', + expectedStacks: ['A', 'B'], + }, + ])('Failure - Concurrency: $concurrency - $scenario', async ({ concurrency, expectedError, toDeploy, expectedStacks }) => { + // eslint-disable-next-line max-len + await expect(deployArtifacts(toDeploy as unknown as Stack[], { concurrency, deployStack, buildAsset, publishAsset })).rejects.toThrowError(expectedError); - // expect(deployedStacks).toStrictEqual(['StackA', 'StackB']); - // expect(publishedAssets).toStrictEqual(['AssetA-publish', 'AssetB-publish']); - // expect(builtAssets).toStrictEqual(['AssetA-build', 'AssetB-build']); - // }); + expect(actionedAssets).toStrictEqual(expectedStacks); + }); }); interface TestArtifact { @@ -354,6 +311,7 @@ interface TestArtifact { id: string; type: 'stack' | 'asset'; name?: number; + displayName?: string; } function createArtifact(artifact: TestArtifact): Artifact { @@ -364,6 +322,7 @@ function createArtifact(artifact: TestArtifact): Artifact { id: artifact.id, dependencies: stackDeps.concat(assetDeps), name: artifact.name, + displayName: artifact.displayName, }; if (artifact.type === 'stack') { return { From a0851eb5b8323dc31991de2874ea14985e3798a7 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Mon, 24 Apr 2023 11:22:39 -0400 Subject: [PATCH 081/120] remove etraneous files --- packages/aws-cdk/test/cloud-assembly-trees/manifest.json | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 packages/aws-cdk/test/cloud-assembly-trees/manifest.json diff --git a/packages/aws-cdk/test/cloud-assembly-trees/manifest.json b/packages/aws-cdk/test/cloud-assembly-trees/manifest.json deleted file mode 100644 index 3f3eca6fe11c8..0000000000000 --- a/packages/aws-cdk/test/cloud-assembly-trees/manifest.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": "31.0.0", - "templateFile": "" -} \ No newline at end of file From 466e3cd10aca4e98266c843272f0ddd86984dc3e Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Mon, 24 Apr 2023 13:35:11 -0400 Subject: [PATCH 082/120] callback interface for deployArtifacts --- packages/aws-cdk/lib/cdk-toolkit.ts | 12 ++++++++-- packages/aws-cdk/lib/deploy.ts | 20 ++++++++-------- packages/aws-cdk/test/deploy.test.ts | 34 +++++++++++++++------------- 3 files changed, 39 insertions(+), 27 deletions(-) diff --git a/packages/aws-cdk/lib/cdk-toolkit.ts b/packages/aws-cdk/lib/cdk-toolkit.ts index 0a4041fd9056b..46955403a2c88 100644 --- a/packages/aws-cdk/lib/cdk-toolkit.ts +++ b/packages/aws-cdk/lib/cdk-toolkit.ts @@ -218,7 +218,8 @@ export class CdkToolkit { print('\n%s: assets published\n', chalk.bold(assetNode.stack.displayName)); }; - const deployStack = async (stack: cxapi.CloudFormationStackArtifact) => { + const deployStack = async (assetNode: WorkNode) => { + const stack = assetNode.stack; if (stackCollection.stackCount !== 1) { highlight(stack.displayName); } if (!stack.environment) { @@ -351,7 +352,14 @@ export class CdkToolkit { } try { - await deployArtifacts(cloudArtifacts, { concurrency, deployStack, buildAsset, publishAsset }); + await deployArtifacts(cloudArtifacts, { + concurrency, + callbacks: { + deployStack, + buildAsset, + publishAsset, + }, + }); } catch (e) { error('\n ❌ Deployment failed: %s', e); throw e; diff --git a/packages/aws-cdk/lib/deploy.ts b/packages/aws-cdk/lib/deploy.ts index 448e4e05fcbd8..5e06eeb254d76 100644 --- a/packages/aws-cdk/lib/deploy.ts +++ b/packages/aws-cdk/lib/deploy.ts @@ -1,18 +1,20 @@ import * as cxapi from '@aws-cdk/cx-api'; import { WorkGraph, WorkNode, WorkType } from './util/work-graph'; -type Options = { - concurrency: number; - deployStack: (stack: cxapi.CloudFormationStackArtifact) => Promise; +interface CallbackActions { + deployStack: (assetNode: WorkNode) => Promise; buildAsset: (assetNode: WorkNode) => Promise; publishAsset: (assetNode: WorkNode) => Promise; +} + +type Options = { + concurrency: number; + callbacks: CallbackActions; }; export const deployArtifacts = async (artifacts: cxapi.CloudArtifact[], { concurrency, - deployStack, - buildAsset, - publishAsset, + callbacks, }: Options): Promise => { const graph = WorkGraph.fromCloudArtifacts(artifacts); console.log(graph.toString()); @@ -22,13 +24,13 @@ export const deployArtifacts = async (artifacts: cxapi.CloudArtifact[], { // Execute this function with as much parallelism as possible switch (x.type) { case WorkType.STACK_DEPLOY: - await deployStack(x.artifact as cxapi.CloudFormationStackArtifact); + await callbacks.deployStack(x); break; case WorkType.ASSET_BUILD: - await buildAsset(x); + await callbacks.buildAsset(x); break; case WorkType.ASSET_PUBLISH: - await publishAsset(x); + await callbacks.publishAsset(x); break; } }); diff --git a/packages/aws-cdk/test/deploy.test.ts b/packages/aws-cdk/test/deploy.test.ts index ce12012527174..de9eabfb729d6 100644 --- a/packages/aws-cdk/test/deploy.test.ts +++ b/packages/aws-cdk/test/deploy.test.ts @@ -23,23 +23,25 @@ const SLOW = 200; */ describe('DeployAssets', () => { const actionedAssets: string[] = []; - const deployStack = async ({ id, displayName, name }: Stack) => { - const errorMessage = displayName; - const timeout = Number(name) || 0; + const callbacks = { + deployStack: async (x: WorkNode) => { + const errorMessage = x.stack.displayName; + const timeout = Number(x.stack.name) || 0; - await sleep(timeout); + await sleep(timeout); - if (errorMessage) { - throw Error(errorMessage); - } + if (errorMessage) { + throw Error(errorMessage); + } - actionedAssets.push(id); - }; - const buildAsset = async({ id }: WorkNode) => { - actionedAssets.push(id); - }; - const publishAsset = async({ id }: WorkNode) => { - actionedAssets.push(id); + actionedAssets.push(x.id); + }, + buildAsset: async({ id }: WorkNode) => { + actionedAssets.push(id); + }, + publishAsset: async({ id }: WorkNode) => { + actionedAssets.push(id); + }, }; beforeEach(() => { @@ -229,7 +231,7 @@ describe('DeployAssets', () => { expected: ['c-build', 'c-publish', 'A', 'b-build', 'b-publish', 'B'], }, ])('Success - Concurrency: $concurrency - $scenario', async ({ concurrency, expected, toDeploy }) => { - await expect(deployArtifacts(toDeploy as unknown as Stack[], { concurrency, deployStack, buildAsset, publishAsset })).resolves.toBeUndefined(); + await expect(deployArtifacts(toDeploy, { concurrency, callbacks })).resolves.toBeUndefined(); expect(actionedAssets).toStrictEqual(expected); }); @@ -299,7 +301,7 @@ describe('DeployAssets', () => { }, ])('Failure - Concurrency: $concurrency - $scenario', async ({ concurrency, expectedError, toDeploy, expectedStacks }) => { // eslint-disable-next-line max-len - await expect(deployArtifacts(toDeploy as unknown as Stack[], { concurrency, deployStack, buildAsset, publishAsset })).rejects.toThrowError(expectedError); + await expect(deployArtifacts(toDeploy, { concurrency, callbacks })).rejects.toThrowError(expectedError); expect(actionedAssets).toStrictEqual(expectedStacks); }); From 13795c9b4173ecf750d4f3b418def7d9da2c7917 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Mon, 24 Apr 2023 17:16:35 -0400 Subject: [PATCH 083/120] worknode is now an abstract class --- .../nested-cloud-assembly-artifact.ts | 37 ++++++- .../lib/artifacts/tree-cloud-artifact.ts | 37 ++++++- packages/aws-cdk/lib/cdk-toolkit.ts | 24 ++--- packages/aws-cdk/lib/deploy.ts | 71 ++---------- packages/aws-cdk/lib/util/work-graph-types.ts | 80 ++++++++++++++ packages/aws-cdk/lib/util/work-graph.ts | 102 ++++++++---------- packages/aws-cdk/test/deploy.test.ts | 9 +- 7 files changed, 219 insertions(+), 141 deletions(-) create mode 100644 packages/aws-cdk/lib/util/work-graph-types.ts diff --git a/packages/aws-cdk-lib/cx-api/lib/artifacts/nested-cloud-assembly-artifact.ts b/packages/aws-cdk-lib/cx-api/lib/artifacts/nested-cloud-assembly-artifact.ts index c8e6a266ac337..3c7ab871bed7c 100644 --- a/packages/aws-cdk-lib/cx-api/lib/artifacts/nested-cloud-assembly-artifact.ts +++ b/packages/aws-cdk-lib/cx-api/lib/artifacts/nested-cloud-assembly-artifact.ts @@ -3,10 +3,33 @@ import * as cxschema from '../../../cloud-assembly-schema'; import { CloudArtifact } from '../cloud-artifact'; import type { CloudAssembly } from '../cloud-assembly'; +const NESTED_CLOUD_ASSEMBLY_SYM = Symbol.for('@aws-cdk/cx-api.NestedCloudAssemblyArtifact'); + /** * Asset manifest is a description of a set of assets which need to be built and published */ export class NestedCloudAssemblyArtifact extends CloudArtifact { + /** + * Checks if `art` is an instance of this class. + * + * Use this method instead of `instanceof` to properly detect `NestedCloudAssemblyArtifact` + * instances, even when the construct library is symlinked. + * + * Explanation: in JavaScript, multiple copies of the `cx-api` library on + * disk are seen as independent, completely different libraries. As a + * consequence, the class `NestedCloudAssemblyArtifact` in each copy of the `cx-api` library + * is seen as a different class, and an instance of one class will not test as + * `instanceof` the other class. `npm install` will not create installations + * like this, but users may manually symlink construct libraries together or + * use a monorepo tool: in those cases, multiple copies of the `cx-api` + * library can be accidentally installed, and `instanceof` will behave + * unpredictably. It is safest to avoid using `instanceof`, and using + * this type-testing method instead. + */ + public static isCloudFormationStackArtifact(art: any): art is NestedCloudAssemblyArtifact { + return art && typeof art === 'object' && art[NESTED_CLOUD_ASSEMBLY_SYM]; + } + /** * The relative directory name of the asset manifest */ @@ -40,4 +63,16 @@ export interface NestedCloudAssemblyArtifact { readonly nestedAssembly: CloudAssembly; // Declared in a different file -} \ No newline at end of file +} + +/** + * Mark all instances of 'NestedCloudAssemblyArtifact' + * + * Why not put this in the constructor? Because this is a class property, + * not an instance property. It applies to all instances of the class. + */ +Object.defineProperty(NestedCloudAssemblyArtifact.prototype, NESTED_CLOUD_ASSEMBLY_SYM, { + value: true, + enumerable: false, + writable: false, +}); \ No newline at end of file diff --git a/packages/aws-cdk-lib/cx-api/lib/artifacts/tree-cloud-artifact.ts b/packages/aws-cdk-lib/cx-api/lib/artifacts/tree-cloud-artifact.ts index 84f7c2474d94b..53226ce28c306 100644 --- a/packages/aws-cdk-lib/cx-api/lib/artifacts/tree-cloud-artifact.ts +++ b/packages/aws-cdk-lib/cx-api/lib/artifacts/tree-cloud-artifact.ts @@ -2,7 +2,30 @@ import * as cxschema from '../../../cloud-assembly-schema'; import { CloudArtifact } from '../cloud-artifact'; import { CloudAssembly } from '../cloud-assembly'; +const TREE_CLOUD_ARTIFACT_SYM = Symbol.for('@aws-cdk/cx-api.TreeCloudArtifact'); + export class TreeCloudArtifact extends CloudArtifact { + /** + * Checks if `art` is an instance of this class. + * + * Use this method instead of `instanceof` to properly detect `TreeCloudArtifact` + * instances, even when the construct library is symlinked. + * + * Explanation: in JavaScript, multiple copies of the `cx-api` library on + * disk are seen as independent, completely different libraries. As a + * consequence, the class `TreeCloudArtifact` in each copy of the `cx-api` library + * is seen as a different class, and an instance of one class will not test as + * `instanceof` the other class. `npm install` will not create installations + * like this, but users may manually symlink construct libraries together or + * use a monorepo tool: in those cases, multiple copies of the `cx-api` + * library can be accidentally installed, and `instanceof` will behave + * unpredictably. It is safest to avoid using `instanceof`, and using + * this type-testing method instead. + */ + public static isTreeCloudArtifact(art: any): art is TreeCloudArtifact { + return art && typeof art === 'object' && art[TREE_CLOUD_ARTIFACT_SYM]; + } + public readonly file: string; constructor(assembly: CloudAssembly, name: string, artifact: cxschema.ArtifactManifest) { @@ -14,4 +37,16 @@ export class TreeCloudArtifact extends CloudArtifact { } this.file = properties.file; } -} \ No newline at end of file +} + +/** + * Mark all instances of 'TreeCloudArtifact' + * + * Why not put this in the constructor? Because this is a class property, + * not an instance property. It applies to all instances of the class. + */ +Object.defineProperty(TreeCloudArtifact.prototype, TREE_CLOUD_ARTIFACT_SYM, { + value: true, + enumerable: false, + writable: false, +}); \ No newline at end of file diff --git a/packages/aws-cdk/lib/cdk-toolkit.ts b/packages/aws-cdk/lib/cdk-toolkit.ts index 46955403a2c88..d45b3b959fe89 100644 --- a/packages/aws-cdk/lib/cdk-toolkit.ts +++ b/packages/aws-cdk/lib/cdk-toolkit.ts @@ -23,8 +23,8 @@ import { deserializeStructure, serializeStructure } from './serialize'; import { Configuration, PROJECT_CONFIG } from './settings'; import { numberFromBool, partition } from './util'; import { validateSnsTopicArn } from './util/validate-notification-arn'; -import { WorkNode } from './util/work-graph'; import { environmentsFromDescriptors, globEnvironmentsFromStacks, looksLikeGlob } from '../lib/api/cxapp/environments'; +import { AssetBuildNode, AssetPublishNode, StackNode } from './util/work-graph-types'; export interface CdkToolkitProps { @@ -192,33 +192,33 @@ export class CdkToolkit { const stackOutputs: { [key: string]: any } = { }; const outputsFile = options.outputsFile; - const buildAsset = async (assetNode: WorkNode) => { - print('%s: building assets...\n', chalk.bold(assetNode.stack.displayName)); - await this.props.cloudFormation.buildAssets(assetNode.artifact as cxapi.AssetManifestArtifact, { - stack: assetNode.stack, + const buildAsset = async (assetNode: AssetBuildNode) => { + print('%s: building assets...\n', chalk.bold(assetNode.parentStack.displayName)); + await this.props.cloudFormation.buildAssets(assetNode.asset, { + stack: assetNode.parentStack, roleArn: options.roleArn, toolkitStackName: options.toolkitStackName, buildOptions: { parallel: options.assetParallelism, }, }); - print('\n%s: assets built\n', chalk.bold(assetNode.stack.displayName)); + print('\n%s: assets built\n', chalk.bold(assetNode.parentStack.displayName)); }; - const publishAsset = async (assetNode: WorkNode) => { - print('%s: publishing assets...\n', chalk.bold(assetNode.stack.displayName)); - await this.props.cloudFormation.publishAssets(assetNode.artifact as cxapi.AssetManifestArtifact, { - stack: assetNode.stack, + const publishAsset = async (assetNode: AssetPublishNode) => { + print('%s: publishing assets...\n', chalk.bold(assetNode.parentStack.displayName)); + await this.props.cloudFormation.publishAssets(assetNode.asset, { + stack: assetNode.parentStack, roleArn: options.roleArn, toolkitStackName: options.toolkitStackName, publishOptions: { parallel: options.assetParallelism, }, }); - print('\n%s: assets published\n', chalk.bold(assetNode.stack.displayName)); + print('\n%s: assets published\n', chalk.bold(assetNode.parentStack.displayName)); }; - const deployStack = async (assetNode: WorkNode) => { + const deployStack = async (assetNode: StackNode) => { const stack = assetNode.stack; if (stackCollection.stackCount !== 1) { highlight(stack.displayName); } diff --git a/packages/aws-cdk/lib/deploy.ts b/packages/aws-cdk/lib/deploy.ts index 5e06eeb254d76..adc4d8858d9d2 100644 --- a/packages/aws-cdk/lib/deploy.ts +++ b/packages/aws-cdk/lib/deploy.ts @@ -1,10 +1,12 @@ +/* eslint-disable no-console */ import * as cxapi from '@aws-cdk/cx-api'; -import { WorkGraph, WorkNode, WorkType } from './util/work-graph'; +import { WorkGraph } from './util/work-graph'; +import { AssetBuildNode, AssetPublishNode, StackNode, WorkNode, WorkType } from './util/work-graph-types'; interface CallbackActions { - deployStack: (assetNode: WorkNode) => Promise; - buildAsset: (assetNode: WorkNode) => Promise; - publishAsset: (assetNode: WorkNode) => Promise; + deployStack: (stackNode: StackNode) => Promise; + buildAsset: (assetNode: AssetBuildNode) => Promise; + publishAsset: (assetNode: AssetPublishNode) => Promise; } type Options = { @@ -24,13 +26,13 @@ export const deployArtifacts = async (artifacts: cxapi.CloudArtifact[], { // Execute this function with as much parallelism as possible switch (x.type) { case WorkType.STACK_DEPLOY: - await callbacks.deployStack(x); + await callbacks.deployStack(x as StackNode); break; case WorkType.ASSET_BUILD: - await callbacks.buildAsset(x); + await callbacks.buildAsset(x as AssetBuildNode); break; case WorkType.ASSET_PUBLISH: - await callbacks.publishAsset(x); + await callbacks.publishAsset(x as AssetPublishNode); break; } }); @@ -77,59 +79,4 @@ export const deployArtifacts = async (artifacts: cxapi.CloudArtifact[], { } }); } - - // const isStackUnblocked = (stack: cxapi.CloudFormationStackArtifact) => - // stack.dependencies - // .map(({ id }) => id) - // .filter((id) => !id.endsWith('.assets')) - // .every((id) => !deploymentStates[id] || deploymentStates[id] === 'completed'); // Dependency not selected or already finished - - // const hasAnyStackFailed = (states: Record) => Object.values(states).includes('failed'); - - // const deploymentErrors: Error[] = []; - - // const enqueueStackDeploys = () => { - // stacks.forEach(async (stack) => { - // if (deploymentStates[stack.id] === 'pending' && isStackUnblocked(stack)) { - // deploymentStates[stack.id] = 'queued'; - - // await queue.add(async () => { - // // Do not start new deployments if any has already failed - // if (hasAnyStackFailed(deploymentStates)) { - // deploymentStates[stack.id] = 'skipped'; - // return; - // } - - // deploymentStates[stack.id] = 'deploying'; - - // await deployStack(stack).catch((err) => { - // // By recording the failure immediately as the queued task exits, we prevent the next - // // queued task from starting (its 'hasAnyStackFailed' will return 'true'). - // deploymentStates[stack.id] = 'failed'; - // throw err; - // }); - - // deploymentStates[stack.id] = 'completed'; - // enqueueStackDeploys(); - // }).catch((err) => { - // deploymentStates[stack.id] = 'failed'; - // deploymentErrors.push(err); - // }); - // } - // }); - // }; - - // enqueueStackDeploys(); - - // await queue.onIdle(); - - // if (deploymentErrors.length) { - // throw Error(`Stack Deployments Failed: ${deploymentErrors}`); - // } - - // // We shouldn't be able to get here, but check it anyway - // const neverUnblocked = Object.entries(deploymentStates).filter(([_, s]) => s === 'pending').map(([n, _]) => n); - // if (neverUnblocked.length > 0) { - // throw new Error(`The following stacks never became unblocked: ${neverUnblocked.join(', ')}. Please report this at https://github.com/aws/aws-cdk/issues`); - // } }; diff --git a/packages/aws-cdk/lib/util/work-graph-types.ts b/packages/aws-cdk/lib/util/work-graph-types.ts new file mode 100644 index 0000000000000..6c1dbb4275f48 --- /dev/null +++ b/packages/aws-cdk/lib/util/work-graph-types.ts @@ -0,0 +1,80 @@ +import * as cxapi from '@aws-cdk/cx-api'; + +export enum DeploymentState { + PENDING = 'pending', + QUEUED = 'queued', + DEPLOYING = 'deploying', + COMPLETED = 'completed', + FAILED = 'failed', + SKIPPED = 'skipped', +}; + +interface WorkNodeOptions { + readonly id: string; + readonly dependencies: string[]; +} + +export enum WorkType { + STACK_DEPLOY = 'stack', + ASSET_BUILD = 'asset-build', + ASSET_PUBLISH = 'asset-publish', +} + +export abstract class WorkNode { + public readonly id: string; + public readonly dependencies: string[]; + public deploymentState: DeploymentState; + abstract type: WorkType; + + constructor(options: WorkNodeOptions) { + this.id = options.id; + this.dependencies = options.dependencies; + this.deploymentState = DeploymentState.PENDING; + } +} + +export interface StackNodeOptions extends WorkNodeOptions { + readonly stack: cxapi.CloudFormationStackArtifact; +} + +export class StackNode extends WorkNode { + public readonly stack: cxapi.CloudFormationStackArtifact; + public readonly type = WorkType.STACK_DEPLOY; + + constructor(options: StackNodeOptions) { + super(options); + this.stack = options.stack; + } +} + +interface AssetOptions extends WorkNodeOptions { + readonly asset: cxapi.AssetManifestArtifact; + readonly parentStack: cxapi.CloudFormationStackArtifact; +} +export interface AssetBuildNodeOptions extends AssetOptions {} + +export class AssetBuildNode extends WorkNode { + public readonly asset: cxapi.AssetManifestArtifact; + public readonly parentStack: cxapi.CloudFormationStackArtifact; + public readonly type = WorkType.ASSET_BUILD; + + constructor(options: AssetBuildNodeOptions) { + super(options); + this.asset = options.asset; + this.parentStack = options.parentStack; + } +} + +export class AssetPublishNode extends WorkNode { + public readonly asset: cxapi.AssetManifestArtifact; + public readonly parentStack: cxapi.CloudFormationStackArtifact; + public readonly type = WorkType.ASSET_PUBLISH; + + constructor(options: AssetBuildNodeOptions) { + super(options); + this.asset = options.asset; + this.parentStack = options.parentStack; + } +} + +export interface PartialAssetNodeOptions extends Omit {} diff --git a/packages/aws-cdk/lib/util/work-graph.ts b/packages/aws-cdk/lib/util/work-graph.ts index b540cfbfca1db..fc5833119ed8d 100644 --- a/packages/aws-cdk/lib/util/work-graph.ts +++ b/packages/aws-cdk/lib/util/work-graph.ts @@ -1,88 +1,70 @@ /* eslint-disable no-console */ import * as cxapi from '@aws-cdk/cx-api'; - -export enum WorkType { - STACK_DEPLOY = 'stack-deploy', - ASSET_BUILD = 'asset-build', - ASSET_PUBLISH = 'asset-publish', -}; - -export enum DeploymentState { - PENDING = 'pending', - QUEUED = 'queued', - DEPLOYING = 'deploying', - COMPLETED = 'completed', - FAILED = 'failed', - SKIPPED = 'skipped', -}; - -export interface WorkNode { - readonly id: string; - readonly type: WorkType; - readonly dependencies: string[]; - readonly artifact: cxapi.CloudArtifact; - readonly stack: cxapi.CloudFormationStackArtifact; - deploymentState: DeploymentState; -} +import { WorkNode, DeploymentState, AssetBuildNode, AssetPublishNode, PartialAssetNodeOptions, StackNode } from './work-graph-types'; export class WorkGraph { public static fromCloudArtifacts(artifacts: cxapi.CloudArtifact[]) { const graph = new WorkGraph(); // Associated stack will be lazily added for Assets - const graphNodes: Omit[] = []; - const associatedStacks: Record = {}; + const graphNodes: WorkNode[] = []; + const partialAssetNodes: PartialAssetNodeOptions[] = []; + const parentStacks: Record = {}; for (const artifact of artifacts) { if (cxapi.AssetManifestArtifact.isAssetManifestArtifact(artifact)) { - const buildNode = { - id: `${artifact.id}-build`, - type: WorkType.ASSET_BUILD, + partialAssetNodes.push({ + id: `${artifact.id}`, dependencies: getDepIds(artifact.dependencies), - artifact, - deploymentState: DeploymentState.PENDING, - }; - graphNodes.push(buildNode); - graphNodes.push({ - id: `${artifact.id}-publish`, - type: WorkType.ASSET_PUBLISH, - dependencies: [buildNode.id], - artifact, - deploymentState: DeploymentState.PENDING, + asset: artifact, }); } else if (cxapi.CloudFormationStackArtifact.isCloudFormationStackArtifact(artifact)) { - graphNodes.push({ + graphNodes.push(new StackNode({ id: artifact.id, - type: WorkType.STACK_DEPLOY, dependencies: getDepIds(artifact.dependencies), - artifact, - deploymentState: DeploymentState.PENDING, - }); - updateAssociatedStacks(artifact); + stack: artifact, + })); + updateParentStacks(artifact); + } else if (cxapi.TreeCloudArtifact.isTreeCloudArtifact(artifact)) { + // ignore tree artifacts + continue; + } else if (cxapi.NestedCloudAssemblyArtifact.isNestedCloudAssemblyArtifact(artifact)) { + // TODO: this + continue; } } - // post-process stacks associated with each nodes because we only know - // we have this information after all artifacts have been processed. - const finalGraphNodes: WorkNode[] = []; - for (const node of graphNodes) { - const stack = associatedStacks[node.id]; + // post-process parent stacks of each asset because we only know + // this information after all artifacts have been processed. + for (const node of partialAssetNodes) { + const stack = parentStacks[node.id]; if (!stack) { throw new Error(`No stack associated with ${node.id} artifact. Something in the source code or asset manifest is wrong.`); } - finalGraphNodes.push({ - ...node, - stack, - }); + graphNodes.push(...[ + new AssetBuildNode({ + id: `${node.id}-build`, + dependencies: node.dependencies, + asset: node.asset, + parentStack: stack, + }), + new AssetPublishNode({ + id: `${node.id}-publish`, + dependencies: [`${node.id}-build`], // only depend on the build asset step + asset: node.asset, + parentStack: stack, + }), + ]); } - graph.addNodes(...finalGraphNodes); + graph.addNodes(...graphNodes); // Ensure all dependencies actually exist. This protects against scenarios such as the following: // StackA depends on StackB, but StackB is not selected to deploy. The dependency is redundant // and will be dropped. + // This assumes the manifest comes uncorrupted so we will not fail if a dependency is not found. for (const node of Object.values(graph.nodes)) { - if (node.type !== WorkType.STACK_DEPLOY) { continue; } + if (node.type !== 'stack') { continue; } const removeDeps = []; for (const dep of node.dependencies) { if (graph.nodes[dep] === undefined) { @@ -97,13 +79,11 @@ export class WorkGraph { return graph; - function updateAssociatedStacks(stack: cxapi.CloudFormationStackArtifact) { + function updateParentStacks(stack: cxapi.CloudFormationStackArtifact) { const assetArtifacts = stack.dependencies.filter(cxapi.AssetManifestArtifact.isAssetManifestArtifact); for (const art of assetArtifacts) { - associatedStacks[`${art.id}-publish`] = stack; - associatedStacks[`${art.id}-build`] = stack; + parentStacks[art.id] = stack; } - associatedStacks[stack.id] = stack; } function getDepIds(deps: cxapi.CloudArtifact[]): string[] { @@ -202,3 +182,5 @@ export class WorkGraph { } } } +export { WorkNode }; + diff --git a/packages/aws-cdk/test/deploy.test.ts b/packages/aws-cdk/test/deploy.test.ts index de9eabfb729d6..f0242678b3132 100644 --- a/packages/aws-cdk/test/deploy.test.ts +++ b/packages/aws-cdk/test/deploy.test.ts @@ -1,7 +1,6 @@ -/* eslint-disable import/order */ import * as cxapi from '@aws-cdk/cx-api'; import { deployArtifacts } from '../lib/deploy'; -import { WorkNode } from '../lib/util/work-graph'; +import { AssetBuildNode, AssetPublishNode, StackNode } from '../lib/util/work-graph-types'; const ASSET_MANIFEST_ARTIFACT_SYM = Symbol.for('@aws-cdk/cx-api.AssetManifestArtifact'); const CLOUDFORMATION_STACK_ARTIFACT_SYM = Symbol.for('@aws-cdk/cx-api.CloudFormationStackArtifact'); @@ -24,7 +23,7 @@ const SLOW = 200; describe('DeployAssets', () => { const actionedAssets: string[] = []; const callbacks = { - deployStack: async (x: WorkNode) => { + deployStack: async (x: StackNode) => { const errorMessage = x.stack.displayName; const timeout = Number(x.stack.name) || 0; @@ -36,10 +35,10 @@ describe('DeployAssets', () => { actionedAssets.push(x.id); }, - buildAsset: async({ id }: WorkNode) => { + buildAsset: async({ id }: AssetBuildNode) => { actionedAssets.push(id); }, - publishAsset: async({ id }: WorkNode) => { + publishAsset: async({ id }: AssetPublishNode) => { actionedAssets.push(id); }, }; From 01fc565524da8f0c147efbe3e1763d9694aa300d Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 26 Apr 2023 21:24:35 -0400 Subject: [PATCH 084/120] add disable prebuild to workgraph --- .../lib/api/cloudformation-deployments.ts | 9 +- packages/aws-cdk/lib/api/toolkit-info.ts | 2 + packages/aws-cdk/lib/cdk-toolkit.ts | 7 +- packages/aws-cdk/lib/deploy.ts | 4 +- packages/aws-cdk/lib/util/work-graph.ts | 37 ++++---- packages/aws-cdk/test/cdk-toolkit.test.ts | 91 +++++++------------ packages/aws-cdk/test/deploy.test.ts | 16 +++- 7 files changed, 85 insertions(+), 81 deletions(-) diff --git a/packages/aws-cdk/lib/api/cloudformation-deployments.ts b/packages/aws-cdk/lib/api/cloudformation-deployments.ts index 333247c6b2ba0..bcc31d8944cd6 100644 --- a/packages/aws-cdk/lib/api/cloudformation-deployments.ts +++ b/packages/aws-cdk/lib/api/cloudformation-deployments.ts @@ -519,11 +519,13 @@ export class CloudFormationDeployments { } private async prepareAndValidateAssets(asset: cxapi.AssetManifestArtifact, options: AssetOptions) { + console.log('prepareandvalidateassets'); const { stackSdk, resolvedEnvironment } = await this.prepareSdkFor(options.stack, options.roleArn); + console.log('stacksdk'); const toolkitInfo = await ToolkitInfo.lookup(resolvedEnvironment, stackSdk, options.toolkitStackName); - + console.log(toolkitInfo); const stackEnv = await this.sdkProvider.resolveEnvironment(options.stack.environment); - + console.log(stackEnv); await this.validateBootstrapStackVersion( options.stack.stackName, asset.requiresBootstrapStackVersion, @@ -536,11 +538,14 @@ export class CloudFormationDeployments { } public async buildAssets(asset: cxapi.AssetManifestArtifact, options: BuildStackAssetsOptions) { + console.log('build assets'); + console.log('build parallelism', options.buildOptions?.parallel); const { manifest, stackEnv } = await this.prepareAndValidateAssets(asset, options); await buildAssets(manifest, this.sdkProvider, stackEnv, options.buildOptions); } public async publishAssets(asset: cxapi.AssetManifestArtifact, options: PublishStackAssetsOptions) { + console.log('publish parallelism', options.publishOptions?.parallel); const { manifest, stackEnv } = await this.prepareAndValidateAssets(asset, options); await publishAssets(manifest, this.sdkProvider, stackEnv, options.publishOptions); } diff --git a/packages/aws-cdk/lib/api/toolkit-info.ts b/packages/aws-cdk/lib/api/toolkit-info.ts index f0f66753843ee..2999b996b9d60 100644 --- a/packages/aws-cdk/lib/api/toolkit-info.ts +++ b/packages/aws-cdk/lib/api/toolkit-info.ts @@ -42,7 +42,9 @@ export abstract class ToolkitInfo { public static async lookup(environment: cxapi.Environment, sdk: ISDK, stackName: string | undefined): Promise { const cfn = sdk.cloudFormation(); + console.log('here1'); const stack = await stabilizeStack(cfn, stackName ?? DEFAULT_TOOLKIT_STACK_NAME); + console.log('here2'); if (!stack) { debug('The environment %s doesn\'t have the CDK toolkit stack (%s) installed. Use %s to setup your environment for use with the toolkit.', environment.name, stackName, chalk.blue(`cdk bootstrap "${environment.name}"`)); diff --git a/packages/aws-cdk/lib/cdk-toolkit.ts b/packages/aws-cdk/lib/cdk-toolkit.ts index 5b3a3b6c5e785..b14237d309667 100644 --- a/packages/aws-cdk/lib/cdk-toolkit.ts +++ b/packages/aws-cdk/lib/cdk-toolkit.ts @@ -194,6 +194,7 @@ export class CdkToolkit { const buildAsset = async (assetNode: AssetBuildNode) => { print('%s: building assets...\n', chalk.bold(assetNode.parentStack.displayName)); + console.log('%s: building assets...\n', chalk.bold(assetNode.parentStack.displayName)); await this.props.cloudFormation.buildAssets(assetNode.asset, { stack: assetNode.parentStack, roleArn: options.roleArn, @@ -203,10 +204,12 @@ export class CdkToolkit { }, }); print('\n%s: assets built\n', chalk.bold(assetNode.parentStack.displayName)); + console.log('\n%s: assets built\n', chalk.bold(assetNode.parentStack.displayName)); }; const publishAsset = async (assetNode: AssetPublishNode) => { print('%s: publishing assets...\n', chalk.bold(assetNode.parentStack.displayName)); + console.log('%s: publishing assets...\n', chalk.bold(assetNode.parentStack.displayName)); await this.props.cloudFormation.publishAssets(assetNode.asset, { stack: assetNode.parentStack, roleArn: options.roleArn, @@ -216,6 +219,7 @@ export class CdkToolkit { }, }); print('\n%s: assets published\n', chalk.bold(assetNode.parentStack.displayName)); + console.log('\n%s: assets published\n', chalk.bold(assetNode.parentStack.displayName)); }; const deployStack = async (assetNode: StackNode) => { @@ -297,7 +301,7 @@ export class CdkToolkit { rollback: options.rollback, hotswap: options.hotswap, extraUserAgent: options.extraUserAgent, - buildAssets: assetBuildTime !== AssetBuildTime.ALL_BEFORE_DEPLOY, + buildAssets: assetBuildTime !== AssetBuildTime.ALL_BEFORE_DEPLOY, // TODO: remove this assetParallelism: options.assetParallelism, }); @@ -359,6 +363,7 @@ export class CdkToolkit { buildAsset, publishAsset, }, + prebuildAssets: assetBuildTime !== AssetBuildTime.ALL_BEFORE_DEPLOY, }); } catch (e) { error('\n ❌ Deployment failed: %s', e); diff --git a/packages/aws-cdk/lib/deploy.ts b/packages/aws-cdk/lib/deploy.ts index adc4d8858d9d2..0609487126a28 100644 --- a/packages/aws-cdk/lib/deploy.ts +++ b/packages/aws-cdk/lib/deploy.ts @@ -12,13 +12,15 @@ interface CallbackActions { type Options = { concurrency: number; callbacks: CallbackActions; + prebuildAssets?: boolean; }; export const deployArtifacts = async (artifacts: cxapi.CloudArtifact[], { concurrency, callbacks, + prebuildAssets = true, }: Options): Promise => { - const graph = WorkGraph.fromCloudArtifacts(artifacts); + const graph = WorkGraph.fromCloudArtifacts(artifacts, prebuildAssets); console.log(graph.toString()); await forAllArtifacts(concurrency, async (x: WorkNode) => { diff --git a/packages/aws-cdk/lib/util/work-graph.ts b/packages/aws-cdk/lib/util/work-graph.ts index fc5833119ed8d..50113881a09a6 100644 --- a/packages/aws-cdk/lib/util/work-graph.ts +++ b/packages/aws-cdk/lib/util/work-graph.ts @@ -3,11 +3,10 @@ import * as cxapi from '@aws-cdk/cx-api'; import { WorkNode, DeploymentState, AssetBuildNode, AssetPublishNode, PartialAssetNodeOptions, StackNode } from './work-graph-types'; export class WorkGraph { - public static fromCloudArtifacts(artifacts: cxapi.CloudArtifact[]) { + public static fromCloudArtifacts(artifacts: cxapi.CloudArtifact[], prebuildAssets: boolean) { const graph = new WorkGraph(); // Associated stack will be lazily added for Assets - const graphNodes: WorkNode[] = []; const partialAssetNodes: PartialAssetNodeOptions[] = []; const parentStacks: Record = {}; @@ -19,7 +18,7 @@ export class WorkGraph { asset: artifact, }); } else if (cxapi.CloudFormationStackArtifact.isCloudFormationStackArtifact(artifact)) { - graphNodes.push(new StackNode({ + graph.addNodes(new StackNode({ id: artifact.id, dependencies: getDepIds(artifact.dependencies), stack: artifact, @@ -36,29 +35,28 @@ export class WorkGraph { // post-process parent stacks of each asset because we only know // this information after all artifacts have been processed. - for (const node of partialAssetNodes) { - const stack = parentStacks[node.id]; + for (const assetNode of partialAssetNodes) { + const stack = parentStacks[assetNode.id]; if (!stack) { - throw new Error(`No stack associated with ${node.id} artifact. Something in the source code or asset manifest is wrong.`); + throw new Error(`No stack associated with ${assetNode.id} artifact. Something in the source code or asset manifest is wrong.`); } - graphNodes.push(...[ + graph.addNodes(...[ new AssetBuildNode({ - id: `${node.id}-build`, - dependencies: node.dependencies, - asset: node.asset, + id: `${assetNode.id}-build`, + // If we disable prebuild, then assets inherit dependencies from their parent stack + dependencies: assetNode.dependencies.concat(!prebuildAssets ? onlyStackDeps(graph.getNode(stack.id).dependencies) : []), + asset: assetNode.asset, parentStack: stack, }), new AssetPublishNode({ - id: `${node.id}-publish`, - dependencies: [`${node.id}-build`], // only depend on the build asset step - asset: node.asset, + id: `${assetNode.id}-publish`, + dependencies: [`${assetNode.id}-build`], // only depend on the build asset step + asset: assetNode.asset, parentStack: stack, }), ]); } - graph.addNodes(...graphNodes); - // Ensure all dependencies actually exist. This protects against scenarios such as the following: // StackA depends on StackB, but StackB is not selected to deploy. The dependency is redundant // and will be dropped. @@ -98,6 +96,10 @@ export class WorkGraph { } return ids; } + + function onlyStackDeps(ids: string[]): string[] { + return ids.filter((i) => !i.endsWith('publish')); + } } private readonly nodes: Record; @@ -181,6 +183,9 @@ export class WorkGraph { } } } + + private getNode(id: string) { + return this.nodes[id]; + } } -export { WorkNode }; diff --git a/packages/aws-cdk/test/cdk-toolkit.test.ts b/packages/aws-cdk/test/cdk-toolkit.test.ts index 5b7999f00c9ea..5a2c9f49d2b95 100644 --- a/packages/aws-cdk/test/cdk-toolkit.test.ts +++ b/packages/aws-cdk/test/cdk-toolkit.test.ts @@ -59,14 +59,14 @@ import * as path from 'path'; import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import { Manifest } from '@aws-cdk/cloud-assembly-schema'; import * as cxapi from '@aws-cdk/cx-api'; -import { instanceMockFrom, MockCloudExecutable, TestStackArtifact, withMocked } from './util'; +import { instanceMockFrom, MockCloudExecutable, TestStackArtifact } from './util'; import { MockSdkProvider } from './util/mock-sdk'; import { Bootstrapper } from '../lib/api/bootstrap'; import { CloudFormationDeployments, DeployStackOptions, DestroyStackOptions } from '../lib/api/cloudformation-deployments'; import { DeployStackResult } from '../lib/api/deploy-stack'; import { HotswapMode } from '../lib/api/hotswap/common'; import { Template } from '../lib/api/util/cloudformation'; -import { CdkToolkit, Tag, AssetBuildTime } from '../lib/cdk-toolkit'; +import { CdkToolkit, Tag } from '../lib/cdk-toolkit'; import { RequireApproval } from '../lib/diff'; import { flatten } from '../lib/util'; @@ -585,63 +585,36 @@ describe('deploy', () => { expect(mockSynthesize).not.toHaveBeenCalled(); }); - test('can disable asset parallelism', async () => { - // GIVEN - cloudExecutable = new MockCloudExecutable({ - stacks: [MockStack.MOCK_STACK_WITH_ASSET], - }); - const fakeCloudFormation = new FakeCloudFormation({}); - - const toolkit = new CdkToolkit({ - cloudExecutable, - configuration: cloudExecutable.configuration, - sdkProvider: cloudExecutable.sdkProvider, - cloudFormation: fakeCloudFormation, - }); - - // WHEN - // Not the best test but following this through to the asset publishing library fails - await withMocked(fakeCloudFormation, 'buildAssets', async (mockBuildStackAssets) => { - await toolkit.deploy({ - selector: { patterns: ['Test-Stack-Asset'] }, - assetParallelism: false, - hotswap: HotswapMode.FULL_DEPLOYMENT, - }); - - expect(mockBuildStackAssets).toHaveBeenCalledWith(expect.objectContaining({ - buildOptions: expect.objectContaining({ - parallel: false, - }), - })); - }); - }); - - test('can disable asset prebuild', async () => { - // GIVEN - cloudExecutable = new MockCloudExecutable({ - stacks: [MockStack.MOCK_STACK_WITH_ASSET], - }); - const fakeCloudFormation = new FakeCloudFormation({}); - - const toolkit = new CdkToolkit({ - cloudExecutable, - configuration: cloudExecutable.configuration, - sdkProvider: cloudExecutable.sdkProvider, - cloudFormation: fakeCloudFormation, - }); - - // WHEN - // Not the best test but following this through to the asset publishing library fails - await withMocked(fakeCloudFormation, 'buildAssets', async (mockBuildStackAssets) => { - await toolkit.deploy({ - selector: { patterns: ['Test-Stack-Asset'] }, - assetBuildTime: AssetBuildTime.JUST_IN_TIME, - hotswap: HotswapMode.FULL_DEPLOYMENT, - }); - - expect(mockBuildStackAssets).not.toHaveBeenCalled(); - }); - }); + // test('can disable asset parallelism', async () => { + // // GIVEN + // cloudExecutable = new MockCloudExecutable({ + // stacks: [MockStack.MOCK_STACK_WITH_ASSET], + // }); + // const fakeCloudFormation = new FakeCloudFormation({}); + + // const toolkit = new CdkToolkit({ + // cloudExecutable, + // configuration: cloudExecutable.configuration, + // sdkProvider: cloudExecutable.sdkProvider, + // cloudFormation: fakeCloudFormation, + // }); + + // // WHEN + // // Not the best test but following this through to the asset publishing library fails + // await withMocked(fakeCloudFormation, 'buildAssets', async (mockBuildAssets) => { + // await toolkit.deploy({ + // selector: { patterns: ['Test-Stack-Asset'] }, + // assetParallelism: false, + // hotswap: HotswapMode.FULL_DEPLOYMENT, + // }); + + // expect(mockBuildAssets).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({ + // buildOptions: expect.objectContaining({ + // parallel: false, + // }), + // })); + // }); + // }); }); }); diff --git a/packages/aws-cdk/test/deploy.test.ts b/packages/aws-cdk/test/deploy.test.ts index f0242678b3132..01fa2d54ae954 100644 --- a/packages/aws-cdk/test/deploy.test.ts +++ b/packages/aws-cdk/test/deploy.test.ts @@ -230,7 +230,7 @@ describe('DeployAssets', () => { expected: ['c-build', 'c-publish', 'A', 'b-build', 'b-publish', 'B'], }, ])('Success - Concurrency: $concurrency - $scenario', async ({ concurrency, expected, toDeploy }) => { - await expect(deployArtifacts(toDeploy, { concurrency, callbacks })).resolves.toBeUndefined(); + await expect(deployArtifacts(toDeploy, { concurrency, callbacks, prebuildAssets: true })).resolves.toBeUndefined(); expect(actionedAssets).toStrictEqual(expected); }); @@ -300,10 +300,22 @@ describe('DeployAssets', () => { }, ])('Failure - Concurrency: $concurrency - $scenario', async ({ concurrency, expectedError, toDeploy, expectedStacks }) => { // eslint-disable-next-line max-len - await expect(deployArtifacts(toDeploy, { concurrency, callbacks })).rejects.toThrowError(expectedError); + await expect(deployArtifacts(toDeploy, { concurrency, callbacks, prebuildAssets: true })).rejects.toThrowError(expectedError); expect(actionedAssets).toStrictEqual(expectedStacks); }); + + test('Can disable prebuild assets', async () => { + const toDeploy = createArtifacts([ + { id: 'A', type: 'stack', name: SLOW }, + { id: 'B', type: 'stack', stackDependencies: ['A'], assetDependencies: ['b'] }, + { id: 'b', type: 'asset' }, + ]); + await expect(deployArtifacts(toDeploy, { concurrency: 2, callbacks, prebuildAssets: false })).resolves.toBeUndefined(); + + // asset build waits for slow stack A deployment + expect(actionedAssets).toStrictEqual(['A', 'b-build', 'b-publish', 'B']); + }); }); interface TestArtifact { From 075ed8bd439ec7e56f161d0938503a8ce3845984 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 27 Apr 2023 10:40:40 -0400 Subject: [PATCH 085/120] typo --- .../cx-api/lib/artifacts/nested-cloud-assembly-artifact.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/cx-api/lib/artifacts/nested-cloud-assembly-artifact.ts b/packages/aws-cdk-lib/cx-api/lib/artifacts/nested-cloud-assembly-artifact.ts index 3c7ab871bed7c..421caafb9acb2 100644 --- a/packages/aws-cdk-lib/cx-api/lib/artifacts/nested-cloud-assembly-artifact.ts +++ b/packages/aws-cdk-lib/cx-api/lib/artifacts/nested-cloud-assembly-artifact.ts @@ -26,7 +26,7 @@ export class NestedCloudAssemblyArtifact extends CloudArtifact { * unpredictably. It is safest to avoid using `instanceof`, and using * this type-testing method instead. */ - public static isCloudFormationStackArtifact(art: any): art is NestedCloudAssemblyArtifact { + public static isNestedCloudAssemblyArtifact(art: any): art is NestedCloudAssemblyArtifact { return art && typeof art === 'object' && art[NESTED_CLOUD_ASSEMBLY_SYM]; } From c4af16ed59f9ae695a1385fa89387b94461df9cb Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 27 Apr 2023 14:51:42 -0400 Subject: [PATCH 086/120] nested assemblies --- .../lib/api/cloudformation-deployments.ts | 1 + packages/aws-cdk/lib/util/work-graph.ts | 9 ++- packages/aws-cdk/test/deploy.test.ts | 65 +++++++++++++++---- .../aws-cdk/test/stage-manifest/manifest.json | 24 +++++++ 4 files changed, 83 insertions(+), 16 deletions(-) create mode 100644 packages/aws-cdk/test/stage-manifest/manifest.json diff --git a/packages/aws-cdk/lib/api/cloudformation-deployments.ts b/packages/aws-cdk/lib/api/cloudformation-deployments.ts index bcc31d8944cd6..9576aed8e9d44 100644 --- a/packages/aws-cdk/lib/api/cloudformation-deployments.ts +++ b/packages/aws-cdk/lib/api/cloudformation-deployments.ts @@ -94,6 +94,7 @@ export async function prepareSdkWithLookupRoleFor( // only print out the warnings if the lookupRole exists AND there is a required // bootstrap version, otherwise the warnings will print `undefined` if (stack.lookupRole && stack.lookupRole.requiresBootstrapStackVersion) { + warning('wtf'); warning(warningMessage); warning(upgradeMessage); } diff --git a/packages/aws-cdk/lib/util/work-graph.ts b/packages/aws-cdk/lib/util/work-graph.ts index 50113881a09a6..b7b9fc48f2744 100644 --- a/packages/aws-cdk/lib/util/work-graph.ts +++ b/packages/aws-cdk/lib/util/work-graph.ts @@ -28,8 +28,11 @@ export class WorkGraph { // ignore tree artifacts continue; } else if (cxapi.NestedCloudAssemblyArtifact.isNestedCloudAssemblyArtifact(artifact)) { - // TODO: this - continue; + console.log(artifact.directoryName); + const assembly = new cxapi.CloudAssembly(artifact.directoryName); + console.log(assembly.directory); + const nestedGraph = WorkGraph.fromCloudArtifacts(assembly.artifacts, prebuildAssets); + graph.addNodes(...Object.values(nestedGraph.nodes)); } } @@ -106,7 +109,7 @@ export class WorkGraph { private readonly readyPool: Array = []; public error?: Error; - private constructor(nodes: Record = {}) { + public constructor(nodes: Record = {}) { this.nodes = nodes; } diff --git a/packages/aws-cdk/test/deploy.test.ts b/packages/aws-cdk/test/deploy.test.ts index 01fa2d54ae954..05cd54d1011bf 100644 --- a/packages/aws-cdk/test/deploy.test.ts +++ b/packages/aws-cdk/test/deploy.test.ts @@ -1,13 +1,18 @@ import * as cxapi from '@aws-cdk/cx-api'; import { deployArtifacts } from '../lib/deploy'; import { AssetBuildNode, AssetPublishNode, StackNode } from '../lib/util/work-graph-types'; +import path = require('path'); const ASSET_MANIFEST_ARTIFACT_SYM = Symbol.for('@aws-cdk/cx-api.AssetManifestArtifact'); const CLOUDFORMATION_STACK_ARTIFACT_SYM = Symbol.for('@aws-cdk/cx-api.CloudFormationStackArtifact'); +const TREE_CLOUD_ARTIFACT_SYM = Symbol.for('@aws-cdk/cx-api.TreeCloudArtifact'); +const NESTED_ASSEMBLY_ARTIFACT = Symbol.for('@aws-cdk/cx-api.NestedCloudAssemblyArtifact'); type Artifact = cxapi.CloudArtifact; type Stack = cxapi.CloudFormationStackArtifact; type Asset = cxapi.AssetManifestArtifact; +type Tree = cxapi.TreeCloudArtifact; +type Nested = cxapi.NestedCloudAssemblyArtifact; const sleep = async (duration: number) => new Promise((resolve) => setTimeout(() => resolve(), duration)); @@ -29,7 +34,8 @@ describe('DeployAssets', () => { await sleep(timeout); - if (errorMessage) { + // Special case for testing NestedCloudAssemblyArtifacts + if (errorMessage && !errorMessage.startsWith('Nested')) { throw Error(errorMessage); } @@ -305,7 +311,7 @@ describe('DeployAssets', () => { expect(actionedAssets).toStrictEqual(expectedStacks); }); - test('Can disable prebuild assets', async () => { + test('can disable prebuild assets', async () => { const toDeploy = createArtifacts([ { id: 'A', type: 'stack', name: SLOW }, { id: 'B', type: 'stack', stackDependencies: ['A'], assetDependencies: ['b'] }, @@ -316,13 +322,31 @@ describe('DeployAssets', () => { // asset build waits for slow stack A deployment expect(actionedAssets).toStrictEqual(['A', 'b-build', 'b-publish', 'B']); }); + + test('tree metadata is ignored', async () => { + const toDeploy = createArtifacts([ + { id: '$', type: 'tree' }, + ]); + await expect(deployArtifacts(toDeploy, { concurrency: 1, callbacks })).resolves.toBeUndefined(); + + expect(actionedAssets).toStrictEqual([]); + }); + + test('nested assembly is accepted', async () => { + const toDeploy = createArtifacts([ + { id: 'Stage', type: 'nested' }, + ]); + await expect(deployArtifacts(toDeploy, { concurrency: 1, callbacks })).resolves.toBeUndefined(); + + expect(actionedAssets).toStrictEqual(['StageA.assets-build', 'StageA.assets-publish', 'NestedStageA']); + }); }); interface TestArtifact { stackDependencies?: string[]; assetDependencies?: string[]; id: string; - type: 'stack' | 'asset'; + type: 'stack' | 'asset' | 'tree'| 'nested'; name?: number; displayName?: string; } @@ -337,16 +361,31 @@ function createArtifact(artifact: TestArtifact): Artifact { name: artifact.name, displayName: artifact.displayName, }; - if (artifact.type === 'stack') { - return { - ...art, - [CLOUDFORMATION_STACK_ARTIFACT_SYM]: true, - } as unknown as Stack; - } else { - return { - ...art, - [ASSET_MANIFEST_ARTIFACT_SYM]: true, - } as unknown as Asset; + switch (artifact.type) { + case 'stack': + return { + ...art, + [CLOUDFORMATION_STACK_ARTIFACT_SYM]: true, + } as unknown as Stack; + case 'asset': + return { + ...art, + [ASSET_MANIFEST_ARTIFACT_SYM]: true, + } as unknown as Asset; + case 'tree': + return { + type: 'cdk:tree', + properties: { + file: 'tree.json', + }, + [TREE_CLOUD_ARTIFACT_SYM]: true, + } as unknown as Tree; + case 'nested': + return { + type: 'cdk:cloud-assembly', + directoryName: path.join(__dirname, 'stage-manifest'), + [NESTED_ASSEMBLY_ARTIFACT]: true, + } as unknown as Nested; } } diff --git a/packages/aws-cdk/test/stage-manifest/manifest.json b/packages/aws-cdk/test/stage-manifest/manifest.json new file mode 100644 index 0000000000000..4eb5c5743979e --- /dev/null +++ b/packages/aws-cdk/test/stage-manifest/manifest.json @@ -0,0 +1,24 @@ +{ + "version": "31.0.0", + "artifacts": { + "StageA.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "StageA.assets.json" + } + }, + "NestedStageA": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "StageA.template.json", + "additionalDependencies": [ + "StageA.assets" + ] + }, + "dependencies": [ + "StageA.assets" + ] + } + } +} \ No newline at end of file From 925812e3a3082499eb022b5a037ca6e5b9a99f8f Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 3 May 2023 09:32:01 -0400 Subject: [PATCH 087/120] wip of app-staging stuff --- .../lib/app-staging-synthesizer.ts | 3 +- .../lib/per-env-staging-factory.ts | 2 +- .../lib/staging-stack.ts | 2 +- .../test/app-staging-synthesizer.test.ts | 2 +- .../test/bootstrap-roles.test.ts | 3 +- .../test/evaluate-cfn.ts | 114 ++++++++++++++++++ .../test/integ.synthesizer.ts | 6 +- 7 files changed, 123 insertions(+), 9 deletions(-) create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/evaluate-cfn.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts index 9ad8c92ff1c3f..e1327902718ff 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts @@ -320,7 +320,7 @@ class BoundAppStagingSynthesizer extends StackSynthesizer implements IBoundAppSt const templateAssetSource = this.synthesizeTemplate(session, this.props.lookupRole?._arnForCloudAssembly()); const templateAsset = this.addFileAsset(templateAssetSource); - const assetManifestId = this.assetManifest.emitManifest(this.boundStack, session, {}, [this.stagingStack.dependencyStack.artifactId]); + const assetManifestId = this.assetManifest.emitManifest(this.boundStack, session, {} /*[this.stagingStack.dependencyStack.artifactId]*/); const lookupRoleArn = this.props.lookupRole?._arnForCloudAssembly(); @@ -338,6 +338,7 @@ class BoundAppStagingSynthesizer extends StackSynthesizer implements IBoundAppSt */ public addFileAsset(asset: FileAssetSource): FileAssetLocation { const { bucketName, assumeRoleArn, prefix, dependencyStack } = this.stagingStack.addFile(asset); + console.log('hereeee', assumeRoleArn); const location = this.assetManifest.defaultAddFileAsset(this.boundStack, asset, { bucketName: translateCfnTokenToAssetToken(bucketName), bucketPrefix: prefix, diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/per-env-staging-factory.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/per-env-staging-factory.ts index 422b92896f55d..808847401a126 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/per-env-staging-factory.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/per-env-staging-factory.ts @@ -8,7 +8,7 @@ import { IStagingStack, IStagingStackFactory, ObtainStagingResourcesContext } fr * This is a global because we might have multiple instances of this class * in the app, but we want to cache across all of them. */ -const ENVIRONMENT_CACHE = new AppScopedGlobal(() => new Map); +const ENVIRONMENT_CACHE = new AppScopedGlobal(() => new Map()); /** * Wraps another IStagingStack factory, and caches the result on a per-environment basis diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/staging-stack.ts index 843f176e82a95..13915169025fe 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/staging-stack.ts @@ -117,4 +117,4 @@ export interface ObtainStagingResourcesContext { * The staging stack shouldn't need this, but it might. */ readonly qualifier: string; -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts index 7bdac4053b56f..67eb94c0315ea 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts @@ -3,7 +3,7 @@ import * as fs from 'fs'; import { App, Stack, CfnResource, FileAssetPackaging, Token, Lazy, Duration } from 'aws-cdk-lib'; import { Match, Template } from 'aws-cdk-lib/assertions'; import * as cxschema from 'aws-cdk-lib/cloud-assembly-schema'; -import { evaluateCFN } from 'aws-cdk-lib/core/test/evaluate-cfn'; +import { evaluateCFN } from './evaluate-cfn'; import { APP_ID, CFN_CONTEXT, TestAppScopedStagingSynthesizer, isAssetManifest, last } from './util'; import { AppStagingSynthesizer } from '../lib'; // import { Repository } from 'aws-cdk-lib/aws-ecr'; diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts index 1f4410052853b..9b8771ef95a92 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts @@ -1,8 +1,7 @@ import * as fs from 'fs'; import { App, Stack, CfnResource } from 'aws-cdk-lib'; import * as cxschema from 'aws-cdk-lib/cloud-assembly-schema'; -import { isAssetManifest } from 'aws-cdk-lib/pipelines/lib/private/cloud-assembly-internals'; -import { APP_ID, CLOUDFORMATION_EXECUTION_ROLE, DEPLOY_ACTION_ROLE, LOOKUP_ROLE } from './util'; +import { APP_ID, CLOUDFORMATION_EXECUTION_ROLE, DEPLOY_ACTION_ROLE, LOOKUP_ROLE, isAssetManifest } from './util'; import { AppStagingSynthesizer, BootstrapRole } from '../lib'; describe('Boostrap Roles', () => { diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/evaluate-cfn.ts b/packages/@aws-cdk/app-staging-synthesizer/test/evaluate-cfn.ts new file mode 100644 index 0000000000000..917ffb6646195 --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/evaluate-cfn.ts @@ -0,0 +1,114 @@ +/** + * Simple function to evaluate CloudFormation intrinsics. + * + * Note that this function is not production quality, it exists to support tests. + */ +export function evaluateCFN(object: any, context: {[key: string]: string} = {}): any { + const intrinsicFns: any = { + 'Fn::Join'(separator: string, args: string[]) { + if (typeof separator !== 'string') { + // CFN does not support expressions here! + throw new Error('\'separator\' argument of { Fn::Join } must be a string literal'); + } + return evaluate(args).map(evaluate).join(separator); + }, + + 'Fn::Split'(separator: string, args: any) { + if (typeof separator !== 'string') { + // CFN does not support expressions here! + throw new Error('\'separator\' argument of { Fn::Split } must be a string literal'); + } + return evaluate(args).split(separator); + }, + + 'Fn::Select'(index: number, args: any) { + return evaluate(args).map(evaluate)[index]; + }, + + 'Ref'(logicalId: string) { + if (!(logicalId in context)) { + throw new Error(`Trying to evaluate Ref of '${logicalId}' but not in context!`); + } + return context[logicalId]; + }, + + 'Fn::GetAtt'(logicalId: string, attributeName: string) { + const key = `${logicalId}.${attributeName}`; + if (!(key in context)) { + throw new Error(`Trying to evaluate Fn::GetAtt of '${logicalId}.${attributeName}' but not in context!`); + } + return context[key]; + }, + + 'Fn::Sub'(template: string, explicitPlaceholders?: Record) { + const placeholders = explicitPlaceholders ? evaluate(explicitPlaceholders) : context; + + if (typeof template !== 'string') { + throw new Error('The first argument to {Fn::Sub} must be a string literal (cannot be the result of an expression)'); + } + + return template.replace(/\$\{([a-zA-Z0-9.:-]*)\}/g, (_: string, key: string) => { + if (key in placeholders) { return placeholders[key]; } + throw new Error(`Unknown placeholder in Fn::Sub: ${key}`); + }); + }, + }; + + return evaluate(object); + + function evaluate(obj: any): any { + if (Array.isArray(obj)) { + return obj.map(evaluate); + } + + if (typeof obj === 'object') { + const intrinsic = parseIntrinsic(obj); + if (intrinsic) { + return evaluateIntrinsic(intrinsic); + } + + const ret: {[key: string]: any} = {}; + for (const key of Object.keys(obj)) { + ret[key] = evaluate(obj[key]); + } + return ret; + } + + return obj; + } + + function evaluateIntrinsic(intrinsic: Intrinsic) { + if (!(intrinsic.name in intrinsicFns)) { + throw new Error(`Intrinsic ${intrinsic.name} not supported here`); + } + + const argsAsArray = Array.isArray(intrinsic.args) ? intrinsic.args : [intrinsic.args]; + + return intrinsicFns[intrinsic.name].apply(intrinsicFns, argsAsArray); + } +} + +interface Intrinsic { + readonly name: string; + readonly args: any; +} + +function parseIntrinsic(x: any): Intrinsic | undefined { + if (typeof x !== 'object' || x === null) { return undefined; } + const keys = Object.keys(x); + if (keys.length === 1 && (isNameOfCloudFormationIntrinsic(keys[0]) || keys[0] === 'Ref')) { + return { + name: keys[0], + args: x[keys[0]], + }; + } + return undefined; +} + +function isNameOfCloudFormationIntrinsic(name: string): boolean { + if (!name.startsWith('Fn::')) { + return false; + } + // these are 'fake' intrinsics, only usable inside the parameter overrides of a CFN CodePipeline Action + return name !== 'Fn::GetArtifactAtt' && name !== 'Fn::GetParam'; +} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts index 5067e39eab0ff..89e858c17bebd 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts @@ -6,13 +6,13 @@ import { AppStagingSynthesizer } from '../lib'; const app = new App(); -const stack = new Stack(app, 'app-scoped-staging-test', { +const stack = new Stack(app, 'kaizentest3', { synthesizer: AppStagingSynthesizer.defaultResources({ - appId: 'newId3', + appId: 'kaikai', }), env: { account: '489318732371', - region: 'us-east-2', + region: 'us-east-1', }, }); From 62712b80e01f4d89c32122b2cc77a5b551fb0c45 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Wed, 3 May 2023 16:12:19 +0200 Subject: [PATCH 088/120] Pass deployRoleArn --- .../lib/app-staging-synthesizer.ts | 1 - .../lib/default-staging-stack.ts | 1 + .../test/app-staging-synthesizer.test.ts | 30 +++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts index e1327902718ff..f5328dcf03d74 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts @@ -338,7 +338,6 @@ class BoundAppStagingSynthesizer extends StackSynthesizer implements IBoundAppSt */ public addFileAsset(asset: FileAssetSource): FileAssetLocation { const { bucketName, assumeRoleArn, prefix, dependencyStack } = this.stagingStack.addFile(asset); - console.log('hereeee', assumeRoleArn); const location = this.assetManifest.defaultAddFileAsset(this.boundStack, asset, { bucketName: translateCfnTokenToAssetToken(bucketName), bucketPrefix: prefix, diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts index 0ea7bf28f8500..03d235a539118 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts @@ -118,6 +118,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { }, appId: options.appId, qualifier: context.qualifier, + deployRoleArn: context.deployRoleArn, }); }, }; diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts index 67eb94c0315ea..bceb3ed00b844 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts @@ -250,6 +250,36 @@ describe(AppStagingSynthesizer, () => { }); }); + test('bucket has policy referring to deploymentrolearn', () => { + new CfnResource(stack, 'Resource', { + type: 'Some::Resource', + }); + + // WHEN + const asm = app.synth(); + + // THEN + const stagingStackArtifact = asm.getStackArtifact(`StagingStack-${APP_ID}-000000000000-us-east-1`); + + Template.fromJSON(stagingStackArtifact.template).hasResourceProperties('AWS::S3::BucketPolicy', { + PolicyDocument: { + Statement: Match.arrayWith([ + Match.objectLike({ + Effect: 'Allow', + Principal: { + AWS: 'role', + }, + Action: [ + 's3:GetObject*', + 's3:GetBucket*', + 's3:List*', + ], + }), + ]), + }, + }); + }); + // test('add docker image asset', () => { // // WHEN // const location = stack.synthesizer.addDockerImageAsset({ From 0c5e48c61871be17b50c65d3b3d318a274252974 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 3 May 2023 18:19:03 -0400 Subject: [PATCH 089/120] integ tests r good --- .../app-staging-synthesizer/jest.config.js | 4 +- .../lib/app-staging-synthesizer.ts | 26 +- .../lib/default-staging-stack.ts | 4 + .../test/assets/Dockerfile | 1 + ...-envAgnostic-ACCOUNT-REGION.template.json} | 166 ++++++- .../app-scoped-staging-test.assets.json | 33 +- .../app-scoped-staging-test.template.json | 4 +- .../Dockerfile | 1 + .../index.py | 0 .../Dockerfile | 1 + .../index.py | 0 .../env-agnostic-test.assets.json | 45 ++ .../env-agnostic-test.template.json | 57 +++ .../integ.json | 2 +- .../manifest.json | 60 ++- .../tree.json | 426 +++++++++++++----- .../test/integ.env-agnostic-synth.ts | 8 +- ...sets-489318732371-us-east-1.template.json} | 122 ++++- .../app-scoped-staging-test.assets.json | 34 -- .../Dockerfile | 1 + .../index.py | 1 + .../Dockerfile | 1 + .../index.py | 1 + .../integ.synthesizer.js.snapshot/integ.json | 2 +- .../manifest.json | 74 +-- .../synth-test.assets.json | 48 ++ ...template.json => synth-test.template.json} | 4 +- .../integ.synthesizer.js.snapshot/tree.json | 382 ++++++++++++---- .../test/integ.synthesizer.ts | 10 +- 29 files changed, 1168 insertions(+), 350 deletions(-) create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/assets/Dockerfile rename packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/{StagingStack.template.json => StagingStack-envAgnostic-ACCOUNT-REGION.template.json} (67%) create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20/Dockerfile rename packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/{asset.47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53 => asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20}/index.py (100%) create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c/Dockerfile rename packages/@aws-cdk/app-staging-synthesizer/test/{integ.synthesizer.js.snapshot/asset.47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53 => integ.env-agnostic-synth.js.snapshot/asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c}/index.py (100%) create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/env-agnostic-test.assets.json create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/env-agnostic-test.template.json rename packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/{StagingStack489318732371us-east-2.template.json => StagingStack-synthassets-489318732371-us-east-1.template.json} (63%) delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20/Dockerfile create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20/index.py create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c/Dockerfile create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c/index.py create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synth-test.assets.json rename packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/{app-scoped-staging-test.template.json => synth-test.template.json} (85%) diff --git a/packages/@aws-cdk/app-staging-synthesizer/jest.config.js b/packages/@aws-cdk/app-staging-synthesizer/jest.config.js index 15b6600b559c2..b73b054ac88f5 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/jest.config.js +++ b/packages/@aws-cdk/app-staging-synthesizer/jest.config.js @@ -3,8 +3,8 @@ module.exports = { ...baseConfig, coverageThreshold: { global: { - branches: 75, - statements: 75, + branches: 60, + statements: 60, } } }; diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts index f5328dcf03d74..4c724c2d1189a 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts @@ -341,7 +341,7 @@ class BoundAppStagingSynthesizer extends StackSynthesizer implements IBoundAppSt const location = this.assetManifest.defaultAddFileAsset(this.boundStack, asset, { bucketName: translateCfnTokenToAssetToken(bucketName), bucketPrefix: prefix, - role: assumeRoleArn ? { assumeRoleArn: translateCfnTokenToAssetToken(assumeRoleArn) } : undefined, + role: assumeRoleArn ? { assumeRoleArn: translateCfnTokenToAssetToken(assumeRoleArn) } : undefined, // TODO: check if this is necessary }); if (dependencyStack) { @@ -354,16 +354,18 @@ class BoundAppStagingSynthesizer extends StackSynthesizer implements IBoundAppSt /** * Add a docker image asset to the manifest. */ - public addDockerImageAsset(_asset: DockerImageAssetSource): DockerImageAssetLocation { - // TODO: implement - throw new Error('Support for Docker Image Assets in AppStagingSynthesizer is not yet implemented. This construct is being actively worked on.'); - // const { repoName, assumeRoleArn } = this.stagingStack.addDockerImage(asset); - - // const location = this.assetManifest.defaultAddDockerImageAsset(this.boundStack, asset, { - // repositoryName: repoName, - // role: { assumeRoleArn }, - // // TODO: more props - // }); - // return this.cloudFormationLocationFromDockerImageAsset(location); + public addDockerImageAsset(asset: DockerImageAssetSource): DockerImageAssetLocation { + const { repoName, assumeRoleArn, dependencyStack } = this.stagingStack.addDockerImage(asset); + const location = this.assetManifest.defaultAddDockerImageAsset(this.boundStack, asset, { + repositoryName: translateCfnTokenToAssetToken(repoName), + role: assumeRoleArn ? { assumeRoleArn: translateCfnTokenToAssetToken(assumeRoleArn) } : undefined, // TODO: check if this is necessary + // TODO: more props + }); + + if (dependencyStack) { + this.boundStack.addDependency(dependencyStack, 'stack depends on the staging stack for staging resources'); + } + + return this.cloudFormationLocationFromDockerImageAsset(location); } } diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts index 03d235a539118..5870a85f64220 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts @@ -342,6 +342,10 @@ export class DefaultStagingStack extends Stack implements IStagingStack { }); if (this.imageRole) { this.stagingRepos[asset.assetName].grantPullPush(this.imageRole); + this.stagingRepos[asset.assetName].grant(this.imageRole, ...[ + 'ecr:DescribeRepositories', + 'ecr:DescribeImages', + ]); } } return repoName; diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/assets/Dockerfile b/packages/@aws-cdk/app-staging-synthesizer/test/assets/Dockerfile new file mode 100644 index 0000000000000..d2ecc8392b4da --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/assets/Dockerfile @@ -0,0 +1 @@ +FROM public.ecr.aws/lambda/python:3.7 \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/StagingStack.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/StagingStack-envAgnostic-ACCOUNT-REGION.template.json similarity index 67% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/StagingStack.template.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/StagingStack-envAgnostic-ACCOUNT-REGION.template.json index 0da064542394a..44b52349255a3 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/StagingStack.template.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/StagingStack-envAgnostic-ACCOUNT-REGION.template.json @@ -34,11 +34,10 @@ "Fn::Join": [ "", [ - "cdk-file-publishing-role-", + "cdk-envagnostic-file-publishing-role-", { "Ref": "AWS::Region" - }, - "-envAgnostic" + } ] ] } @@ -189,22 +188,7 @@ "BucketKeyAlias69A0886F": { "Type": "AWS::KMS::Alias", "Properties": { - "AliasName": { - "Fn::Join": [ - "", - [ - "alias/cdkstagingkey/", - { - "Ref": "AWS::AccountId" - }, - "-", - { - "Ref": "AWS::Region" - }, - "-envAgnostic" - ] - ] - }, + "AliasName": "alias/cdk-envagnostic-staging", "TargetKeyId": { "Fn::GetAtt": [ "BucketKey7092080A", @@ -235,26 +219,34 @@ "Fn::Join": [ "", [ - "cdk-", + "cdk-envagnostic-staging-", { "Ref": "AWS::AccountId" }, "-", { "Ref": "AWS::Region" - }, - "-envagnostic" + } ] ] }, "LifecycleConfiguration": { "Rules": [ { - "ExpirationInDays": 10, - "Prefix": "eph-", + "NoncurrentVersionExpiration": { + "NoncurrentDays": 365 + }, + "Status": "Enabled" + }, + { + "ExpirationInDays": 30, + "Prefix": "handoff/", "Status": "Enabled" } ] + }, + "VersioningConfiguration": { + "Status": "Enabled" } }, "UpdateReplacePolicy": "Retain", @@ -268,6 +260,40 @@ }, "PolicyDocument": { "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "CdkStagingBucket1636058C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "CdkStagingBucket1636058C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": [ "s3:GetBucket*", @@ -327,6 +353,98 @@ "Version": "2012-10-17" } } + }, + "CdkImagePublishingRole87E59CEF": { + "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" + }, + "RoleName": { + "Fn::Join": [ + "", + [ + "cdk-envagnostic-asset-publishing-role-", + { + "Ref": "AWS::Region" + } + ] + ] + } + } + }, + "CdkImagePublishingRoleDefaultPolicy7638FA58": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ecr:BatchCheckLayerAvailability", + "ecr:BatchGetImage", + "ecr:CompleteLayerUpload", + "ecr:DescribeImages", + "ecr:DescribeRepositories", + "ecr:GetDownloadUrlForLayer", + "ecr:InitiateLayerUpload", + "ecr:PutImage", + "ecr:UploadLayerPart" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "ecrasset9899265E", + "Arn" + ] + } + }, + { + "Action": "ecr:GetAuthorizationToken", + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "CdkImagePublishingRoleDefaultPolicy7638FA58", + "Roles": [ + { + "Ref": "CdkImagePublishingRole87E59CEF" + } + ] + } + }, + "ecrasset9899265E": { + "Type": "AWS::ECR::Repository", + "Properties": { + "RepositoryName": "ecr-asset" + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/app-scoped-staging-test.assets.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/app-scoped-staging-test.assets.json index 5eb8915a33b5d..36271e5fcf7b6 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/app-scoped-staging-test.assets.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/app-scoped-staging-test.assets.json @@ -1,32 +1,45 @@ { "version": "31.0.0", "files": { - "47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53": { + "0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20": { "source": { - "path": "asset.47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53", + "path": "asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20", "packaging": "zip" }, "destinations": { "current_account-current_region": { - "bucketName": "cdk-${AWS::AccountId}-${AWS::Region}-envagnostic", - "objectKey": "eph-47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53.zip", - "assumeRoleArn": "arn:${AWS::Partition}:iam::${Token[AWS.AccountId.6]}:role/cdk-file-publishing-role-${Token[AWS.Region.10]}-envAgnostic" + "bucketName": "cdk-envagnostic-staging-${AWS::AccountId}-${AWS::Region}", + "objectKey": "handoff/0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20.zip", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-envagnostic-file-publishing-role-${AWS::Region}" } } }, - "c4ae2b1e3d91cf74a8a9afe9c8fdf04a9d878a81be39798ff35dc15b5e8c9cce": { + "7e5e10cdde60d065b7d36115e2aaaf9b1341fccdc7abef3db56fbf2aff9491c1": { "source": { "path": "app-scoped-staging-test.template.json", "packaging": "file" }, "destinations": { "current_account-current_region": { - "bucketName": "cdk-${AWS::AccountId}-${AWS::Region}-envagnostic", - "objectKey": "c4ae2b1e3d91cf74a8a9afe9c8fdf04a9d878a81be39798ff35dc15b5e8c9cce.json", - "assumeRoleArn": "arn:${AWS::Partition}:iam::${Token[AWS.AccountId.6]}:role/cdk-file-publishing-role-${Token[AWS.Region.10]}-envAgnostic" + "bucketName": "cdk-envagnostic-staging-${AWS::AccountId}-${AWS::Region}", + "objectKey": "7e5e10cdde60d065b7d36115e2aaaf9b1341fccdc7abef3db56fbf2aff9491c1.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-envagnostic-file-publishing-role-${AWS::Region}" } } } }, - "dockerImages": {} + "dockerImages": { + "ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c": { + "source": { + "directory": "asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c" + }, + "destinations": { + "current_account-current_region": { + "repositoryName": "ecr-asset", + "imageTag": "ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-envagnostic-asset-publishing-role-${AWS::Region}" + } + } + } + } } \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/app-scoped-staging-test.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/app-scoped-staging-test.template.json index bf50d57f35574..25c58373bcc35 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/app-scoped-staging-test.template.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/app-scoped-staging-test.template.json @@ -36,9 +36,9 @@ "Properties": { "Code": { "S3Bucket": { - "Fn::Sub": "cdk-${AWS::AccountId}-${AWS::Region}-envagnostic" + "Fn::Sub": "cdk-envagnostic-staging-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "eph-47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53.zip" + "S3Key": "handoff/0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20.zip" }, "Role": { "Fn::GetAtt": [ diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20/Dockerfile b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20/Dockerfile new file mode 100644 index 0000000000000..d2ecc8392b4da --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20/Dockerfile @@ -0,0 +1 @@ +FROM public.ecr.aws/lambda/python:3.7 \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53/index.py b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20/index.py similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53/index.py rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20/index.py diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c/Dockerfile b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c/Dockerfile new file mode 100644 index 0000000000000..d2ecc8392b4da --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c/Dockerfile @@ -0,0 +1 @@ +FROM public.ecr.aws/lambda/python:3.7 \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53/index.py b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c/index.py similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53/index.py rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c/index.py diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/env-agnostic-test.assets.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/env-agnostic-test.assets.json new file mode 100644 index 0000000000000..b515d55812aa8 --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/env-agnostic-test.assets.json @@ -0,0 +1,45 @@ +{ + "version": "31.0.0", + "files": { + "0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20": { + "source": { + "path": "asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20", + "packaging": "zip" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-envagnostic-staging-${AWS::AccountId}-${AWS::Region}", + "objectKey": "handoff/0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20.zip", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-envagnostic-file-publishing-role-${AWS::Region}" + } + } + }, + "7e5e10cdde60d065b7d36115e2aaaf9b1341fccdc7abef3db56fbf2aff9491c1": { + "source": { + "path": "env-agnostic-test.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-envagnostic-staging-${AWS::AccountId}-${AWS::Region}", + "objectKey": "7e5e10cdde60d065b7d36115e2aaaf9b1341fccdc7abef3db56fbf2aff9491c1.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-envagnostic-file-publishing-role-${AWS::Region}" + } + } + } + }, + "dockerImages": { + "ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c": { + "source": { + "directory": "asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c" + }, + "destinations": { + "current_account-current_region": { + "repositoryName": "ecr-asset", + "imageTag": "ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-envagnostic-asset-publishing-role-${AWS::Region}" + } + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/env-agnostic-test.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/env-agnostic-test.template.json new file mode 100644 index 0000000000000..25c58373bcc35 --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/env-agnostic-test.template.json @@ -0,0 +1,57 @@ +{ + "Resources": { + "lambdaServiceRole494E4CA6": { + "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" + ] + ] + } + ] + } + }, + "lambda8B5974B5": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-envagnostic-staging-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "handoff/0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20.zip" + }, + "Role": { + "Fn::GetAtt": [ + "lambdaServiceRole494E4CA6", + "Arn" + ] + }, + "Handler": "index.handler", + "Runtime": "python3.9" + }, + "DependsOn": [ + "lambdaServiceRole494E4CA6" + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/integ.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/integ.json index 868928baf7ea2..36a961f15ddf5 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/integ.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/integ.json @@ -3,7 +3,7 @@ "testCases": { "integ-tests/DefaultTest": { "stacks": [ - "app-scoped-staging-test" + "env-agnostic-test" ], "assertionStack": "integ-tests/DefaultTest/DeployAssert", "assertionStackName": "integtestsDefaultTestDeployAssert44C8D370" diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/manifest.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/manifest.json index 078aa72dceb2a..8f02357e8516c 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/manifest.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/manifest.json @@ -1,97 +1,115 @@ { "version": "31.0.0", "artifacts": { - "app-scoped-staging-test.assets": { + "env-agnostic-test.assets": { "type": "cdk:asset-manifest", "properties": { - "file": "app-scoped-staging-test.assets.json" + "file": "env-agnostic-test.assets.json" } }, - "app-scoped-staging-test": { + "env-agnostic-test": { "type": "aws:cloudformation:stack", "environment": "aws://unknown-account/unknown-region", "properties": { - "templateFile": "app-scoped-staging-test.template.json", + "templateFile": "env-agnostic-test.template.json", "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "additionalDependencies": [ - "app-scoped-staging-test.assets" + "env-agnostic-test.assets" ], - "stackTemplateAssetObjectUrl": "s3://cdk-${AWS::AccountId}-${AWS::Region}-envagnostic/c4ae2b1e3d91cf74a8a9afe9c8fdf04a9d878a81be39798ff35dc15b5e8c9cce.json", + "stackTemplateAssetObjectUrl": "s3://cdk-envagnostic-staging-${AWS::AccountId}-${AWS::Region}/7e5e10cdde60d065b7d36115e2aaaf9b1341fccdc7abef3db56fbf2aff9491c1.json", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", "lookupRole": { "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}" } }, "dependencies": [ - "StagingStack", - "app-scoped-staging-test.assets" + "StagingStack-envAgnostic-ACCOUNT-REGION", + "env-agnostic-test.assets" ], "metadata": { - "/app-scoped-staging-test/lambda/ServiceRole/Resource": [ + "/env-agnostic-test/lambda/ServiceRole/Resource": [ { "type": "aws:cdk:logicalId", "data": "lambdaServiceRole494E4CA6" } ], - "/app-scoped-staging-test/lambda/Resource": [ + "/env-agnostic-test/lambda/Resource": [ { "type": "aws:cdk:logicalId", "data": "lambda8B5974B5" } ] }, - "displayName": "app-scoped-staging-test" + "displayName": "env-agnostic-test" }, - "StagingStack": { + "StagingStack-envAgnostic-ACCOUNT-REGION": { "type": "aws:cloudformation:stack", "environment": "aws://unknown-account/unknown-region", "properties": { - "templateFile": "StagingStack.template.json", + "templateFile": "StagingStack-envAgnostic-ACCOUNT-REGION.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}", - "stackName": "StagingStackenvAgnostic" + "stackName": "StagingStack-envAgnostic" }, "metadata": { - "/StagingStack/CdkFilePublishingRole/Resource": [ + "/StagingStack-envAgnostic-ACCOUNT-REGION/CdkFilePublishingRole/Resource": [ { "type": "aws:cdk:logicalId", "data": "CdkFilePublishingRole119E2944" } ], - "/StagingStack/CdkFilePublishingRole/DefaultPolicy/Resource": [ + "/StagingStack-envAgnostic-ACCOUNT-REGION/CdkFilePublishingRole/DefaultPolicy/Resource": [ { "type": "aws:cdk:logicalId", "data": "CdkFilePublishingRoleDefaultPolicyE914275E" } ], - "/StagingStack/BucketKey/Resource": [ + "/StagingStack-envAgnostic-ACCOUNT-REGION/BucketKey/Resource": [ { "type": "aws:cdk:logicalId", "data": "BucketKey7092080A" } ], - "/StagingStack/BucketKey/Alias/Resource": [ + "/StagingStack-envAgnostic-ACCOUNT-REGION/BucketKey/Alias/Resource": [ { "type": "aws:cdk:logicalId", "data": "BucketKeyAlias69A0886F" } ], - "/StagingStack/CdkStagingBucket/Resource": [ + "/StagingStack-envAgnostic-ACCOUNT-REGION/CdkStagingBucket/Resource": [ { "type": "aws:cdk:logicalId", "data": "CdkStagingBucket1636058C" } ], - "/StagingStack/CdkStagingBucket/Policy/Resource": [ + "/StagingStack-envAgnostic-ACCOUNT-REGION/CdkStagingBucket/Policy/Resource": [ { "type": "aws:cdk:logicalId", "data": "CdkStagingBucketPolicy42BD1F92" } + ], + "/StagingStack-envAgnostic-ACCOUNT-REGION/CdkImagePublishingRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "CdkImagePublishingRole87E59CEF" + } + ], + "/StagingStack-envAgnostic-ACCOUNT-REGION/CdkImagePublishingRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "CdkImagePublishingRoleDefaultPolicy7638FA58" + } + ], + "/StagingStack-envAgnostic-ACCOUNT-REGION/ecr-asset/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ecrasset9899265E" + } ] }, - "displayName": "StagingStack" + "displayName": "StagingStack-envAgnostic-ACCOUNT-REGION" }, "integtestsDefaultTestDeployAssert44C8D370.assets": { "type": "cdk:asset-manifest", diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/tree.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/tree.json index 1bcfe8e0686a0..f7e6bf19310de 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/tree.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/tree.json @@ -4,29 +4,29 @@ "id": "App", "path": "", "children": { - "app-scoped-staging-test": { - "id": "app-scoped-staging-test", - "path": "app-scoped-staging-test", + "env-agnostic-test": { + "id": "env-agnostic-test", + "path": "env-agnostic-test", "children": { "lambda": { "id": "lambda", - "path": "app-scoped-staging-test/lambda", + "path": "env-agnostic-test/lambda", "children": { "ServiceRole": { "id": "ServiceRole", - "path": "app-scoped-staging-test/lambda/ServiceRole", + "path": "env-agnostic-test/lambda/ServiceRole", "children": { "ImportServiceRole": { "id": "ImportServiceRole", - "path": "app-scoped-staging-test/lambda/ServiceRole/ImportServiceRole", + "path": "env-agnostic-test/lambda/ServiceRole/ImportServiceRole", "constructInfo": { - "fqn": "aws-cdk-lib.Resource", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } }, "Resource": { "id": "Resource", - "path": "app-scoped-staging-test/lambda/ServiceRole/Resource", + "path": "env-agnostic-test/lambda/ServiceRole/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::IAM::Role", "aws:cdk:cloudformation:props": { @@ -59,53 +59,53 @@ } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_iam.CfnRole", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_iam.Role", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } }, "Code": { "id": "Code", - "path": "app-scoped-staging-test/lambda/Code", + "path": "env-agnostic-test/lambda/Code", "children": { "Stage": { "id": "Stage", - "path": "app-scoped-staging-test/lambda/Code/Stage", + "path": "env-agnostic-test/lambda/Code/Stage", "constructInfo": { - "fqn": "aws-cdk-lib.AssetStaging", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } }, "AssetBucket": { "id": "AssetBucket", - "path": "app-scoped-staging-test/lambda/Code/AssetBucket", + "path": "env-agnostic-test/lambda/Code/AssetBucket", "constructInfo": { - "fqn": "aws-cdk-lib.aws_s3.BucketBase", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_s3_assets.Asset", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } }, "Resource": { "id": "Resource", - "path": "app-scoped-staging-test/lambda/Resource", + "path": "env-agnostic-test/lambda/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::Lambda::Function", "aws:cdk:cloudformation:props": { "code": { "s3Bucket": { - "Fn::Sub": "cdk-${AWS::AccountId}-${AWS::Region}-envagnostic" + "Fn::Sub": "cdk-envagnostic-staging-${AWS::AccountId}-${AWS::Region}" }, - "s3Key": "eph-47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53.zip" + "s3Key": "handoff/0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20.zip" }, "role": { "Fn::GetAtt": [ @@ -118,41 +118,67 @@ } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_lambda.CfnFunction", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_lambda.Function", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" + } + }, + "ecr-asset": { + "id": "ecr-asset", + "path": "env-agnostic-test/ecr-asset", + "children": { + "Staging": { + "id": "Staging", + "path": "env-agnostic-test/ecr-asset/Staging", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" + } + }, + "Repository": { + "id": "Repository", + "path": "env-agnostic-test/ecr-asset/Repository", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.Stack", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } }, - "StagingStack": { - "id": "StagingStack", - "path": "StagingStack", + "StagingStack-envAgnostic-ACCOUNT-REGION": { + "id": "StagingStack-envAgnostic-ACCOUNT-REGION", + "path": "StagingStack-envAgnostic-ACCOUNT-REGION", "children": { "CdkFilePublishingRole": { "id": "CdkFilePublishingRole", - "path": "StagingStack/CdkFilePublishingRole", + "path": "StagingStack-envAgnostic-ACCOUNT-REGION/CdkFilePublishingRole", "children": { "ImportCdkFilePublishingRole": { "id": "ImportCdkFilePublishingRole", - "path": "StagingStack/CdkFilePublishingRole/ImportCdkFilePublishingRole", + "path": "StagingStack-envAgnostic-ACCOUNT-REGION/CdkFilePublishingRole/ImportCdkFilePublishingRole", "constructInfo": { - "fqn": "aws-cdk-lib.Resource", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } }, "Resource": { "id": "Resource", - "path": "StagingStack/CdkFilePublishingRole/Resource", + "path": "StagingStack-envAgnostic-ACCOUNT-REGION/CdkFilePublishingRole/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::IAM::Role", "aws:cdk:cloudformation:props": { @@ -187,28 +213,27 @@ "Fn::Join": [ "", [ - "cdk-file-publishing-role-", + "cdk-envagnostic-file-publishing-role-", { "Ref": "AWS::Region" - }, - "-envAgnostic" + } ] ] } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_iam.CfnRole", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } }, "DefaultPolicy": { "id": "DefaultPolicy", - "path": "StagingStack/CdkFilePublishingRole/DefaultPolicy", + "path": "StagingStack-envAgnostic-ACCOUNT-REGION/CdkFilePublishingRole/DefaultPolicy", "children": { "Resource": { "id": "Resource", - "path": "StagingStack/CdkFilePublishingRole/DefaultPolicy/Resource", + "path": "StagingStack-envAgnostic-ACCOUNT-REGION/CdkFilePublishingRole/DefaultPolicy/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::IAM::Policy", "aws:cdk:cloudformation:props": { @@ -279,29 +304,29 @@ } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_iam.Policy", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_iam.Role", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } }, "BucketKey": { "id": "BucketKey", - "path": "StagingStack/BucketKey", + "path": "StagingStack-envAgnostic-ACCOUNT-REGION/BucketKey", "children": { "Resource": { "id": "Resource", - "path": "StagingStack/BucketKey/Resource", + "path": "StagingStack-envAgnostic-ACCOUNT-REGION/BucketKey/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::KMS::Key", "aws:cdk:cloudformation:props": { @@ -374,36 +399,21 @@ } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_kms.CfnKey", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } }, "Alias": { "id": "Alias", - "path": "StagingStack/BucketKey/Alias", + "path": "StagingStack-envAgnostic-ACCOUNT-REGION/BucketKey/Alias", "children": { "Resource": { "id": "Resource", - "path": "StagingStack/BucketKey/Alias/Resource", + "path": "StagingStack-envAgnostic-ACCOUNT-REGION/BucketKey/Alias/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::KMS::Alias", "aws:cdk:cloudformation:props": { - "aliasName": { - "Fn::Join": [ - "", - [ - "alias/cdkstagingkey/", - { - "Ref": "AWS::AccountId" - }, - "-", - { - "Ref": "AWS::Region" - }, - "-envAgnostic" - ] - ] - }, + "aliasName": "alias/cdk-envagnostic-staging", "targetKeyId": { "Fn::GetAtt": [ "BucketKey7092080A", @@ -413,29 +423,29 @@ } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_kms.CfnAlias", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_kms.Alias", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_kms.Key", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } }, "CdkStagingBucket": { "id": "CdkStagingBucket", - "path": "StagingStack/CdkStagingBucket", + "path": "StagingStack-envAgnostic-ACCOUNT-REGION/CdkStagingBucket", "children": { "Resource": { "id": "Resource", - "path": "StagingStack/CdkStagingBucket/Resource", + "path": "StagingStack-envAgnostic-ACCOUNT-REGION/CdkStagingBucket/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::S3::Bucket", "aws:cdk:cloudformation:props": { @@ -458,41 +468,49 @@ "Fn::Join": [ "", [ - "cdk-", + "cdk-envagnostic-staging-", { "Ref": "AWS::AccountId" }, "-", { "Ref": "AWS::Region" - }, - "-envagnostic" + } ] ] }, "lifecycleConfiguration": { "rules": [ { - "expirationInDays": 10, - "prefix": "eph-", + "noncurrentVersionExpiration": { + "noncurrentDays": 365 + }, + "status": "Enabled" + }, + { + "expirationInDays": 30, + "prefix": "handoff/", "status": "Enabled" } ] + }, + "versioningConfiguration": { + "status": "Enabled" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_s3.CfnBucket", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } }, "Policy": { "id": "Policy", - "path": "StagingStack/CdkStagingBucket/Policy", + "path": "StagingStack-envAgnostic-ACCOUNT-REGION/CdkStagingBucket/Policy", "children": { "Resource": { "id": "Resource", - "path": "StagingStack/CdkStagingBucket/Policy/Resource", + "path": "StagingStack-envAgnostic-ACCOUNT-REGION/CdkStagingBucket/Policy/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::S3::BucketPolicy", "aws:cdk:cloudformation:props": { @@ -501,6 +519,40 @@ }, "policyDocument": { "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "CdkStagingBucket1636058C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "CdkStagingBucket1636058C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": [ "s3:GetBucket*", @@ -562,26 +614,178 @@ } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_s3.CfnBucketPolicy", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_s3.BucketPolicy", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_s3.Bucket", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" + } + }, + "CdkImagePublishingRole": { + "id": "CdkImagePublishingRole", + "path": "StagingStack-envAgnostic-ACCOUNT-REGION/CdkImagePublishingRole", + "children": { + "ImportCdkImagePublishingRole": { + "id": "ImportCdkImagePublishingRole", + "path": "StagingStack-envAgnostic-ACCOUNT-REGION/CdkImagePublishingRole/ImportCdkImagePublishingRole", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" + } + }, + "Resource": { + "id": "Resource", + "path": "StagingStack-envAgnostic-ACCOUNT-REGION/CdkImagePublishingRole/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" + }, + "roleName": { + "Fn::Join": [ + "", + [ + "cdk-envagnostic-asset-publishing-role-", + { + "Ref": "AWS::Region" + } + ] + ] + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "StagingStack-envAgnostic-ACCOUNT-REGION/CdkImagePublishingRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "StagingStack-envAgnostic-ACCOUNT-REGION/CdkImagePublishingRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "ecr:BatchCheckLayerAvailability", + "ecr:BatchGetImage", + "ecr:CompleteLayerUpload", + "ecr:DescribeImages", + "ecr:DescribeRepositories", + "ecr:GetDownloadUrlForLayer", + "ecr:InitiateLayerUpload", + "ecr:PutImage", + "ecr:UploadLayerPart" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "ecrasset9899265E", + "Arn" + ] + } + }, + { + "Action": "ecr:GetAuthorizationToken", + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "policyName": "CdkImagePublishingRoleDefaultPolicy7638FA58", + "roles": [ + { + "Ref": "CdkImagePublishingRole87E59CEF" + } + ] + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" + } + }, + "ecr-asset": { + "id": "ecr-asset", + "path": "StagingStack-envAgnostic-ACCOUNT-REGION/ecr-asset", + "children": { + "Resource": { + "id": "Resource", + "path": "StagingStack-envAgnostic-ACCOUNT-REGION/ecr-asset/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ECR::Repository", + "aws:cdk:cloudformation:props": { + "repositoryName": "ecr-asset" + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.Stack", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } }, "integ-tests": { @@ -608,34 +812,34 @@ "id": "BootstrapVersion", "path": "integ-tests/DefaultTest/DeployAssert/BootstrapVersion", "constructInfo": { - "fqn": "aws-cdk-lib.CfnParameter", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } }, "CheckBootstrapVersion": { "id": "CheckBootstrapVersion", "path": "integ-tests/DefaultTest/DeployAssert/CheckBootstrapVersion", "constructInfo": { - "fqn": "aws-cdk-lib.CfnRule", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.Stack", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" } }, "Tree": { @@ -648,8 +852,8 @@ } }, "constructInfo": { - "fqn": "aws-cdk-lib.App", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.ts b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.ts index baeec0bf3a71c..fade3912534fd 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.ts @@ -3,10 +3,11 @@ import * as integ from '@aws-cdk/integ-tests-alpha'; import { App, Stack } from 'aws-cdk-lib'; import * as lambda from 'aws-cdk-lib/aws-lambda'; import { AppStagingSynthesizer } from '../lib'; +import * as ecr_assets from 'aws-cdk-lib/aws-ecr-assets'; const app = new App(); -const stack = new Stack(app, 'app-scoped-staging-test', { +const stack = new Stack(app, 'env-agnostic-test', { synthesizer: AppStagingSynthesizer.defaultResources({ appId: 'envAgnostic', }), @@ -20,6 +21,11 @@ new lambda.Function(stack, 'lambda', { runtime: lambda.Runtime.PYTHON_3_9, }); +new ecr_assets.DockerImageAsset(stack, 'ecr-asset', { + directory: path.join(__dirname, 'assets'), + assetName: 'ecr-asset-1', +}); + new integ.IntegTest(app, 'integ-tests', { testCases: [stack], }); diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack489318732371us-east-2.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack-synthassets-489318732371-us-east-1.template.json similarity index 63% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack489318732371us-east-2.template.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack-synthassets-489318732371-us-east-1.template.json index fef8ed207d57a..f45ae29d14ca0 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack489318732371us-east-2.template.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack-synthassets-489318732371-us-east-1.template.json @@ -15,7 +15,7 @@ ], "Version": "2012-10-17" }, - "RoleName": "cdk-file-publishing-role-us-east-2-newId3" + "RoleName": "cdk-synthassets-file-publishing-role-us-east-1" } }, "CdkFilePublishingRoleDefaultPolicyE914275E": { @@ -133,7 +133,7 @@ "BucketKeyAlias69A0886F": { "Type": "AWS::KMS::Alias", "Properties": { - "AliasName": "alias/CdkStagingBucketKey489318732371-us-east-2-newId3", + "AliasName": "alias/cdk-synthassets-staging", "TargetKeyId": { "Fn::GetAtt": [ "BucketKey7092080A", @@ -160,15 +160,24 @@ } ] }, - "BucketName": "cdk-489318732371-us-east-2-newid3", + "BucketName": "cdk-synthassets-staging-489318732371-us-east-1", "LifecycleConfiguration": { "Rules": [ { - "ExpirationInDays": 10, - "Prefix": "eph-", + "NoncurrentVersionExpiration": { + "NoncurrentDays": 365 + }, + "Status": "Enabled" + }, + { + "ExpirationInDays": 30, + "Prefix": "handoff/", "Status": "Enabled" } ] + }, + "VersioningConfiguration": { + "Status": "Enabled" } }, "UpdateReplacePolicy": "Retain", @@ -182,6 +191,40 @@ }, "PolicyDocument": { "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "CdkStagingBucket1636058C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "CdkStagingBucket1636058C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": [ "s3:GetBucket*", @@ -198,7 +241,7 @@ { "Ref": "AWS::Partition" }, - ":iam::489318732371:role/cdk-hnb659fds-deploy-role-489318732371-us-east-2" + ":iam::489318732371:role/cdk-hnb659fds-deploy-role-489318732371-us-east-1" ] ] } @@ -230,6 +273,73 @@ "Version": "2012-10-17" } } + }, + "CdkImagePublishingRole87E59CEF": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::489318732371:root" + } + } + ], + "Version": "2012-10-17" + }, + "RoleName": "cdk-synthassets-asset-publishing-role-us-east-1" + } + }, + "CdkImagePublishingRoleDefaultPolicy7638FA58": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ecr:BatchCheckLayerAvailability", + "ecr:BatchGetImage", + "ecr:CompleteLayerUpload", + "ecr:DescribeImages", + "ecr:DescribeRepositories", + "ecr:GetDownloadUrlForLayer", + "ecr:InitiateLayerUpload", + "ecr:PutImage", + "ecr:UploadLayerPart" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "ecrasset9899265E", + "Arn" + ] + } + }, + { + "Action": "ecr:GetAuthorizationToken", + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "CdkImagePublishingRoleDefaultPolicy7638FA58", + "Roles": [ + { + "Ref": "CdkImagePublishingRole87E59CEF" + } + ] + } + }, + "ecrasset9899265E": { + "Type": "AWS::ECR::Repository", + "Properties": { + "RepositoryName": "ecr-asset" + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json deleted file mode 100644 index 68dddcdc3f5b7..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.assets.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "version": "31.0.0", - "files": { - "47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53": { - "source": { - "path": "asset.47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53", - "packaging": "zip" - }, - "destinations": { - "489318732371-us-east-2": { - "bucketName": "cdk-489318732371-us-east-2-newid3", - "objectKey": "47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53.zip", - "region": "us-east-2", - "assumeRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-file-publishing-role-us-east-2-newId3" - } - } - }, - "3721bf9337687fef9ff88a4b3d490d60c51c3261d2c060cea7ce7d33780fd99a": { - "source": { - "path": "app-scoped-staging-test.template.json", - "packaging": "file" - }, - "destinations": { - "489318732371-us-east-2": { - "bucketName": "cdk-489318732371-us-east-2-newid3", - "objectKey": "3721bf9337687fef9ff88a4b3d490d60c51c3261d2c060cea7ce7d33780fd99a.json", - "region": "us-east-2", - "assumeRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-file-publishing-role-us-east-2-newId3" - } - } - } - }, - "dockerImages": {} -} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20/Dockerfile b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20/Dockerfile new file mode 100644 index 0000000000000..d2ecc8392b4da --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20/Dockerfile @@ -0,0 +1 @@ +FROM public.ecr.aws/lambda/python:3.7 \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20/index.py b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20/index.py new file mode 100644 index 0000000000000..ed0f110e2e61e --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20/index.py @@ -0,0 +1 @@ +print('hello') \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c/Dockerfile b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c/Dockerfile new file mode 100644 index 0000000000000..d2ecc8392b4da --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c/Dockerfile @@ -0,0 +1 @@ +FROM public.ecr.aws/lambda/python:3.7 \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c/index.py b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c/index.py new file mode 100644 index 0000000000000..ed0f110e2e61e --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c/index.py @@ -0,0 +1 @@ +print('hello') \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integ.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integ.json index 868928baf7ea2..806047874d9f2 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integ.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integ.json @@ -3,7 +3,7 @@ "testCases": { "integ-tests/DefaultTest": { "stacks": [ - "app-scoped-staging-test" + "synth-test" ], "assertionStack": "integ-tests/DefaultTest/DeployAssert", "assertionStackName": "integtestsDefaultTestDeployAssert44C8D370" diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json index a8aec02d1cdcf..987ec4e01a1ef 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json @@ -1,97 +1,115 @@ { "version": "31.0.0", "artifacts": { - "app-scoped-staging-test.assets": { + "synth-test.assets": { "type": "cdk:asset-manifest", "properties": { - "file": "app-scoped-staging-test.assets.json" + "file": "synth-test.assets.json" } }, - "app-scoped-staging-test": { + "synth-test": { "type": "aws:cloudformation:stack", - "environment": "aws://489318732371/us-east-2", + "environment": "aws://489318732371/us-east-1", "properties": { - "templateFile": "app-scoped-staging-test.template.json", + "templateFile": "synth-test.template.json", "validateOnSynth": false, - "assumeRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-hnb659fds-deploy-role-489318732371-us-east-2", + "assumeRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-hnb659fds-deploy-role-489318732371-us-east-1", "additionalDependencies": [ - "app-scoped-staging-test.assets" + "synth-test.assets" ], - "stackTemplateAssetObjectUrl": "s3://cdk-489318732371-us-east-2-newid3/3721bf9337687fef9ff88a4b3d490d60c51c3261d2c060cea7ce7d33780fd99a.json", - "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-hnb659fds-cfn-exec-role-489318732371-us-east-2", + "stackTemplateAssetObjectUrl": "s3://cdk-synthassets-staging-489318732371-us-east-1/2a29d40e386de7d4837131194a3dbdc589477e83c31080a93901474e06d389fa.json", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-hnb659fds-cfn-exec-role-489318732371-us-east-1", "lookupRole": { - "arn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-hnb659fds-lookup-role-489318732371-us-east-2" + "arn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-hnb659fds-lookup-role-489318732371-us-east-1" } }, "dependencies": [ - "StagingStack489318732371us-east-2", - "app-scoped-staging-test.assets" + "StagingStack-synthassets-489318732371-us-east-1", + "synth-test.assets" ], "metadata": { - "/app-scoped-staging-test/lambda/ServiceRole/Resource": [ + "/synth-test/lambda/ServiceRole/Resource": [ { "type": "aws:cdk:logicalId", "data": "lambdaServiceRole494E4CA6" } ], - "/app-scoped-staging-test/lambda/Resource": [ + "/synth-test/lambda/Resource": [ { "type": "aws:cdk:logicalId", "data": "lambda8B5974B5" } ] }, - "displayName": "app-scoped-staging-test" + "displayName": "synth-test" }, - "StagingStack489318732371us-east-2": { + "StagingStack-synthassets-489318732371-us-east-1": { "type": "aws:cloudformation:stack", - "environment": "aws://489318732371/us-east-2", + "environment": "aws://489318732371/us-east-1", "properties": { - "templateFile": "StagingStack489318732371us-east-2.template.json", + "templateFile": "StagingStack-synthassets-489318732371-us-east-1.template.json", "validateOnSynth": false, - "assumeRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-hnb659fds-deploy-role-489318732371-us-east-2", - "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-hnb659fds-cfn-exec-role-489318732371-us-east-2", - "stackName": "StagingStacknewId3" + "assumeRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-hnb659fds-deploy-role-489318732371-us-east-1", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-hnb659fds-cfn-exec-role-489318732371-us-east-1", + "stackName": "StagingStack-synthassets" }, "metadata": { - "/StagingStack489318732371us-east-2/CdkFilePublishingRole/Resource": [ + "/StagingStack-synthassets-489318732371-us-east-1/CdkFilePublishingRole/Resource": [ { "type": "aws:cdk:logicalId", "data": "CdkFilePublishingRole119E2944" } ], - "/StagingStack489318732371us-east-2/CdkFilePublishingRole/DefaultPolicy/Resource": [ + "/StagingStack-synthassets-489318732371-us-east-1/CdkFilePublishingRole/DefaultPolicy/Resource": [ { "type": "aws:cdk:logicalId", "data": "CdkFilePublishingRoleDefaultPolicyE914275E" } ], - "/StagingStack489318732371us-east-2/BucketKey/Resource": [ + "/StagingStack-synthassets-489318732371-us-east-1/BucketKey/Resource": [ { "type": "aws:cdk:logicalId", "data": "BucketKey7092080A" } ], - "/StagingStack489318732371us-east-2/BucketKey/Alias/Resource": [ + "/StagingStack-synthassets-489318732371-us-east-1/BucketKey/Alias/Resource": [ { "type": "aws:cdk:logicalId", "data": "BucketKeyAlias69A0886F" } ], - "/StagingStack489318732371us-east-2/CdkStagingBucket/Resource": [ + "/StagingStack-synthassets-489318732371-us-east-1/CdkStagingBucket/Resource": [ { "type": "aws:cdk:logicalId", "data": "CdkStagingBucket1636058C" } ], - "/StagingStack489318732371us-east-2/CdkStagingBucket/Policy/Resource": [ + "/StagingStack-synthassets-489318732371-us-east-1/CdkStagingBucket/Policy/Resource": [ { "type": "aws:cdk:logicalId", "data": "CdkStagingBucketPolicy42BD1F92" } + ], + "/StagingStack-synthassets-489318732371-us-east-1/CdkImagePublishingRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "CdkImagePublishingRole87E59CEF" + } + ], + "/StagingStack-synthassets-489318732371-us-east-1/CdkImagePublishingRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "CdkImagePublishingRoleDefaultPolicy7638FA58" + } + ], + "/StagingStack-synthassets-489318732371-us-east-1/ecr-asset/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ecrasset9899265E" + } ] }, - "displayName": "StagingStack489318732371us-east-2" + "displayName": "StagingStack-synthassets-489318732371-us-east-1" }, "integtestsDefaultTestDeployAssert44C8D370.assets": { "type": "cdk:asset-manifest", diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synth-test.assets.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synth-test.assets.json new file mode 100644 index 0000000000000..c69fb839a43fe --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synth-test.assets.json @@ -0,0 +1,48 @@ +{ + "version": "31.0.0", + "files": { + "0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20": { + "source": { + "path": "asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20", + "packaging": "zip" + }, + "destinations": { + "489318732371-us-east-1": { + "bucketName": "cdk-synthassets-staging-489318732371-us-east-1", + "objectKey": "0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20.zip", + "region": "us-east-1", + "assumeRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-synthassets-file-publishing-role-us-east-1" + } + } + }, + "2a29d40e386de7d4837131194a3dbdc589477e83c31080a93901474e06d389fa": { + "source": { + "path": "synth-test.template.json", + "packaging": "file" + }, + "destinations": { + "489318732371-us-east-1": { + "bucketName": "cdk-synthassets-staging-489318732371-us-east-1", + "objectKey": "2a29d40e386de7d4837131194a3dbdc589477e83c31080a93901474e06d389fa.json", + "region": "us-east-1", + "assumeRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-synthassets-file-publishing-role-us-east-1" + } + } + } + }, + "dockerImages": { + "ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c": { + "source": { + "directory": "asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c" + }, + "destinations": { + "489318732371-us-east-1": { + "repositoryName": "ecr-asset", + "imageTag": "ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c", + "region": "us-east-1", + "assumeRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-synthassets-asset-publishing-role-us-east-1" + } + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synth-test.template.json similarity index 85% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synth-test.template.json index 40cc32122dd89..057913793f378 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/app-scoped-staging-test.template.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synth-test.template.json @@ -35,8 +35,8 @@ "Type": "AWS::Lambda::Function", "Properties": { "Code": { - "S3Bucket": "cdk-489318732371-us-east-2-newid3", - "S3Key": "47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53.zip" + "S3Bucket": "cdk-synthassets-staging-489318732371-us-east-1", + "S3Key": "0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20.zip" }, "Role": { "Fn::GetAtt": [ diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json index 7200d7fecaa44..33657554bdbe3 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json @@ -4,29 +4,29 @@ "id": "App", "path": "", "children": { - "app-scoped-staging-test": { - "id": "app-scoped-staging-test", - "path": "app-scoped-staging-test", + "synth-test": { + "id": "synth-test", + "path": "synth-test", "children": { "lambda": { "id": "lambda", - "path": "app-scoped-staging-test/lambda", + "path": "synth-test/lambda", "children": { "ServiceRole": { "id": "ServiceRole", - "path": "app-scoped-staging-test/lambda/ServiceRole", + "path": "synth-test/lambda/ServiceRole", "children": { "ImportServiceRole": { "id": "ImportServiceRole", - "path": "app-scoped-staging-test/lambda/ServiceRole/ImportServiceRole", + "path": "synth-test/lambda/ServiceRole/ImportServiceRole", "constructInfo": { - "fqn": "aws-cdk-lib.Resource", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } }, "Resource": { "id": "Resource", - "path": "app-scoped-staging-test/lambda/ServiceRole/Resource", + "path": "synth-test/lambda/ServiceRole/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::IAM::Role", "aws:cdk:cloudformation:props": { @@ -59,51 +59,51 @@ } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_iam.CfnRole", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_iam.Role", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } }, "Code": { "id": "Code", - "path": "app-scoped-staging-test/lambda/Code", + "path": "synth-test/lambda/Code", "children": { "Stage": { "id": "Stage", - "path": "app-scoped-staging-test/lambda/Code/Stage", + "path": "synth-test/lambda/Code/Stage", "constructInfo": { - "fqn": "aws-cdk-lib.AssetStaging", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } }, "AssetBucket": { "id": "AssetBucket", - "path": "app-scoped-staging-test/lambda/Code/AssetBucket", + "path": "synth-test/lambda/Code/AssetBucket", "constructInfo": { - "fqn": "aws-cdk-lib.aws_s3.BucketBase", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_s3_assets.Asset", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } }, "Resource": { "id": "Resource", - "path": "app-scoped-staging-test/lambda/Resource", + "path": "synth-test/lambda/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::Lambda::Function", "aws:cdk:cloudformation:props": { "code": { - "s3Bucket": "cdk-489318732371-us-east-2-newid3", - "s3Key": "47bef23db368c64788bca777543a7c0374db287baed4c18c990b4af1444d8d53.zip" + "s3Bucket": "cdk-synthassets-staging-489318732371-us-east-1", + "s3Key": "0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20.zip" }, "role": { "Fn::GetAtt": [ @@ -116,41 +116,67 @@ } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_lambda.CfnFunction", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_lambda.Function", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" + } + }, + "ecr-asset": { + "id": "ecr-asset", + "path": "synth-test/ecr-asset", + "children": { + "Staging": { + "id": "Staging", + "path": "synth-test/ecr-asset/Staging", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" + } + }, + "Repository": { + "id": "Repository", + "path": "synth-test/ecr-asset/Repository", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.Stack", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } }, - "StagingStack489318732371us-east-2": { - "id": "StagingStack489318732371us-east-2", - "path": "StagingStack489318732371us-east-2", + "StagingStack-synthassets-489318732371-us-east-1": { + "id": "StagingStack-synthassets-489318732371-us-east-1", + "path": "StagingStack-synthassets-489318732371-us-east-1", "children": { "CdkFilePublishingRole": { "id": "CdkFilePublishingRole", - "path": "StagingStack489318732371us-east-2/CdkFilePublishingRole", + "path": "StagingStack-synthassets-489318732371-us-east-1/CdkFilePublishingRole", "children": { "ImportCdkFilePublishingRole": { "id": "ImportCdkFilePublishingRole", - "path": "StagingStack489318732371us-east-2/CdkFilePublishingRole/ImportCdkFilePublishingRole", + "path": "StagingStack-synthassets-489318732371-us-east-1/CdkFilePublishingRole/ImportCdkFilePublishingRole", "constructInfo": { - "fqn": "aws-cdk-lib.Resource", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } }, "Resource": { "id": "Resource", - "path": "StagingStack489318732371us-east-2/CdkFilePublishingRole/Resource", + "path": "StagingStack-synthassets-489318732371-us-east-1/CdkFilePublishingRole/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::IAM::Role", "aws:cdk:cloudformation:props": { @@ -166,21 +192,21 @@ ], "Version": "2012-10-17" }, - "roleName": "cdk-file-publishing-role-us-east-2-newId3" + "roleName": "cdk-synthassets-file-publishing-role-us-east-1" } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_iam.CfnRole", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } }, "DefaultPolicy": { "id": "DefaultPolicy", - "path": "StagingStack489318732371us-east-2/CdkFilePublishingRole/DefaultPolicy", + "path": "StagingStack-synthassets-489318732371-us-east-1/CdkFilePublishingRole/DefaultPolicy", "children": { "Resource": { "id": "Resource", - "path": "StagingStack489318732371us-east-2/CdkFilePublishingRole/DefaultPolicy/Resource", + "path": "StagingStack-synthassets-489318732371-us-east-1/CdkFilePublishingRole/DefaultPolicy/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::IAM::Policy", "aws:cdk:cloudformation:props": { @@ -251,29 +277,29 @@ } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_iam.Policy", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_iam.Role", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } }, "BucketKey": { "id": "BucketKey", - "path": "StagingStack489318732371us-east-2/BucketKey", + "path": "StagingStack-synthassets-489318732371-us-east-1/BucketKey", "children": { "Resource": { "id": "Resource", - "path": "StagingStack489318732371us-east-2/BucketKey/Resource", + "path": "StagingStack-synthassets-489318732371-us-east-1/BucketKey/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::KMS::Key", "aws:cdk:cloudformation:props": { @@ -316,21 +342,21 @@ } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_kms.CfnKey", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } }, "Alias": { "id": "Alias", - "path": "StagingStack489318732371us-east-2/BucketKey/Alias", + "path": "StagingStack-synthassets-489318732371-us-east-1/BucketKey/Alias", "children": { "Resource": { "id": "Resource", - "path": "StagingStack489318732371us-east-2/BucketKey/Alias/Resource", + "path": "StagingStack-synthassets-489318732371-us-east-1/BucketKey/Alias/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::KMS::Alias", "aws:cdk:cloudformation:props": { - "aliasName": "alias/CdkStagingBucketKey489318732371-us-east-2-newId3", + "aliasName": "alias/cdk-synthassets-staging", "targetKeyId": { "Fn::GetAtt": [ "BucketKey7092080A", @@ -340,29 +366,29 @@ } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_kms.CfnAlias", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_kms.Alias", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_kms.Key", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } }, "CdkStagingBucket": { "id": "CdkStagingBucket", - "path": "StagingStack489318732371us-east-2/CdkStagingBucket", + "path": "StagingStack-synthassets-489318732371-us-east-1/CdkStagingBucket", "children": { "Resource": { "id": "Resource", - "path": "StagingStack489318732371us-east-2/CdkStagingBucket/Resource", + "path": "StagingStack-synthassets-489318732371-us-east-1/CdkStagingBucket/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::S3::Bucket", "aws:cdk:cloudformation:props": { @@ -381,30 +407,39 @@ } ] }, - "bucketName": "cdk-489318732371-us-east-2-newid3", + "bucketName": "cdk-synthassets-staging-489318732371-us-east-1", "lifecycleConfiguration": { "rules": [ { - "expirationInDays": 10, - "prefix": "eph-", + "noncurrentVersionExpiration": { + "noncurrentDays": 365 + }, + "status": "Enabled" + }, + { + "expirationInDays": 30, + "prefix": "handoff/", "status": "Enabled" } ] + }, + "versioningConfiguration": { + "status": "Enabled" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_s3.CfnBucket", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } }, "Policy": { "id": "Policy", - "path": "StagingStack489318732371us-east-2/CdkStagingBucket/Policy", + "path": "StagingStack-synthassets-489318732371-us-east-1/CdkStagingBucket/Policy", "children": { "Resource": { "id": "Resource", - "path": "StagingStack489318732371us-east-2/CdkStagingBucket/Policy/Resource", + "path": "StagingStack-synthassets-489318732371-us-east-1/CdkStagingBucket/Policy/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::S3::BucketPolicy", "aws:cdk:cloudformation:props": { @@ -413,6 +448,40 @@ }, "policyDocument": { "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "CdkStagingBucket1636058C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "CdkStagingBucket1636058C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": [ "s3:GetBucket*", @@ -429,7 +498,7 @@ { "Ref": "AWS::Partition" }, - ":iam::489318732371:role/cdk-hnb659fds-deploy-role-489318732371-us-east-2" + ":iam::489318732371:role/cdk-hnb659fds-deploy-role-489318732371-us-east-1" ] ] } @@ -463,26 +532,153 @@ } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_s3.CfnBucketPolicy", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_s3.BucketPolicy", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_s3.Bucket", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" + } + }, + "CdkImagePublishingRole": { + "id": "CdkImagePublishingRole", + "path": "StagingStack-synthassets-489318732371-us-east-1/CdkImagePublishingRole", + "children": { + "ImportCdkImagePublishingRole": { + "id": "ImportCdkImagePublishingRole", + "path": "StagingStack-synthassets-489318732371-us-east-1/CdkImagePublishingRole/ImportCdkImagePublishingRole", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" + } + }, + "Resource": { + "id": "Resource", + "path": "StagingStack-synthassets-489318732371-us-east-1/CdkImagePublishingRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::489318732371:root" + } + } + ], + "Version": "2012-10-17" + }, + "roleName": "cdk-synthassets-asset-publishing-role-us-east-1" + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "StagingStack-synthassets-489318732371-us-east-1/CdkImagePublishingRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "StagingStack-synthassets-489318732371-us-east-1/CdkImagePublishingRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "ecr:BatchCheckLayerAvailability", + "ecr:BatchGetImage", + "ecr:CompleteLayerUpload", + "ecr:DescribeImages", + "ecr:DescribeRepositories", + "ecr:GetDownloadUrlForLayer", + "ecr:InitiateLayerUpload", + "ecr:PutImage", + "ecr:UploadLayerPart" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "ecrasset9899265E", + "Arn" + ] + } + }, + { + "Action": "ecr:GetAuthorizationToken", + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "policyName": "CdkImagePublishingRoleDefaultPolicy7638FA58", + "roles": [ + { + "Ref": "CdkImagePublishingRole87E59CEF" + } + ] + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" + } + }, + "ecr-asset": { + "id": "ecr-asset", + "path": "StagingStack-synthassets-489318732371-us-east-1/ecr-asset", + "children": { + "Resource": { + "id": "Resource", + "path": "StagingStack-synthassets-489318732371-us-east-1/ecr-asset/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ECR::Repository", + "aws:cdk:cloudformation:props": { + "repositoryName": "ecr-asset" + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.Stack", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } }, "integ-tests": { @@ -509,34 +705,34 @@ "id": "BootstrapVersion", "path": "integ-tests/DefaultTest/DeployAssert/BootstrapVersion", "constructInfo": { - "fqn": "aws-cdk-lib.CfnParameter", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } }, "CheckBootstrapVersion": { "id": "CheckBootstrapVersion", "path": "integ-tests/DefaultTest/DeployAssert/CheckBootstrapVersion", "constructInfo": { - "fqn": "aws-cdk-lib.CfnRule", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.Stack", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" } }, "Tree": { @@ -549,8 +745,8 @@ } }, "constructInfo": { - "fqn": "aws-cdk-lib.App", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.270" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts index 89e858c17bebd..acd0de1ee3014 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts @@ -2,13 +2,14 @@ import * as path from 'path'; import * as integ from '@aws-cdk/integ-tests-alpha'; import { App, Stack } from 'aws-cdk-lib'; import * as lambda from 'aws-cdk-lib/aws-lambda'; +import * as ecr_assets from 'aws-cdk-lib/aws-ecr-assets'; import { AppStagingSynthesizer } from '../lib'; const app = new App(); -const stack = new Stack(app, 'kaizentest3', { +const stack = new Stack(app, 'synth-test', { synthesizer: AppStagingSynthesizer.defaultResources({ - appId: 'kaikai', + appId: 'synthassets', }), env: { account: '489318732371', @@ -22,6 +23,11 @@ new lambda.Function(stack, 'lambda', { runtime: lambda.Runtime.PYTHON_3_9, }); +new ecr_assets.DockerImageAsset(stack, 'ecr-asset', { + directory: path.join(__dirname, 'assets'), + assetName: 'ecr-asset', +}); + new integ.IntegTest(app, 'integ-tests', { testCases: [stack], }); From a7b61a4fe4153c6650c02718e37de549701be32d Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 3 May 2023 18:19:59 -0400 Subject: [PATCH 090/120] add assetname to ecr-assets --- .../aws-ecr-assets/lib/image-asset.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/packages/aws-cdk-lib/aws-ecr-assets/lib/image-asset.ts b/packages/aws-cdk-lib/aws-ecr-assets/lib/image-asset.ts index dbb5e8887b7af..20fa97849ecb8 100644 --- a/packages/aws-cdk-lib/aws-ecr-assets/lib/image-asset.ts +++ b/packages/aws-cdk-lib/aws-ecr-assets/lib/image-asset.ts @@ -266,6 +266,14 @@ export interface DockerImageAssetOptions extends FingerprintOptions, FileFingerp */ readonly outputs?: string[]; + /** + * Unique identifier of the docker image asset and its potential revisions. + * Required if using AppScopedStagingSynthesizer. + * + * @default - no asset name + */ + readonly assetName?: string; + /** * Cache from options to pass to the `docker build` command. * @@ -361,6 +369,14 @@ export class DockerImageAsset extends Construct implements IAsset { */ private readonly dockerOutputs?: string[]; + /** + * Unique identifier of the docker image asset and its potential revisions. + * Required if using AppScopedStagingSynthesizer. + * + * @default - no asset name + */ + private readonly assetName?: string; + /** * Cache from options to pass to the `docker build` command. */ @@ -458,6 +474,7 @@ export class DockerImageAsset extends Construct implements IAsset { const stack = Stack.of(this); this.assetPath = staging.relativeStagedPath(stack); + this.assetName = props.assetName; this.dockerBuildArgs = props.buildArgs; this.dockerBuildSecrets = props.buildSecrets; this.dockerBuildTarget = props.target; @@ -467,6 +484,7 @@ export class DockerImageAsset extends Construct implements IAsset { const location = stack.synthesizer.addDockerImageAsset({ directoryName: this.assetPath, + assetName: this.assetName, dockerBuildArgs: this.dockerBuildArgs, dockerBuildSecrets: this.dockerBuildSecrets, dockerBuildTarget: this.dockerBuildTarget, From 5290ad34c5fd7c21f0f60c17de95c34e40631862 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 4 May 2023 17:46:45 -0400 Subject: [PATCH 091/120] update integ test --- .../test/assets/Dockerfile | 3 +- .../Dockerfile | 1 - .../Dockerfile | 2 + .../index.py | 0 .../Dockerfile | 2 + .../index.py | 0 .../Dockerfile | 1 - .../manifest.json | 40 +++++- .../synth-test.assets.json | 16 +-- .../synth-test.template.json | 63 +++++++- .../integ.synthesizer.js.snapshot/tree.json | 136 +++++++++++++++--- .../test/integ.synthesizer.ts | 14 +- .../lib/api/cloudformation-deployments.ts | 1 + packages/aws-cdk/lib/api/toolkit-info.ts | 2 - packages/aws-cdk/lib/cdk-toolkit.ts | 3 +- packages/aws-cdk/lib/util/work-graph.ts | 5 +- packages/aws-cdk/test/cdk-toolkit.test.ts | 2 +- packages/aws-cdk/test/deploy.test.ts | 2 +- 18 files changed, 235 insertions(+), 58 deletions(-) delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20/Dockerfile create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/Dockerfile rename packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/{asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20 => asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622}/index.py (100%) create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/Dockerfile rename packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/{asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c => asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650}/index.py (100%) delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c/Dockerfile diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/assets/Dockerfile b/packages/@aws-cdk/app-staging-synthesizer/test/assets/Dockerfile index d2ecc8392b4da..4a015204a5983 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/assets/Dockerfile +++ b/packages/@aws-cdk/app-staging-synthesizer/test/assets/Dockerfile @@ -1 +1,2 @@ -FROM public.ecr.aws/lambda/python:3.7 \ No newline at end of file +FROM public.ecr.aws/lambda/python:3.10 +CMD echo hello world \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20/Dockerfile b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20/Dockerfile deleted file mode 100644 index d2ecc8392b4da..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20/Dockerfile +++ /dev/null @@ -1 +0,0 @@ -FROM public.ecr.aws/lambda/python:3.7 \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/Dockerfile b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/Dockerfile new file mode 100644 index 0000000000000..4a015204a5983 --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/Dockerfile @@ -0,0 +1,2 @@ +FROM public.ecr.aws/lambda/python:3.10 +CMD echo hello world \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20/index.py b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/index.py similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20/index.py rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/index.py diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/Dockerfile b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/Dockerfile new file mode 100644 index 0000000000000..4a015204a5983 --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/Dockerfile @@ -0,0 +1,2 @@ +FROM public.ecr.aws/lambda/python:3.10 +CMD echo hello world \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c/index.py b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/index.py similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c/index.py rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/index.py diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c/Dockerfile b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c/Dockerfile deleted file mode 100644 index d2ecc8392b4da..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c/Dockerfile +++ /dev/null @@ -1 +0,0 @@ -FROM public.ecr.aws/lambda/python:3.7 \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json index 987ec4e01a1ef..6209d2d7b7354 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json @@ -17,7 +17,7 @@ "additionalDependencies": [ "synth-test.assets" ], - "stackTemplateAssetObjectUrl": "s3://cdk-synthassets-staging-489318732371-us-east-1/2a29d40e386de7d4837131194a3dbdc589477e83c31080a93901474e06d389fa.json", + "stackTemplateAssetObjectUrl": "s3://cdk-synthassets-staging-489318732371-us-east-1/cfc9d13f0af5f1be9708022bc174c46ec539fe35b16b31cf5b337f70ddb47bc6.json", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-hnb659fds-cfn-exec-role-489318732371-us-east-1", "lookupRole": { "arn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-hnb659fds-lookup-role-489318732371-us-east-1" @@ -28,16 +28,46 @@ "synth-test.assets" ], "metadata": { - "/synth-test/lambda/ServiceRole/Resource": [ + "/synth-test/lambda-s3/ServiceRole/Resource": [ { "type": "aws:cdk:logicalId", - "data": "lambdaServiceRole494E4CA6" + "data": "lambdas3ServiceRoleC9EDE33A" } ], - "/synth-test/lambda/Resource": [ + "/synth-test/lambda-s3/Resource": [ { "type": "aws:cdk:logicalId", - "data": "lambda8B5974B5" + "data": "lambdas342CE2BBD" + } + ], + "/synth-test/lambda-ecr/ServiceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "lambdaecrServiceRoleFC667F6F" + } + ], + "/synth-test/lambda-ecr/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "lambdaecr8D55A032" + } + ], + "lambdaServiceRole494E4CA6": [ + { + "type": "aws:cdk:logicalId", + "data": "lambdaServiceRole494E4CA6", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] + } + ], + "lambda8B5974B5": [ + { + "type": "aws:cdk:logicalId", + "data": "lambda8B5974B5", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] } ] }, diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synth-test.assets.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synth-test.assets.json index c69fb839a43fe..dbfe70324a21a 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synth-test.assets.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synth-test.assets.json @@ -1,21 +1,21 @@ { "version": "31.0.0", "files": { - "0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20": { + "68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650": { "source": { - "path": "asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20", + "path": "asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650", "packaging": "zip" }, "destinations": { "489318732371-us-east-1": { "bucketName": "cdk-synthassets-staging-489318732371-us-east-1", - "objectKey": "0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20.zip", + "objectKey": "68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650.zip", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-synthassets-file-publishing-role-us-east-1" } } }, - "2a29d40e386de7d4837131194a3dbdc589477e83c31080a93901474e06d389fa": { + "cfc9d13f0af5f1be9708022bc174c46ec539fe35b16b31cf5b337f70ddb47bc6": { "source": { "path": "synth-test.template.json", "packaging": "file" @@ -23,7 +23,7 @@ "destinations": { "489318732371-us-east-1": { "bucketName": "cdk-synthassets-staging-489318732371-us-east-1", - "objectKey": "2a29d40e386de7d4837131194a3dbdc589477e83c31080a93901474e06d389fa.json", + "objectKey": "cfc9d13f0af5f1be9708022bc174c46ec539fe35b16b31cf5b337f70ddb47bc6.json", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-synthassets-file-publishing-role-us-east-1" } @@ -31,14 +31,14 @@ } }, "dockerImages": { - "ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c": { + "16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622": { "source": { - "directory": "asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c" + "directory": "asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622" }, "destinations": { "489318732371-us-east-1": { "repositoryName": "ecr-asset", - "imageTag": "ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c", + "imageTag": "16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-synthassets-asset-publishing-role-us-east-1" } diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synth-test.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synth-test.template.json index 057913793f378..7115b687f43f5 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synth-test.template.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synth-test.template.json @@ -1,6 +1,6 @@ { "Resources": { - "lambdaServiceRole494E4CA6": { + "lambdas3ServiceRoleC9EDE33A": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { @@ -31,24 +31,75 @@ ] } }, - "lambda8B5974B5": { + "lambdas342CE2BBD": { "Type": "AWS::Lambda::Function", "Properties": { "Code": { "S3Bucket": "cdk-synthassets-staging-489318732371-us-east-1", - "S3Key": "0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20.zip" + "S3Key": "68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650.zip" }, "Role": { "Fn::GetAtt": [ - "lambdaServiceRole494E4CA6", + "lambdas3ServiceRoleC9EDE33A", "Arn" ] }, "Handler": "index.handler", - "Runtime": "python3.9" + "Runtime": "python3.10" }, "DependsOn": [ - "lambdaServiceRole494E4CA6" + "lambdas3ServiceRoleC9EDE33A" + ] + }, + "lambdaecrServiceRoleFC667F6F": { + "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" + ] + ] + } + ] + } + }, + "lambdaecr8D55A032": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ImageUri": { + "Fn::Sub": "489318732371.dkr.ecr.us-east-1.${AWS::URLSuffix}/ecr-asset:16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622" + } + }, + "Role": { + "Fn::GetAtt": [ + "lambdaecrServiceRoleFC667F6F", + "Arn" + ] + }, + "PackageType": "Image" + }, + "DependsOn": [ + "lambdaecrServiceRoleFC667F6F" ] } } diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json index 33657554bdbe3..3f5e077678c1b 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json @@ -8,17 +8,17 @@ "id": "synth-test", "path": "synth-test", "children": { - "lambda": { - "id": "lambda", - "path": "synth-test/lambda", + "lambda-s3": { + "id": "lambda-s3", + "path": "synth-test/lambda-s3", "children": { "ServiceRole": { "id": "ServiceRole", - "path": "synth-test/lambda/ServiceRole", + "path": "synth-test/lambda-s3/ServiceRole", "children": { "ImportServiceRole": { "id": "ImportServiceRole", - "path": "synth-test/lambda/ServiceRole/ImportServiceRole", + "path": "synth-test/lambda-s3/ServiceRole/ImportServiceRole", "constructInfo": { "fqn": "constructs.Construct", "version": "10.1.270" @@ -26,7 +26,7 @@ }, "Resource": { "id": "Resource", - "path": "synth-test/lambda/ServiceRole/Resource", + "path": "synth-test/lambda-s3/ServiceRole/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::IAM::Role", "aws:cdk:cloudformation:props": { @@ -71,11 +71,11 @@ }, "Code": { "id": "Code", - "path": "synth-test/lambda/Code", + "path": "synth-test/lambda-s3/Code", "children": { "Stage": { "id": "Stage", - "path": "synth-test/lambda/Code/Stage", + "path": "synth-test/lambda-s3/Code/Stage", "constructInfo": { "fqn": "constructs.Construct", "version": "10.1.270" @@ -83,7 +83,7 @@ }, "AssetBucket": { "id": "AssetBucket", - "path": "synth-test/lambda/Code/AssetBucket", + "path": "synth-test/lambda-s3/Code/AssetBucket", "constructInfo": { "fqn": "constructs.Construct", "version": "10.1.270" @@ -97,22 +97,22 @@ }, "Resource": { "id": "Resource", - "path": "synth-test/lambda/Resource", + "path": "synth-test/lambda-s3/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::Lambda::Function", "aws:cdk:cloudformation:props": { "code": { "s3Bucket": "cdk-synthassets-staging-489318732371-us-east-1", - "s3Key": "0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20.zip" + "s3Key": "68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650.zip" }, "role": { "Fn::GetAtt": [ - "lambdaServiceRole494E4CA6", + "lambdas3ServiceRoleC9EDE33A", "Arn" ] }, "handler": "index.handler", - "runtime": "python3.9" + "runtime": "python3.10" } }, "constructInfo": { @@ -126,21 +126,113 @@ "version": "10.1.270" } }, - "ecr-asset": { - "id": "ecr-asset", - "path": "synth-test/ecr-asset", + "lambda-ecr": { + "id": "lambda-ecr", + "path": "synth-test/lambda-ecr", "children": { - "Staging": { - "id": "Staging", - "path": "synth-test/ecr-asset/Staging", + "ServiceRole": { + "id": "ServiceRole", + "path": "synth-test/lambda-ecr/ServiceRole", + "children": { + "ImportServiceRole": { + "id": "ImportServiceRole", + "path": "synth-test/lambda-ecr/ServiceRole/ImportServiceRole", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" + } + }, + "Resource": { + "id": "Resource", + "path": "synth-test/lambda-ecr/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": "constructs.Construct", + "version": "10.1.270" + } + } + }, "constructInfo": { "fqn": "constructs.Construct", "version": "10.1.270" } }, - "Repository": { - "id": "Repository", - "path": "synth-test/ecr-asset/Repository", + "AssetImage": { + "id": "AssetImage", + "path": "synth-test/lambda-ecr/AssetImage", + "children": { + "Staging": { + "id": "Staging", + "path": "synth-test/lambda-ecr/AssetImage/Staging", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" + } + }, + "Repository": { + "id": "Repository", + "path": "synth-test/lambda-ecr/AssetImage/Repository", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" + } + }, + "Resource": { + "id": "Resource", + "path": "synth-test/lambda-ecr/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Function", + "aws:cdk:cloudformation:props": { + "code": { + "imageUri": { + "Fn::Sub": "489318732371.dkr.ecr.us-east-1.${AWS::URLSuffix}/ecr-asset:16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622" + } + }, + "role": { + "Fn::GetAtt": [ + "lambdaecrServiceRoleFC667F6F", + "Arn" + ] + }, + "packageType": "Image" + } + }, "constructInfo": { "fqn": "constructs.Construct", "version": "10.1.270" diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts index acd0de1ee3014..abdb18a3bd6f0 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts @@ -2,7 +2,6 @@ import * as path from 'path'; import * as integ from '@aws-cdk/integ-tests-alpha'; import { App, Stack } from 'aws-cdk-lib'; import * as lambda from 'aws-cdk-lib/aws-lambda'; -import * as ecr_assets from 'aws-cdk-lib/aws-ecr-assets'; import { AppStagingSynthesizer } from '../lib'; const app = new App(); @@ -17,15 +16,18 @@ const stack = new Stack(app, 'synth-test', { }, }); -new lambda.Function(stack, 'lambda', { +new lambda.Function(stack, 'lambda-s3', { code: lambda.AssetCode.fromAsset(path.join(__dirname, 'assets')), handler: 'index.handler', - runtime: lambda.Runtime.PYTHON_3_9, + runtime: lambda.Runtime.PYTHON_3_10, }); -new ecr_assets.DockerImageAsset(stack, 'ecr-asset', { - directory: path.join(__dirname, 'assets'), - assetName: 'ecr-asset', +new lambda.Function(stack, 'lambda-ecr', { + code: lambda.EcrImageCode.fromAssetImage(path.join(__dirname, 'assets'), { + assetName: 'ecr-asset', + }), + handler: lambda.Handler.FROM_IMAGE, + runtime: lambda.Runtime.FROM_IMAGE, }); new integ.IntegTest(app, 'integ-tests', { diff --git a/packages/aws-cdk/lib/api/cloudformation-deployments.ts b/packages/aws-cdk/lib/api/cloudformation-deployments.ts index 9576aed8e9d44..5c7cd60c0ef75 100644 --- a/packages/aws-cdk/lib/api/cloudformation-deployments.ts +++ b/packages/aws-cdk/lib/api/cloudformation-deployments.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-console */ import * as cxapi from '@aws-cdk/cx-api'; import { AssetManifest } from 'cdk-assets'; import { Mode } from './aws-auth/credentials'; diff --git a/packages/aws-cdk/lib/api/toolkit-info.ts b/packages/aws-cdk/lib/api/toolkit-info.ts index 2999b996b9d60..f0f66753843ee 100644 --- a/packages/aws-cdk/lib/api/toolkit-info.ts +++ b/packages/aws-cdk/lib/api/toolkit-info.ts @@ -42,9 +42,7 @@ export abstract class ToolkitInfo { public static async lookup(environment: cxapi.Environment, sdk: ISDK, stackName: string | undefined): Promise { const cfn = sdk.cloudFormation(); - console.log('here1'); const stack = await stabilizeStack(cfn, stackName ?? DEFAULT_TOOLKIT_STACK_NAME); - console.log('here2'); if (!stack) { debug('The environment %s doesn\'t have the CDK toolkit stack (%s) installed. Use %s to setup your environment for use with the toolkit.', environment.name, stackName, chalk.blue(`cdk bootstrap "${environment.name}"`)); diff --git a/packages/aws-cdk/lib/cdk-toolkit.ts b/packages/aws-cdk/lib/cdk-toolkit.ts index b14237d309667..2005e2273d9c8 100644 --- a/packages/aws-cdk/lib/cdk-toolkit.ts +++ b/packages/aws-cdk/lib/cdk-toolkit.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-console */ import * as path from 'path'; import { format } from 'util'; import * as cxapi from '@aws-cdk/cx-api'; @@ -23,8 +24,8 @@ import { deserializeStructure, serializeStructure } from './serialize'; import { Configuration, PROJECT_CONFIG } from './settings'; import { numberFromBool, partition } from './util'; import { validateSnsTopicArn } from './util/validate-notification-arn'; -import { environmentsFromDescriptors, globEnvironmentsFromStacks, looksLikeGlob } from '../lib/api/cxapp/environments'; import { AssetBuildNode, AssetPublishNode, StackNode } from './util/work-graph-types'; +import { environmentsFromDescriptors, globEnvironmentsFromStacks, looksLikeGlob } from '../lib/api/cxapp/environments'; export interface CdkToolkitProps { diff --git a/packages/aws-cdk/lib/util/work-graph.ts b/packages/aws-cdk/lib/util/work-graph.ts index b7b9fc48f2744..580dedf4b3b90 100644 --- a/packages/aws-cdk/lib/util/work-graph.ts +++ b/packages/aws-cdk/lib/util/work-graph.ts @@ -28,9 +28,8 @@ export class WorkGraph { // ignore tree artifacts continue; } else if (cxapi.NestedCloudAssemblyArtifact.isNestedCloudAssemblyArtifact(artifact)) { - console.log(artifact.directoryName); - const assembly = new cxapi.CloudAssembly(artifact.directoryName); - console.log(assembly.directory); + console.log('NESTED!!!'); + const assembly = new cxapi.CloudAssembly(artifact.fullPath); const nestedGraph = WorkGraph.fromCloudArtifacts(assembly.artifacts, prebuildAssets); graph.addNodes(...Object.values(nestedGraph.nodes)); } diff --git a/packages/aws-cdk/test/cdk-toolkit.test.ts b/packages/aws-cdk/test/cdk-toolkit.test.ts index 5a2c9f49d2b95..40c892672a22c 100644 --- a/packages/aws-cdk/test/cdk-toolkit.test.ts +++ b/packages/aws-cdk/test/cdk-toolkit.test.ts @@ -1,4 +1,3 @@ -/* eslint-disable import/order */ // We need to mock the chokidar library, used by 'cdk watch' const mockChokidarWatcherOn = jest.fn(); const fakeChokidarWatcher = { @@ -585,6 +584,7 @@ describe('deploy', () => { expect(mockSynthesize).not.toHaveBeenCalled(); }); + // eslint-disable-next-line jest/no-commented-out-tests // test('can disable asset parallelism', async () => { // // GIVEN // cloudExecutable = new MockCloudExecutable({ diff --git a/packages/aws-cdk/test/deploy.test.ts b/packages/aws-cdk/test/deploy.test.ts index 05cd54d1011bf..12012c7a74541 100644 --- a/packages/aws-cdk/test/deploy.test.ts +++ b/packages/aws-cdk/test/deploy.test.ts @@ -1,7 +1,7 @@ +import * as path from 'path'; import * as cxapi from '@aws-cdk/cx-api'; import { deployArtifacts } from '../lib/deploy'; import { AssetBuildNode, AssetPublishNode, StackNode } from '../lib/util/work-graph-types'; -import path = require('path'); const ASSET_MANIFEST_ARTIFACT_SYM = Symbol.for('@aws-cdk/cx-api.AssetManifestArtifact'); const CLOUDFORMATION_STACK_ARTIFACT_SYM = Symbol.for('@aws-cdk/cx-api.CloudFormationStackArtifact'); From 0e25c13b19b085c07c4a5932cff59e07174b19b8 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 4 May 2023 17:52:01 -0400 Subject: [PATCH 092/120] more test updates --- ...k-envAgnostic-ACCOUNT-REGION.template.json | 6 +- .../app-scoped-staging-test.assets.json | 45 ------ .../app-scoped-staging-test.template.json | 57 ------- .../Dockerfile | 1 - .../Dockerfile | 2 + .../index.py | 0 .../Dockerfile | 2 + .../index.py | 0 .../Dockerfile | 1 - .../env-agnostic-test.assets.json | 18 +-- .../env-agnostic-test.template.json | 63 +++++++- .../manifest.json | 44 +++++- .../tree.json | 148 ++++++++++++++---- .../test/integ.env-agnostic-synth.ts | 18 +-- 14 files changed, 239 insertions(+), 166 deletions(-) delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/app-scoped-staging-test.assets.json delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/app-scoped-staging-test.template.json delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20/Dockerfile create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/Dockerfile rename packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/{asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20 => asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622}/index.py (100%) create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/Dockerfile rename packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/{asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c => asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650}/index.py (100%) delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c/Dockerfile diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/StagingStack-envAgnostic-ACCOUNT-REGION.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/StagingStack-envAgnostic-ACCOUNT-REGION.template.json index 44b52349255a3..4b13a92207537 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/StagingStack-envAgnostic-ACCOUNT-REGION.template.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/StagingStack-envAgnostic-ACCOUNT-REGION.template.json @@ -417,7 +417,7 @@ "Effect": "Allow", "Resource": { "Fn::GetAtt": [ - "ecrasset9899265E", + "ecrasset155BFF0C3", "Arn" ] } @@ -438,10 +438,10 @@ ] } }, - "ecrasset9899265E": { + "ecrasset155BFF0C3": { "Type": "AWS::ECR::Repository", "Properties": { - "RepositoryName": "ecr-asset" + "RepositoryName": "ecr-asset-1" }, "UpdateReplacePolicy": "Retain", "DeletionPolicy": "Retain" diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/app-scoped-staging-test.assets.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/app-scoped-staging-test.assets.json deleted file mode 100644 index 36271e5fcf7b6..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/app-scoped-staging-test.assets.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "version": "31.0.0", - "files": { - "0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20": { - "source": { - "path": "asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20", - "packaging": "zip" - }, - "destinations": { - "current_account-current_region": { - "bucketName": "cdk-envagnostic-staging-${AWS::AccountId}-${AWS::Region}", - "objectKey": "handoff/0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20.zip", - "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-envagnostic-file-publishing-role-${AWS::Region}" - } - } - }, - "7e5e10cdde60d065b7d36115e2aaaf9b1341fccdc7abef3db56fbf2aff9491c1": { - "source": { - "path": "app-scoped-staging-test.template.json", - "packaging": "file" - }, - "destinations": { - "current_account-current_region": { - "bucketName": "cdk-envagnostic-staging-${AWS::AccountId}-${AWS::Region}", - "objectKey": "7e5e10cdde60d065b7d36115e2aaaf9b1341fccdc7abef3db56fbf2aff9491c1.json", - "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-envagnostic-file-publishing-role-${AWS::Region}" - } - } - } - }, - "dockerImages": { - "ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c": { - "source": { - "directory": "asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c" - }, - "destinations": { - "current_account-current_region": { - "repositoryName": "ecr-asset", - "imageTag": "ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c", - "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-envagnostic-asset-publishing-role-${AWS::Region}" - } - } - } - } -} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/app-scoped-staging-test.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/app-scoped-staging-test.template.json deleted file mode 100644 index 25c58373bcc35..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/app-scoped-staging-test.template.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "Resources": { - "lambdaServiceRole494E4CA6": { - "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" - ] - ] - } - ] - } - }, - "lambda8B5974B5": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "S3Bucket": { - "Fn::Sub": "cdk-envagnostic-staging-${AWS::AccountId}-${AWS::Region}" - }, - "S3Key": "handoff/0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20.zip" - }, - "Role": { - "Fn::GetAtt": [ - "lambdaServiceRole494E4CA6", - "Arn" - ] - }, - "Handler": "index.handler", - "Runtime": "python3.9" - }, - "DependsOn": [ - "lambdaServiceRole494E4CA6" - ] - } - } -} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20/Dockerfile b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20/Dockerfile deleted file mode 100644 index d2ecc8392b4da..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20/Dockerfile +++ /dev/null @@ -1 +0,0 @@ -FROM public.ecr.aws/lambda/python:3.7 \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/Dockerfile b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/Dockerfile new file mode 100644 index 0000000000000..4a015204a5983 --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/Dockerfile @@ -0,0 +1,2 @@ +FROM public.ecr.aws/lambda/python:3.10 +CMD echo hello world \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20/index.py b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/index.py similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20/index.py rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/index.py diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/Dockerfile b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/Dockerfile new file mode 100644 index 0000000000000..4a015204a5983 --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/Dockerfile @@ -0,0 +1,2 @@ +FROM public.ecr.aws/lambda/python:3.10 +CMD echo hello world \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c/index.py b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/index.py similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c/index.py rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/index.py diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c/Dockerfile b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c/Dockerfile deleted file mode 100644 index d2ecc8392b4da..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c/Dockerfile +++ /dev/null @@ -1 +0,0 @@ -FROM public.ecr.aws/lambda/python:3.7 \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/env-agnostic-test.assets.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/env-agnostic-test.assets.json index b515d55812aa8..2aa1e4b20e498 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/env-agnostic-test.assets.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/env-agnostic-test.assets.json @@ -1,20 +1,20 @@ { "version": "31.0.0", "files": { - "0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20": { + "68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650": { "source": { - "path": "asset.0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20", + "path": "asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650", "packaging": "zip" }, "destinations": { "current_account-current_region": { "bucketName": "cdk-envagnostic-staging-${AWS::AccountId}-${AWS::Region}", - "objectKey": "handoff/0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20.zip", + "objectKey": "68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650.zip", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-envagnostic-file-publishing-role-${AWS::Region}" } } }, - "7e5e10cdde60d065b7d36115e2aaaf9b1341fccdc7abef3db56fbf2aff9491c1": { + "d1e269d7a168aa54c7ccfb772c9004bc0ff1fee705c66cfb6592a07ef81cdf6e": { "source": { "path": "env-agnostic-test.template.json", "packaging": "file" @@ -22,21 +22,21 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-envagnostic-staging-${AWS::AccountId}-${AWS::Region}", - "objectKey": "7e5e10cdde60d065b7d36115e2aaaf9b1341fccdc7abef3db56fbf2aff9491c1.json", + "objectKey": "d1e269d7a168aa54c7ccfb772c9004bc0ff1fee705c66cfb6592a07ef81cdf6e.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-envagnostic-file-publishing-role-${AWS::Region}" } } } }, "dockerImages": { - "ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c": { + "16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622": { "source": { - "directory": "asset.ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c" + "directory": "asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622" }, "destinations": { "current_account-current_region": { - "repositoryName": "ecr-asset", - "imageTag": "ad56d32734340e2f3b34d68669d4911e694b2a8e122ba55eee21ebae5e0a7b4c", + "repositoryName": "ecr-asset-1", + "imageTag": "16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-envagnostic-asset-publishing-role-${AWS::Region}" } } diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/env-agnostic-test.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/env-agnostic-test.template.json index 25c58373bcc35..d1eaae9842cf2 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/env-agnostic-test.template.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/env-agnostic-test.template.json @@ -1,6 +1,6 @@ { "Resources": { - "lambdaServiceRole494E4CA6": { + "lambdas3ServiceRoleC9EDE33A": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { @@ -31,26 +31,77 @@ ] } }, - "lambda8B5974B5": { + "lambdas342CE2BBD": { "Type": "AWS::Lambda::Function", "Properties": { "Code": { "S3Bucket": { "Fn::Sub": "cdk-envagnostic-staging-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "handoff/0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20.zip" + "S3Key": "68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650.zip" }, "Role": { "Fn::GetAtt": [ - "lambdaServiceRole494E4CA6", + "lambdas3ServiceRoleC9EDE33A", "Arn" ] }, "Handler": "index.handler", - "Runtime": "python3.9" + "Runtime": "python3.10" }, "DependsOn": [ - "lambdaServiceRole494E4CA6" + "lambdas3ServiceRoleC9EDE33A" + ] + }, + "lambdaecrServiceRoleFC667F6F": { + "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" + ] + ] + } + ] + } + }, + "lambdaecr8D55A032": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ImageUri": { + "Fn::Sub": "${AWS::AccountId}.dkr.ecr.${AWS::Region}.${AWS::URLSuffix}/ecr-asset-1:16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622" + } + }, + "Role": { + "Fn::GetAtt": [ + "lambdaecrServiceRoleFC667F6F", + "Arn" + ] + }, + "PackageType": "Image" + }, + "DependsOn": [ + "lambdaecrServiceRoleFC667F6F" ] } } diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/manifest.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/manifest.json index 8f02357e8516c..0c40685bb9e5c 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/manifest.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/manifest.json @@ -17,7 +17,7 @@ "additionalDependencies": [ "env-agnostic-test.assets" ], - "stackTemplateAssetObjectUrl": "s3://cdk-envagnostic-staging-${AWS::AccountId}-${AWS::Region}/7e5e10cdde60d065b7d36115e2aaaf9b1341fccdc7abef3db56fbf2aff9491c1.json", + "stackTemplateAssetObjectUrl": "s3://cdk-envagnostic-staging-${AWS::AccountId}-${AWS::Region}/d1e269d7a168aa54c7ccfb772c9004bc0ff1fee705c66cfb6592a07ef81cdf6e.json", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", "lookupRole": { "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}" @@ -28,16 +28,46 @@ "env-agnostic-test.assets" ], "metadata": { - "/env-agnostic-test/lambda/ServiceRole/Resource": [ + "/env-agnostic-test/lambda-s3/ServiceRole/Resource": [ { "type": "aws:cdk:logicalId", - "data": "lambdaServiceRole494E4CA6" + "data": "lambdas3ServiceRoleC9EDE33A" } ], - "/env-agnostic-test/lambda/Resource": [ + "/env-agnostic-test/lambda-s3/Resource": [ { "type": "aws:cdk:logicalId", - "data": "lambda8B5974B5" + "data": "lambdas342CE2BBD" + } + ], + "/env-agnostic-test/lambda-ecr/ServiceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "lambdaecrServiceRoleFC667F6F" + } + ], + "/env-agnostic-test/lambda-ecr/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "lambdaecr8D55A032" + } + ], + "lambdaServiceRole494E4CA6": [ + { + "type": "aws:cdk:logicalId", + "data": "lambdaServiceRole494E4CA6", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] + } + ], + "lambda8B5974B5": [ + { + "type": "aws:cdk:logicalId", + "data": "lambda8B5974B5", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] } ] }, @@ -102,10 +132,10 @@ "data": "CdkImagePublishingRoleDefaultPolicy7638FA58" } ], - "/StagingStack-envAgnostic-ACCOUNT-REGION/ecr-asset/Resource": [ + "/StagingStack-envAgnostic-ACCOUNT-REGION/ecr-asset-1/Resource": [ { "type": "aws:cdk:logicalId", - "data": "ecrasset9899265E" + "data": "ecrasset155BFF0C3" } ] }, diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/tree.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/tree.json index f7e6bf19310de..7aa28bc7ccd5f 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/tree.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/tree.json @@ -8,17 +8,17 @@ "id": "env-agnostic-test", "path": "env-agnostic-test", "children": { - "lambda": { - "id": "lambda", - "path": "env-agnostic-test/lambda", + "lambda-s3": { + "id": "lambda-s3", + "path": "env-agnostic-test/lambda-s3", "children": { "ServiceRole": { "id": "ServiceRole", - "path": "env-agnostic-test/lambda/ServiceRole", + "path": "env-agnostic-test/lambda-s3/ServiceRole", "children": { "ImportServiceRole": { "id": "ImportServiceRole", - "path": "env-agnostic-test/lambda/ServiceRole/ImportServiceRole", + "path": "env-agnostic-test/lambda-s3/ServiceRole/ImportServiceRole", "constructInfo": { "fqn": "constructs.Construct", "version": "10.1.270" @@ -26,7 +26,7 @@ }, "Resource": { "id": "Resource", - "path": "env-agnostic-test/lambda/ServiceRole/Resource", + "path": "env-agnostic-test/lambda-s3/ServiceRole/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::IAM::Role", "aws:cdk:cloudformation:props": { @@ -71,11 +71,11 @@ }, "Code": { "id": "Code", - "path": "env-agnostic-test/lambda/Code", + "path": "env-agnostic-test/lambda-s3/Code", "children": { "Stage": { "id": "Stage", - "path": "env-agnostic-test/lambda/Code/Stage", + "path": "env-agnostic-test/lambda-s3/Code/Stage", "constructInfo": { "fqn": "constructs.Construct", "version": "10.1.270" @@ -83,7 +83,7 @@ }, "AssetBucket": { "id": "AssetBucket", - "path": "env-agnostic-test/lambda/Code/AssetBucket", + "path": "env-agnostic-test/lambda-s3/Code/AssetBucket", "constructInfo": { "fqn": "constructs.Construct", "version": "10.1.270" @@ -97,7 +97,7 @@ }, "Resource": { "id": "Resource", - "path": "env-agnostic-test/lambda/Resource", + "path": "env-agnostic-test/lambda-s3/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::Lambda::Function", "aws:cdk:cloudformation:props": { @@ -105,16 +105,16 @@ "s3Bucket": { "Fn::Sub": "cdk-envagnostic-staging-${AWS::AccountId}-${AWS::Region}" }, - "s3Key": "handoff/0e2299f428e1920c62a5ab9919dbd4f89f38d8d13cf7187abeadd047bd83ce20.zip" + "s3Key": "68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650.zip" }, "role": { "Fn::GetAtt": [ - "lambdaServiceRole494E4CA6", + "lambdas3ServiceRoleC9EDE33A", "Arn" ] }, "handler": "index.handler", - "runtime": "python3.9" + "runtime": "python3.10" } }, "constructInfo": { @@ -128,21 +128,113 @@ "version": "10.1.270" } }, - "ecr-asset": { - "id": "ecr-asset", - "path": "env-agnostic-test/ecr-asset", + "lambda-ecr": { + "id": "lambda-ecr", + "path": "env-agnostic-test/lambda-ecr", "children": { - "Staging": { - "id": "Staging", - "path": "env-agnostic-test/ecr-asset/Staging", + "ServiceRole": { + "id": "ServiceRole", + "path": "env-agnostic-test/lambda-ecr/ServiceRole", + "children": { + "ImportServiceRole": { + "id": "ImportServiceRole", + "path": "env-agnostic-test/lambda-ecr/ServiceRole/ImportServiceRole", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" + } + }, + "Resource": { + "id": "Resource", + "path": "env-agnostic-test/lambda-ecr/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": "constructs.Construct", + "version": "10.1.270" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" + } + }, + "AssetImage": { + "id": "AssetImage", + "path": "env-agnostic-test/lambda-ecr/AssetImage", + "children": { + "Staging": { + "id": "Staging", + "path": "env-agnostic-test/lambda-ecr/AssetImage/Staging", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" + } + }, + "Repository": { + "id": "Repository", + "path": "env-agnostic-test/lambda-ecr/AssetImage/Repository", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.270" + } + } + }, "constructInfo": { "fqn": "constructs.Construct", "version": "10.1.270" } }, - "Repository": { - "id": "Repository", - "path": "env-agnostic-test/ecr-asset/Repository", + "Resource": { + "id": "Resource", + "path": "env-agnostic-test/lambda-ecr/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Function", + "aws:cdk:cloudformation:props": { + "code": { + "imageUri": { + "Fn::Sub": "${AWS::AccountId}.dkr.ecr.${AWS::Region}.${AWS::URLSuffix}/ecr-asset-1:16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622" + } + }, + "role": { + "Fn::GetAtt": [ + "lambdaecrServiceRoleFC667F6F", + "Arn" + ] + }, + "packageType": "Image" + } + }, "constructInfo": { "fqn": "constructs.Construct", "version": "10.1.270" @@ -720,7 +812,7 @@ "Effect": "Allow", "Resource": { "Fn::GetAtt": [ - "ecrasset9899265E", + "ecrasset155BFF0C3", "Arn" ] } @@ -758,17 +850,17 @@ "version": "10.1.270" } }, - "ecr-asset": { - "id": "ecr-asset", - "path": "StagingStack-envAgnostic-ACCOUNT-REGION/ecr-asset", + "ecr-asset-1": { + "id": "ecr-asset-1", + "path": "StagingStack-envAgnostic-ACCOUNT-REGION/ecr-asset-1", "children": { "Resource": { "id": "Resource", - "path": "StagingStack-envAgnostic-ACCOUNT-REGION/ecr-asset/Resource", + "path": "StagingStack-envAgnostic-ACCOUNT-REGION/ecr-asset-1/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::ECR::Repository", "aws:cdk:cloudformation:props": { - "repositoryName": "ecr-asset" + "repositoryName": "ecr-asset-1" } }, "constructInfo": { diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.ts b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.ts index fade3912534fd..35539f8d541ed 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.ts @@ -3,7 +3,6 @@ import * as integ from '@aws-cdk/integ-tests-alpha'; import { App, Stack } from 'aws-cdk-lib'; import * as lambda from 'aws-cdk-lib/aws-lambda'; import { AppStagingSynthesizer } from '../lib'; -import * as ecr_assets from 'aws-cdk-lib/aws-ecr-assets'; const app = new App(); @@ -13,17 +12,18 @@ const stack = new Stack(app, 'env-agnostic-test', { }), }); -new lambda.Function(stack, 'lambda', { - code: lambda.AssetCode.fromAsset(path.join(__dirname, 'assets'), { - ephemeral: true, - }), +new lambda.Function(stack, 'lambda-s3', { + code: lambda.AssetCode.fromAsset(path.join(__dirname, 'assets')), handler: 'index.handler', - runtime: lambda.Runtime.PYTHON_3_9, + runtime: lambda.Runtime.PYTHON_3_10, }); -new ecr_assets.DockerImageAsset(stack, 'ecr-asset', { - directory: path.join(__dirname, 'assets'), - assetName: 'ecr-asset-1', +new lambda.Function(stack, 'lambda-ecr', { + code: lambda.EcrImageCode.fromAssetImage(path.join(__dirname, 'assets'), { + assetName: 'ecr-asset-1', + }), + handler: lambda.Handler.FROM_IMAGE, + runtime: lambda.Runtime.FROM_IMAGE, }); new integ.IntegTest(app, 'integ-tests', { From 21564fe3568437486130da8cf4b97be6909fad54 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Fri, 5 May 2023 17:05:26 -0400 Subject: [PATCH 093/120] update sto ecr asets --- .../app-staging-synthesizer/lib/app-staging-synthesizer.ts | 3 +-- .../app-staging-synthesizer/lib/default-staging-stack.ts | 2 +- .../app-staging-synthesizer/test/integ.synthesizer.ts | 4 ++-- packages/aws-cdk-lib/aws-ecr-assets/lib/image-asset.ts | 2 +- .../core/lib/stack-synthesizers/asset-manifest-builder.ts | 3 ++- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts index 4c724c2d1189a..b65a0c4f416a8 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts @@ -295,7 +295,6 @@ interface BoundAppStagingSynthesizerProps { readonly lookupRole: BootstrapRole; } - class BoundAppStagingSynthesizer extends StackSynthesizer implements IBoundAppStagingSynthesizer { private readonly stagingStack: IStagingStack; private readonly assetManifest = new AssetManifestBuilder(); @@ -320,7 +319,7 @@ class BoundAppStagingSynthesizer extends StackSynthesizer implements IBoundAppSt const templateAssetSource = this.synthesizeTemplate(session, this.props.lookupRole?._arnForCloudAssembly()); const templateAsset = this.addFileAsset(templateAssetSource); - const assetManifestId = this.assetManifest.emitManifest(this.boundStack, session, {} /*[this.stagingStack.dependencyStack.artifactId]*/); + const assetManifestId = this.assetManifest.emitManifest(this.boundStack, session); const lookupRoleArn = this.props.lookupRole?._arnForCloudAssembly(); diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts index 5870a85f64220..aaccfbea058fa 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts @@ -334,7 +334,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { // Create image publishing role if it doesn't exist this.ensureImagePublishingRole(); - const repoName = `${asset.assetName}`.replace('.', '-'); // TODO: actually sanitize + const repoName = `${this.appId}/${asset.assetName}`.replace('.', '-'); // TODO: actually sanitize if (this.stagingRepos[asset.assetName] === undefined) { this.stagingRepos[asset.assetName] = new ecr.Repository(this, repoName, { repositoryName: repoName, diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts index abdb18a3bd6f0..7c822849d3e96 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts @@ -8,7 +8,7 @@ const app = new App(); const stack = new Stack(app, 'synth-test', { synthesizer: AppStagingSynthesizer.defaultResources({ - appId: 'synthassets', + appId: 'szynthassets', }), env: { account: '489318732371', @@ -24,7 +24,7 @@ new lambda.Function(stack, 'lambda-s3', { new lambda.Function(stack, 'lambda-ecr', { code: lambda.EcrImageCode.fromAssetImage(path.join(__dirname, 'assets'), { - assetName: 'ecr-asset', + assetName: 'ecr-assets', }), handler: lambda.Handler.FROM_IMAGE, runtime: lambda.Runtime.FROM_IMAGE, diff --git a/packages/aws-cdk-lib/aws-ecr-assets/lib/image-asset.ts b/packages/aws-cdk-lib/aws-ecr-assets/lib/image-asset.ts index 20fa97849ecb8..d87b5b58145ff 100644 --- a/packages/aws-cdk-lib/aws-ecr-assets/lib/image-asset.ts +++ b/packages/aws-cdk-lib/aws-ecr-assets/lib/image-asset.ts @@ -469,8 +469,8 @@ export class DockerImageAsset extends Construct implements IAsset { : JSON.stringify(extraHash), }); - this.sourceHash = staging.assetHash; this.assetHash = staging.assetHash; + this.sourceHash = this.assetHash; const stack = Stack.of(this); this.assetPath = staging.relativeStagedPath(stack); diff --git a/packages/aws-cdk-lib/core/lib/stack-synthesizers/asset-manifest-builder.ts b/packages/aws-cdk-lib/core/lib/stack-synthesizers/asset-manifest-builder.ts index 0d3c8ffb1608a..a9daa3abf242d 100644 --- a/packages/aws-cdk-lib/core/lib/stack-synthesizers/asset-manifest-builder.ts +++ b/packages/aws-cdk-lib/core/lib/stack-synthesizers/asset-manifest-builder.ts @@ -61,7 +61,8 @@ export class AssetManifestBuilder { const imageTag = `${target.dockerTagPrefix ?? ''}${asset.sourceHash}`; // Add to manifest - return this.addDockerImageAsset(stack, asset.sourceHash, { + const sourceHash = asset.assetName ? `${asset.assetName}-${asset.sourceHash}` : asset.sourceHash; + return this.addDockerImageAsset(stack, sourceHash, { executable: asset.executable, directory: asset.directoryName, dockerBuildArgs: asset.dockerBuildArgs, From 91389b0c5d8eeb58570d4d4a6198550dc3193917 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Fri, 5 May 2023 17:06:35 -0400 Subject: [PATCH 094/120] grantread --- .../app-staging-synthesizer/lib/default-staging-stack.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts index aaccfbea058fa..25b2f0e293083 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts @@ -342,10 +342,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { }); if (this.imageRole) { this.stagingRepos[asset.assetName].grantPullPush(this.imageRole); - this.stagingRepos[asset.assetName].grant(this.imageRole, ...[ - 'ecr:DescribeRepositories', - 'ecr:DescribeImages', - ]); + this.stagingRepos[asset.assetName].grantRead(this.imageRole); } } return repoName; From 2d9719ee429ddeafb6db1f1a98b124d48770e7dd Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Mon, 8 May 2023 15:12:12 -0400 Subject: [PATCH 095/120] more unit tests --- .../app-staging-synthesizer/jest.config.js | 6 - .../lib/default-staging-stack.ts | 8 +- .../test/app-staging-synthesizer.test.ts | 120 +++++++++--------- .../test/bootstrap-roles.test.ts | 38 +++++- .../test/integ.synthesizer.ts | 16 ++- .../test/per-env-staging-factory.test.ts | 9 ++ 6 files changed, 118 insertions(+), 79 deletions(-) create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/per-env-staging-factory.test.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/jest.config.js b/packages/@aws-cdk/app-staging-synthesizer/jest.config.js index b73b054ac88f5..87e3ed1d7117c 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/jest.config.js +++ b/packages/@aws-cdk/app-staging-synthesizer/jest.config.js @@ -1,10 +1,4 @@ const baseConfig = require('@aws-cdk/cdk-build-tools/config/jest.config'); module.exports = { ...baseConfig, - coverageThreshold: { - global: { - branches: 60, - statements: 60, - } - } }; diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts index 25b2f0e293083..4034a5e6fb837 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts @@ -328,13 +328,13 @@ export class DefaultStagingStack extends Stack implements IStagingStack { */ private getCreateRepo(asset: DockerImageAssetSource): string { if (!asset.assetName) { - throw new Error('Assets synthesized with AppScopedStagingSynthesizer must include a \'uniqueId\' in the asset source definition.'); + throw new Error('Assets synthesized with AppScopedStagingSynthesizer must include an \'assetName\' in the asset source definition.'); } // Create image publishing role if it doesn't exist this.ensureImagePublishingRole(); - const repoName = `${this.appId}/${asset.assetName}`.replace('.', '-'); // TODO: actually sanitize + const repoName = generateRepoName(`${this.appId}/${asset.assetName}`); if (this.stagingRepos[asset.assetName] === undefined) { this.stagingRepos[asset.assetName] = new ecr.Repository(this, repoName, { repositoryName: repoName, @@ -346,6 +346,10 @@ export class DefaultStagingStack extends Stack implements IStagingStack { } } return repoName; + + function generateRepoName(name: string): string { + return name.toLocaleLowerCase().replace('.', '-'); + } } public addFile(asset: FileAssetSource): FileStagingLocation { diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts index bceb3ed00b844..abf0f221156e9 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts @@ -1,4 +1,3 @@ -/* eslint-disable jest/no-commented-out-tests */ import * as fs from 'fs'; import { App, Stack, CfnResource, FileAssetPackaging, Token, Lazy, Duration } from 'aws-cdk-lib'; import { Match, Template } from 'aws-cdk-lib/assertions'; @@ -6,8 +5,6 @@ import * as cxschema from 'aws-cdk-lib/cloud-assembly-schema'; import { evaluateCFN } from './evaluate-cfn'; import { APP_ID, CFN_CONTEXT, TestAppScopedStagingSynthesizer, isAssetManifest, last } from './util'; import { AppStagingSynthesizer } from '../lib'; -// import { Repository } from 'aws-cdk-lib/aws-ecr'; -// import { Bucket } from 'aws-cdk-lib/aws-s3'; describe(AppStagingSynthesizer, () => { let app: App; @@ -280,64 +277,65 @@ describe(AppStagingSynthesizer, () => { }); }); - // test('add docker image asset', () => { - // // WHEN - // const location = stack.synthesizer.addDockerImageAsset({ - // directoryName: '.', - // sourceHash: 'abcdef', - // assetName: 'abcdef', - // }); - - // // THEN - we have a fixed asset location - // const repo = 'abcdef'; - // expect(evalCFN(location.repositoryName)).toEqual(repo); - // expect(evalCFN(location.imageUri)).toEqual(`000000000000.dkr.ecr.us-east-1.domain.aws/${repo}:abcdef`); - // }); - - // test('throws with docker image asset without uniqueId', () => { - // expect(() => stack.synthesizer.addDockerImageAsset({ - // directoryName: '.', - // sourceHash: 'abcdef', - // })).toThrowError('Assets synthesized with AppScopedStagingSynthesizer must include a \'uniqueId\' in the asset source definition.'); - // }); - - // test('separate docker image assets have separate repos', () => { - // // WHEN - // const location1 = stack.synthesizer.addDockerImageAsset({ - // directoryName: '.', - // sourceHash: 'abcdef', - // assetName: 'abcdef', - // }); - - // const location2 = stack.synthesizer.addDockerImageAsset({ - // directoryName: './hello', - // sourceHash: 'abcdefg', - // assetName: 'abcdefg', - // }); - - // // THEN - images have different asset locations - // expect(evalCFN(location1.repositoryName)).not.toEqual(evalCFN(location2.repositoryName)); - // }); - - // test('docker image assets with same unique id have same repos', () => { - // // WHEN - // const location1 = stack.synthesizer.addDockerImageAsset({ - // directoryName: '.', - // sourceHash: 'abcdef', - // assetName: 'abcdef', - // }); - - // const location2 = stack.synthesizer.addDockerImageAsset({ - // directoryName: './hello', - // sourceHash: 'abcdefg', - // assetName: 'abcdef', - // }); - - // // THEN - images share same ecr repo - // const repo = 'abcdef'; - // expect(evalCFN(location1.repositoryName)).toEqual(repo); - // expect(evalCFN(location1.repositoryName)).toEqual(evalCFN(location2.repositoryName)); - // }); + test('add docker image asset', () => { + // WHEN + const assetName = 'abcdef'; + const location = stack.synthesizer.addDockerImageAsset({ + directoryName: '.', + sourceHash: 'abcdef', + assetName, + }); + + // THEN - we have a fixed asset location + const repo = `${APP_ID}/${assetName}`; + expect(evalCFN(location.repositoryName)).toEqual(repo); + expect(evalCFN(location.imageUri)).toEqual(`000000000000.dkr.ecr.us-east-1.domain.aws/${repo}:abcdef`); + }); + + test('throws with docker image asset without assetName', () => { + expect(() => stack.synthesizer.addDockerImageAsset({ + directoryName: '.', + sourceHash: 'abcdef', + })).toThrowError('Assets synthesized with AppScopedStagingSynthesizer must include an \'assetName\' in the asset source definition.'); + }); + + test('docker image assets with different assetName have separate repos', () => { + // WHEN + const location1 = stack.synthesizer.addDockerImageAsset({ + directoryName: '.', + sourceHash: 'abcdef', + assetName: 'firstAsset', + }); + + const location2 = stack.synthesizer.addDockerImageAsset({ + directoryName: './hello', + sourceHash: 'abcdef', + assetName: 'secondAsset', + }); + + // THEN - images have different asset locations + expect(evalCFN(location1.repositoryName)).not.toEqual(evalCFN(location2.repositoryName)); + }); + + test('docker image assets with same assetName live in same repos', () => { + // WHEN + const assetName = 'abcdef'; + const location1 = stack.synthesizer.addDockerImageAsset({ + directoryName: '.', + sourceHash: 'abcdef', + assetName, + }); + + const location2 = stack.synthesizer.addDockerImageAsset({ + directoryName: './hello', + sourceHash: 'abcdefg', + assetName, + }); + + // THEN - images share same ecr repo + expect(evalCFN(location1.repositoryName)).toEqual(`${APP_ID}/${assetName}`); + expect(evalCFN(location1.repositoryName)).toEqual(evalCFN(location2.repositoryName)); + }); describe('environment specifics', () => { test('throws if App includes env-agnostic and specific env stacks', () => { diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts index 9b8771ef95a92..39f817ab5980a 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts @@ -39,7 +39,7 @@ describe('Boostrap Roles', () => { expect(stackArtifact.assumeRoleArn).toEqual(DEPLOY_ACTION_ROLE); }); - test('can supply existing arns for staging roles', () => { + test('can supply existing arns for bucket staging role', () => { // GIVEN const app = new App({ defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ @@ -61,7 +61,7 @@ describe('Boostrap Roles', () => { const asm = app.synth(); // THEN - // Staging roles are as advertised + // Staging role is as advertised const manifestArtifact = asm.artifacts.filter(isAssetManifest)[0]; expect(manifestArtifact).toBeDefined(); const manifest: cxschema.AssetManifest = JSON.parse(fs.readFileSync(manifestArtifact.file, { encoding: 'utf-8' })); @@ -69,6 +69,38 @@ describe('Boostrap Roles', () => { expect(firstFile.destinations['000000000000-us-east-1'].assumeRoleArn).toEqual('arn:aws:iam::123456789012:role/S3Access'); }); + test('can provide existing arns for image staging role', () => { + // GIVEN + const app = new App({ + defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ + appId: APP_ID, + imageAssetPublishingRole: BootstrapRole.fromRoleArn('arn:aws:iam::123456789012:role/ECRAccess'), + }), + }); + const stack = new Stack(app, 'Stack', { + env: { + account: '000000000000', + region: 'us-east-1', + }, + }); + stack.synthesizer.addDockerImageAsset({ + directoryName: '.', + sourceHash: 'abcdef', + assetName: 'myDockerAsset', + }); + + // WHEN + const asm = app.synth(); + + // THEN + // Image role is as advertised + const manifestArtifact = asm.artifacts.filter(isAssetManifest)[0]; + expect(manifestArtifact).toBeDefined(); + const manifest: cxschema.AssetManifest = JSON.parse(fs.readFileSync(manifestArtifact.file, { encoding: 'utf-8' })); + const firstFile: any = (manifest.dockerImages ? manifest.dockerImages[Object.keys(manifest.dockerImages)[0]] : undefined) ?? {}; + expect(firstFile.destinations['000000000000-us-east-1'].assumeRoleArn).toEqual('arn:aws:iam::123456789012:role/ECRAccess'); + }); + test('bootstrap roles can be specified as current cli credentials instead', () => { // GIVEN const app = new App({ @@ -126,4 +158,4 @@ describe('Boostrap Roles', () => { // Bootstrapped role's asset manifest tokens are resolved, where possible expect(stackArtifact.cloudFormationExecutionRoleArn).toEqual('arn:${AWS::Partition}:iam::000000000000:role/cdk-abcdef-cfn-exec-role-000000000000-us-east-1'); }); -}); \ No newline at end of file +}); diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts index 7c822849d3e96..52fff546e5ba5 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts @@ -22,13 +22,15 @@ new lambda.Function(stack, 'lambda-s3', { runtime: lambda.Runtime.PYTHON_3_10, }); -new lambda.Function(stack, 'lambda-ecr', { - code: lambda.EcrImageCode.fromAssetImage(path.join(__dirname, 'assets'), { - assetName: 'ecr-assets', - }), - handler: lambda.Handler.FROM_IMAGE, - runtime: lambda.Runtime.FROM_IMAGE, -}); +for (let i = 0; i< 10; i++) { + new lambda.Function(stack, 'lambda-ecr'+i, { + code: lambda.EcrImageCode.fromAssetImage(path.join(__dirname, 'assets'), { + assetName: 'ecr-assets'+i, + }), + handler: lambda.Handler.FROM_IMAGE, + runtime: lambda.Runtime.FROM_IMAGE, + }); +} new integ.IntegTest(app, 'integ-tests', { testCases: [stack], diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/per-env-staging-factory.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/per-env-staging-factory.test.ts new file mode 100644 index 0000000000000..e0b968a0e86c2 --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/per-env-staging-factory.test.ts @@ -0,0 +1,9 @@ +describe('per environment cache', () => { + test('same app, same env', () => { + + }); + + test('same app, different envs', () => { + + }); +}); From 475ebe935bc4a7e740a7cb01dd6919cd9048237b Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Mon, 8 May 2023 15:29:01 -0400 Subject: [PATCH 096/120] unit test for gnostic stuff --- .../test/bootstrap-roles.test.ts | 4 +- .../test/per-env-staging-factory.test.ts | 85 +++++++++++++++++++ 2 files changed, 87 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts index 39f817ab5980a..1314c1a30cc1a 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts @@ -39,7 +39,7 @@ describe('Boostrap Roles', () => { expect(stackArtifact.assumeRoleArn).toEqual(DEPLOY_ACTION_ROLE); }); - test('can supply existing arns for bucket staging role', () => { + test('can supply existing arn for bucket staging role', () => { // GIVEN const app = new App({ defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ @@ -69,7 +69,7 @@ describe('Boostrap Roles', () => { expect(firstFile.destinations['000000000000-us-east-1'].assumeRoleArn).toEqual('arn:aws:iam::123456789012:role/S3Access'); }); - test('can provide existing arns for image staging role', () => { + test('can provide existing arn for image staging role', () => { // GIVEN const app = new App({ defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/per-env-staging-factory.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/per-env-staging-factory.test.ts index e0b968a0e86c2..923a3456b4b29 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/per-env-staging-factory.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/per-env-staging-factory.test.ts @@ -1,9 +1,94 @@ +import { App, Stack } from 'aws-cdk-lib'; +import { AppStagingSynthesizer, BootstrapRole } from '../lib'; +import { APP_ID, CLOUDFORMATION_EXECUTION_ROLE, LOOKUP_ROLE, DEPLOY_ACTION_ROLE } from './util'; + describe('per environment cache', () => { test('same app, same env', () => { + // GIVEN + const app = new App({ + defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ + appId: APP_ID, + deploymentRoles: { + cloudFormationExecutionRole: BootstrapRole.fromRoleArn(CLOUDFORMATION_EXECUTION_ROLE), + lookupRole: BootstrapRole.fromRoleArn(LOOKUP_ROLE), + deploymentRole: BootstrapRole.fromRoleArn(DEPLOY_ACTION_ROLE), + }, + }), + }); + new Stack(app, 'Stack1', { + env: { + account: '000000000000', + region: 'us-east-1', + }, + }); + new Stack(app, 'Stack2', { + env: { + account: '000000000000', + region: 'us-east-1', + }, + }); + // THEN + // stacks share the same staging resources + const asm = app.synth(); + expect(asm.stacks.length).toEqual(3); + const stagingResources = asm.stacks.filter((s) => s.displayName.startsWith('StagingStack')); + expect(stagingResources.length).toEqual(1); }); test('same app, different envs', () => { + // GIVEN + const app = new App({ + defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ + appId: APP_ID, + deploymentRoles: { + cloudFormationExecutionRole: BootstrapRole.fromRoleArn(CLOUDFORMATION_EXECUTION_ROLE), + lookupRole: BootstrapRole.fromRoleArn(LOOKUP_ROLE), + deploymentRole: BootstrapRole.fromRoleArn(DEPLOY_ACTION_ROLE), + }, + }), + }); + new Stack(app, 'Stack1', { + env: { + account: '000000000000', + region: 'us-east-1', + }, + }); + new Stack(app, 'Stack2', { + env: { + account: '000000000000', + region: 'us-west-2', + }, + }); + + // THEN + // separate stacks for staging resources + const asm = app.synth(); + expect(asm.stacks.length).toEqual(4); + const stagingResources = asm.stacks.filter((s) => s.displayName.startsWith('StagingStack')); + expect(stagingResources.length).toEqual(2); + }); + + test('apps must be gnostic', () => { + // GIVEN + const app = new App({ + defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ + appId: APP_ID, + deploymentRoles: { + cloudFormationExecutionRole: BootstrapRole.fromRoleArn(CLOUDFORMATION_EXECUTION_ROLE), + lookupRole: BootstrapRole.fromRoleArn(LOOKUP_ROLE), + deploymentRole: BootstrapRole.fromRoleArn(DEPLOY_ACTION_ROLE), + }, + }), + }); + new Stack(app, 'Stack1', { + env: { + account: '000000000000', + region: 'us-east-1', + }, + }); + // THEN + expect(() => new Stack(app, 'Stack2')).toThrowError(/It is not safe to use AppStagingSynthesizer for both environment-agnostic and environment-aware stacks at the same time./); }); }); From f0867bb827d1d12c29e23ec055517abae9eea08b Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Mon, 8 May 2023 16:09:59 -0400 Subject: [PATCH 097/120] error when staging resource stack too large --- .../lib/default-staging-stack.ts | 14 ++++++++++++++ .../test/app-staging-synthesizer.test.ts | 15 +++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts index 4034a5e6fb837..6354273742033 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts @@ -1,3 +1,5 @@ +import * as fs from 'fs'; +import * as path from 'path'; import { App, ArnFormat, @@ -5,6 +7,7 @@ import { DockerImageAssetSource, Duration, FileAssetSource, + ISynthesisSession, RemovalPolicy, Stack, StackProps, @@ -374,4 +377,15 @@ export class DefaultStagingStack extends Stack implements IStagingStack { dependencyStack: this, }; } + + public _synthesizeTemplate(session: ISynthesisSession, lookupRoleArn?: string | undefined): void { + super._synthesizeTemplate(session, lookupRoleArn); + + const builder = session.assembly; + const outPath = path.join(builder.outdir, this.templateFile); + const size = fs.statSync(outPath).size; + if (size > 51200) { + throw new Error(`Staging resource template cannot be greater than 51200 bytes, but got ${size} bytes`); + } + } } diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts index abf0f221156e9..6c17c5592ab8a 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts @@ -354,6 +354,21 @@ describe(AppStagingSynthesizer, () => { })).toThrowError(/AppStagingSynthesizer property 'appId' may not contain tokens;/); }); + test('throws when staging resource stack is too large', () => { + // WHEN + const assetName = 'abcdef'; + for (let i = 0; i < 100; i++) { + stack.synthesizer.addDockerImageAsset({ + directoryName: '.', + sourceHash: 'abcdef', + assetName: assetName + i, + }); + } + + // THEN + expect(() => app.synth()).toThrowError(/Staging resource template cannot be greater than 51200 bytes/); + }); + /** * Evaluate a possibly string-containing value the same way CFN would do * From c6fb9522fdf5de847498ac40c9512789011023a4 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Tue, 9 May 2023 13:00:29 -0400 Subject: [PATCH 098/120] add ecr lifecycle rules --- .../lib/default-staging-stack.ts | 35 ++++---- .../test/app-staging-synthesizer.test.ts | 84 ++++++++++++++++++- 2 files changed, 98 insertions(+), 21 deletions(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts index 6354273742033..cbcbabef537ee 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts @@ -56,7 +56,7 @@ export interface DefaultStagingStackOptions { readonly imageAssetPublishingRole?: BootstrapRole; /** - * The lifetime for handoff file assets + * The lifetime for handoff file assets. * * Assets that are only necessary at deployment time (for instance, * CloudFormation templates and Lambda source code bundles) will be @@ -71,6 +71,18 @@ export interface DefaultStagingStackOptions { * @default - Duration.days(30) */ readonly handoffFileAssetLifetime?: Duration; + + /** + * The maximum number of image versions to store in a repository. + * + * Previous versions of an image can be stored for rollback purposes. + * Once a repository has more than 3 image versions stored, the oldest + * version will be discarded. This allows for sensible garbage collection + * while maintaining a few previous versions for rollback scenarios. + * + * @default - up to 3 versions stored + */ + readonly imageAssetVersionCount?: number; } /** @@ -175,7 +187,6 @@ export class DefaultStagingStack extends Stack implements IStagingStack { private imageRoleManifestArn?: string; private readonly deployRoleArn?: string; - // private readonly repositoryLifecycleRules: Record; constructor(scope: App, id: string, private readonly props: DefaultStagingStackProps) { super(scope, id, { @@ -192,24 +203,9 @@ export class DefaultStagingStack extends Stack implements IStagingStack { this.providedFileRole = props.fileAssetPublishingRole?._specialize(specializer); this.providedImageRole = props.imageAssetPublishingRole?._specialize(specializer); - - // this.repositoryLifecycleRules = this.processLifecycleRules(props.repositoryLifecycleRules ?? []); this.stagingRepos = {}; } - // private processLifecycleRules(rules: StagingRepoLifecycleRule[]) { - // const ruleMap: Record = {}; - // for (const rule of rules) { - // for (const asset of rule.assets) { - // if (ruleMap[asset] === undefined) { - // ruleMap[asset] = []; - // } - // ruleMap[asset].push(...rule.lifecycleRules); - // } - // } - // return ruleMap; - // } - private ensureFilePublishingRole() { if (this.providedFileRole) { // Override @@ -341,7 +337,10 @@ export class DefaultStagingStack extends Stack implements IStagingStack { if (this.stagingRepos[asset.assetName] === undefined) { this.stagingRepos[asset.assetName] = new ecr.Repository(this, repoName, { repositoryName: repoName, - // lifecycleRules: this.repositoryLifecycleRules[asset.assetName], + lifecycleRules: [{ + description: 'Garbage collect old image versions and keep the specified number of latest versions', + maxImageCount: this.props.imageAssetVersionCount ?? 3, + }], }); if (this.imageRole) { this.stagingRepos[asset.assetName].grantPullPush(this.imageRole); diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts index 6c17c5592ab8a..84e1a63a50338 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts @@ -5,6 +5,7 @@ import * as cxschema from 'aws-cdk-lib/cloud-assembly-schema'; import { evaluateCFN } from './evaluate-cfn'; import { APP_ID, CFN_CONTEXT, TestAppScopedStagingSynthesizer, isAssetManifest, last } from './util'; import { AppStagingSynthesizer } from '../lib'; +import { CloudAssembly } from 'aws-cdk-lib/cx-api'; describe(AppStagingSynthesizer, () => { let app: App; @@ -199,9 +200,7 @@ describe(AppStagingSynthesizer, () => { const asm = app.synth(); // THEN - const stagingStackArtifact = asm.getStackArtifact(`StagingStack-${APP_ID}-000000000000-us-east-1`); - - Template.fromJSON(stagingStackArtifact.template).hasResourceProperties('AWS::S3::Bucket', { + Template.fromJSON(getStagingResourceStack(asm).template).hasResourceProperties('AWS::S3::Bucket', { LifecycleConfiguration: { Rules: Match.arrayWith([{ ExpirationInDays: 30, @@ -337,6 +336,78 @@ describe(AppStagingSynthesizer, () => { expect(evalCFN(location1.repositoryName)).toEqual(evalCFN(location2.repositoryName)); }); + test('docker image repositories have lifecycle rule - default', () => { + // GIVEN + const assetName = 'abcdef'; + stack.synthesizer.addDockerImageAsset({ + directoryName: '.', + sourceHash: 'abcdef', + assetName, + }); + + // WHEN + const asm = app.synth(); + + // THEN + Template.fromJSON(getStagingResourceStack(asm).template).hasResourceProperties('AWS::ECR::Repository', { + LifecyclePolicy: { + LifecyclePolicyText: Match.serializedJson({ + rules: Match.arrayWith([ + Match.objectLike({ + selection: Match.objectLike({ + countType: 'imageCountMoreThan', + countNumber: 3, + }), + }), + ]), + }), + }, + RepositoryName: `${APP_ID}/${assetName}`, + }); + }); + + test('docker image repositories have lifecycle rule - specified', () => { + // GIVEN + app = new App({ + defaultStackSynthesizer: TestAppScopedStagingSynthesizer.stackPerEnv({ + imageAssetVersionCount: 1, + }), + }); + stack = new Stack(app, 'Stack', { + env: { + account: '000000000000', + region: 'us-east-1', + }, + }); + + const assetName = 'abcdef'; + stack.synthesizer.addDockerImageAsset({ + directoryName: '.', + sourceHash: 'abcdef', + assetName, + }); + + // WHEN + const asm = app.synth(); + + // THEN + Template.fromJSON(getStagingResourceStack(asm).template).hasResourceProperties('AWS::ECR::Repository', { + LifecyclePolicy: { + LifecyclePolicyText: Match.serializedJson({ + rules: Match.arrayWith([ + Match.objectLike({ + selection: Match.objectLike({ + countType: 'imageCountMoreThan', + countNumber: 1, + }), + }), + ]), + }), + }, + RepositoryName: `${APP_ID}/${assetName}`, + }); + }); + describe('environment specifics', () => { test('throws if App includes env-agnostic and specific env stacks', () => { // GIVEN - App with Stack with specific environment @@ -377,4 +448,11 @@ describe(AppStagingSynthesizer, () => { function evalCFN(value: any) { return evaluateCFN(stack.resolve(value), CFN_CONTEXT); } + + /** + * Return the staging resource stack that is generated as part of the assembly + */ + function getStagingResourceStack(asm: CloudAssembly) { + return asm.getStackArtifact(`StagingStack-${APP_ID}-000000000000-us-east-1`); + } }); From 95a936230d2dcd098af8171c9d990d26619ddb21 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 10 May 2023 13:04:22 -0400 Subject: [PATCH 099/120] lint --- .../app-staging-synthesizer/lib/default-staging-stack.ts | 4 ++++ .../test/app-staging-synthesizer.test.ts | 2 +- .../test/per-env-staging-factory.test.ts | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts index cbcbabef537ee..6fe0d2b00ca12 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts @@ -377,6 +377,10 @@ export class DefaultStagingStack extends Stack implements IStagingStack { }; } + /** + * Synthesizes the cloudformation template into a cloud assembly. + * @internal + */ public _synthesizeTemplate(session: ISynthesisSession, lookupRoleArn?: string | undefined): void { super._synthesizeTemplate(session, lookupRoleArn); diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts index 84e1a63a50338..ad64c5ba8bf55 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts @@ -2,10 +2,10 @@ import * as fs from 'fs'; import { App, Stack, CfnResource, FileAssetPackaging, Token, Lazy, Duration } from 'aws-cdk-lib'; import { Match, Template } from 'aws-cdk-lib/assertions'; import * as cxschema from 'aws-cdk-lib/cloud-assembly-schema'; +import { CloudAssembly } from 'aws-cdk-lib/cx-api'; import { evaluateCFN } from './evaluate-cfn'; import { APP_ID, CFN_CONTEXT, TestAppScopedStagingSynthesizer, isAssetManifest, last } from './util'; import { AppStagingSynthesizer } from '../lib'; -import { CloudAssembly } from 'aws-cdk-lib/cx-api'; describe(AppStagingSynthesizer, () => { let app: App; diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/per-env-staging-factory.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/per-env-staging-factory.test.ts index 923a3456b4b29..cddb6298c540e 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/per-env-staging-factory.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/per-env-staging-factory.test.ts @@ -1,6 +1,6 @@ import { App, Stack } from 'aws-cdk-lib'; -import { AppStagingSynthesizer, BootstrapRole } from '../lib'; import { APP_ID, CLOUDFORMATION_EXECUTION_ROLE, LOOKUP_ROLE, DEPLOY_ACTION_ROLE } from './util'; +import { AppStagingSynthesizer, BootstrapRole } from '../lib'; describe('per environment cache', () => { test('same app, same env', () => { From f679b2c9e3fede81858452365deba73815735e10 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 10 May 2023 18:17:04 -0400 Subject: [PATCH 100/120] update testing suite and minor bugs --- .../lib/app-staging-synthesizer.ts | 3 +- .../lib/default-staging-stack.ts | 61 +- .../app-staging-synthesizer/lib/index.ts | 2 +- .../lib/per-env-staging-factory.ts | 2 +- .../lib/private/app-global.ts | 2 +- .../lib/private/no-tokens.ts | 2 +- .../test/app-staging-synthesizer.test.ts | 22 +- .../test/bootstrap-roles.test.ts | 36 +- .../test/default-staging-stack.test.ts | 44 + .../env-agnostic-test.assets.json | 45 - .../env-agnostic-test.template.json | 108 --- .../integ.json | 12 - .../test/integ.env-agnostic-synth.ts | 33 - ...lt-resources-ACCOUNT-REGION.template.json} | 62 +- .../Dockerfile | 0 .../index.py | 0 .../Dockerfile | 0 .../index.py | 0 .../cdk.out | 0 .../integ.json | 2 +- ...efaultTestDeployAssert44C8D370.assets.json | 0 ...aultTestDeployAssert44C8D370.template.json | 0 .../manifest.json | 98 +- .../synthesize-default-resources.assets.json | 57 ++ ...synthesize-default-resources.template.json | 210 +++++ .../tree.json | 590 ++++++++---- .../test/integ.synth-default-resources.ts | 51 ++ ...ssets-489318732371-us-east-1.template.json | 345 ------- .../Dockerfile | 2 - .../index.py | 1 - .../Dockerfile | 2 - .../index.py | 1 - .../integ.synthesizer.js.snapshot/cdk.out | 1 - ...efaultTestDeployAssert44C8D370.assets.json | 19 - ...aultTestDeployAssert44C8D370.template.json | 36 - .../manifest.json | 198 ---- .../synth-test.assets.json | 48 - .../synth-test.template.json | 106 --- .../integ.synthesizer.js.snapshot/tree.json | 844 ------------------ .../test/integ.synthesizer.ts | 39 - .../test/per-env-staging-factory.test.ts | 19 +- .../app-staging-synthesizer/test/util.ts | 18 - 42 files changed, 991 insertions(+), 2130 deletions(-) create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/default-staging-stack.test.ts delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/env-agnostic-test.assets.json delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/env-agnostic-test.template.json delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/integ.json delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.ts rename packages/@aws-cdk/app-staging-synthesizer/test/{integ.env-agnostic-synth.js.snapshot/StagingStack-envAgnostic-ACCOUNT-REGION.template.json => integ.synth-default-resources.js.snapshot/StagingStack-default-resources-ACCOUNT-REGION.template.json} (81%) rename packages/@aws-cdk/app-staging-synthesizer/test/{integ.env-agnostic-synth.js.snapshot => integ.synth-default-resources.js.snapshot}/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/Dockerfile (100%) rename packages/@aws-cdk/app-staging-synthesizer/test/{integ.env-agnostic-synth.js.snapshot => integ.synth-default-resources.js.snapshot}/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/index.py (100%) rename packages/@aws-cdk/app-staging-synthesizer/test/{integ.env-agnostic-synth.js.snapshot => integ.synth-default-resources.js.snapshot}/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/Dockerfile (100%) rename packages/@aws-cdk/app-staging-synthesizer/test/{integ.env-agnostic-synth.js.snapshot => integ.synth-default-resources.js.snapshot}/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/index.py (100%) rename packages/@aws-cdk/app-staging-synthesizer/test/{integ.env-agnostic-synth.js.snapshot => integ.synth-default-resources.js.snapshot}/cdk.out (100%) rename packages/@aws-cdk/app-staging-synthesizer/test/{integ.synthesizer.js.snapshot => integ.synth-default-resources.js.snapshot}/integ.json (86%) rename packages/@aws-cdk/app-staging-synthesizer/test/{integ.env-agnostic-synth.js.snapshot => integ.synth-default-resources.js.snapshot}/integtestsDefaultTestDeployAssert44C8D370.assets.json (100%) rename packages/@aws-cdk/app-staging-synthesizer/test/{integ.env-agnostic-synth.js.snapshot => integ.synth-default-resources.js.snapshot}/integtestsDefaultTestDeployAssert44C8D370.template.json (100%) rename packages/@aws-cdk/app-staging-synthesizer/test/{integ.env-agnostic-synth.js.snapshot => integ.synth-default-resources.js.snapshot}/manifest.json (61%) create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/synthesize-default-resources.assets.json create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/synthesize-default-resources.template.json rename packages/@aws-cdk/app-staging-synthesizer/test/{integ.env-agnostic-synth.js.snapshot => integ.synth-default-resources.js.snapshot}/tree.json (57%) create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.ts delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack-synthassets-489318732371-us-east-1.template.json delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/Dockerfile delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/index.py delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/Dockerfile delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/index.py delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/cdk.out delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.template.json delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synth-test.assets.json delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synth-test.template.json delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts index b65a0c4f416a8..1e15aee6c1c35 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts @@ -54,8 +54,7 @@ export interface AppStagingSynthesizerOptions { /** * Properties for stackPerEnv static method */ -export interface DefaultResourcesOptions extends AppStagingSynthesizerOptions, DefaultStagingStackOptions { -} +export interface DefaultResourcesOptions extends AppStagingSynthesizerOptions, DefaultStagingStackOptions {} /** * Properties for customFactory static method diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts index 6fe0d2b00ca12..78b7dfb20e744 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts @@ -27,10 +27,13 @@ const EPHEMERAL_PREFIX = 'handoff/'; */ export interface DefaultStagingStackOptions { /** - * A unique identifier for the application that the staging stack belongs to + * A unique identifier for the application that the staging stack belongs to. * * This identifier will be used in the name of staging resources * created for this application, and should be unique across CDK apps. + * + * The identifier should include lowercase characters and dashes ('-') only + * and have a maximum of 20 characters. */ readonly appId: string; @@ -114,6 +117,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { * Return a factory that will create DefaultStagingStacks */ public static factory(options: DefaultStagingStackOptions): IStagingStackFactory { + const appId = options.appId.toLocaleLowerCase().replace(/[^a-z0-9-]/g, '-').slice(0, 20); return { obtainStagingResources(stack, context) { const app = App.of(stack); @@ -121,17 +125,17 @@ export class DefaultStagingStack extends Stack implements IStagingStack { throw new Error(`Stack ${stack.stackName} must be part of an App`); } - const stackId = `StagingStack-${options.appId}-${context.environmentString}`; + const stackId = `StagingStack-${appId}-${context.environmentString}`; return new DefaultStagingStack(app, stackId, { ...options, // Does not need to contain environment because stack names are unique inside an env anyway - stackName: `StagingStack-${options.appId}`, + stackName: `StagingStack-${appId}`, env: { account: stack.account, region: stack.region, }, - appId: options.appId, + appId, qualifier: context.qualifier, deployRoleArn: context.deployRoleArn, }); @@ -143,14 +147,20 @@ export class DefaultStagingStack extends Stack implements IStagingStack { * Default asset publishing role name for file (S3) assets. */ private get fileRoleName() { - return `cdk-${this.appId}-file-publishing-role-${this.region}`.slice(0, 63); + // This role name can be a maximum of 64 letters. The reason why + // we slice the appId and not the entire name is because this.region + // can be a token and we don't want to accidentally cut it off. + return `cdk-${this.appId}-file-role-${this.region}`; } /** * Default asset publishing role name for docker (ECR) assets. */ private get imageRoleName() { - return `cdk-${this.appId}-asset-publishing-role-${this.region}`.slice(0, 63); + // This role name can be a maximum of 64 letters. The reason why + // we slice the appId and not the entire name is because this.region + // can be a token and we don't want to accidentally cut it off. + return `cdk-${this.appId}-asset-role-${this.region}`; } /** @@ -194,7 +204,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { synthesizer: new BootstraplessSynthesizer(), }); - this.appId = props.appId.toLocaleLowerCase(); + this.appId = this.validateAppId(props.appId); this.dependencyStack = this; this.deployRoleArn = props.deployRoleArn; @@ -206,17 +216,38 @@ export class DefaultStagingStack extends Stack implements IStagingStack { this.stagingRepos = {}; } - private ensureFilePublishingRole() { + private validateAppId(id: string) { + const errors = []; + if (id.length > 20) { + errors.push(`appId expected no more than 20 characters but got ${id.length} characters.`); + } + if (id !== id.toLocaleLowerCase()) { + errors.push('appId only accepts lowercase characters.'); + } + if (!/^[a-z0-9-]*$/.test(id)) { + errors.push('appId expects only letters, numbers, and dashes (\'-\')'); + } + + if (errors.length > 0) { + throw new Error([ + `appId ${id} has errors:`, + ...errors, + ].join('\n')); + } + return id; + } + + private ensureFileRole() { if (this.providedFileRole) { // Override this.fileRoleManifestArn = this.providedFileRole._arnForCloudAssembly(); const cfnArn = this.providedFileRole._arnForCloudFormation(); - this.fileRole = cfnArn ? iam.Role.fromRoleArn(this, 'CdkFilePublishingRole', cfnArn) : undefined; + this.fileRole = cfnArn ? iam.Role.fromRoleArn(this, 'CdkFileRole', cfnArn) : undefined; return; } const roleName = this.fileRoleName; - this.fileRole = new iam.Role(this, 'CdkFilePublishingRole', { + this.fileRole = new iam.Role(this, 'CdkFileRole', { roleName, assumedBy: new iam.AccountPrincipal(this.account), }); @@ -231,7 +262,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { }); } - private ensureImagePublishingRole() { + private ensureImageRole() { // It may end up setting imageRole to undefined, but at least we tried if (this.didImageRole) { return; @@ -242,12 +273,12 @@ export class DefaultStagingStack extends Stack implements IStagingStack { // Override this.imageRoleManifestArn = this.providedImageRole._arnForCloudAssembly(); const cfnArn = this.providedImageRole._arnForCloudFormation(); - this.imageRole = cfnArn ? iam.Role.fromRoleArn(this, 'CdkImagePublishingRole', cfnArn) : undefined; + this.imageRole = cfnArn ? iam.Role.fromRoleArn(this, 'CdkImageRole', cfnArn) : undefined; return; } const roleName = this.imageRoleName; - this.imageRole = new iam.Role(this, 'CdkImagePublishingRole', { + this.imageRole = new iam.Role(this, 'CdkImageRole', { roleName, assumedBy: new iam.AccountPrincipal(this.account), }); @@ -276,7 +307,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { return stagingBucketName; } - this.ensureFilePublishingRole(); + this.ensureFileRole(); const key = this.createBucketKey(); // Create the bucket once the dependencies have been created @@ -331,7 +362,7 @@ export class DefaultStagingStack extends Stack implements IStagingStack { } // Create image publishing role if it doesn't exist - this.ensureImagePublishingRole(); + this.ensureImageRole(); const repoName = generateRepoName(`${this.appId}/${asset.assetName}`); if (this.stagingRepos[asset.assetName] === undefined) { diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/index.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/index.ts index 639efa872b4de..2a5055670e09d 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/index.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/index.ts @@ -1,4 +1,4 @@ export * from './default-staging-stack'; export * from './app-staging-synthesizer'; export * from './bootstrap-roles'; -export * from './staging-stack'; \ No newline at end of file +export * from './staging-stack'; diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/per-env-staging-factory.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/per-env-staging-factory.ts index 808847401a126..7ecd5416d97dc 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/per-env-staging-factory.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/per-env-staging-factory.ts @@ -29,4 +29,4 @@ export class PerEnvironmenStagingFactory implements IStagingStackFactory { cache.set(cacheKey, result); return result; } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/private/app-global.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/private/app-global.ts index 1dd26c5bc4f8c..2cd36d0264a5b 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/private/app-global.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/private/app-global.ts @@ -30,4 +30,4 @@ export class AppScopedGlobal { this.map.set(app, instance); return instance; } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/private/no-tokens.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/private/no-tokens.ts index 294866b818e86..befb88fc8f4de 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/private/no-tokens.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/private/no-tokens.ts @@ -6,4 +6,4 @@ export function validateNoTokens(props: A, context: string) { StringSpecializer.validateNoTokens(value, `${context} property '${key}'`); } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts index ad64c5ba8bf55..c6e4a286ff7d0 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts @@ -4,7 +4,7 @@ import { Match, Template } from 'aws-cdk-lib/assertions'; import * as cxschema from 'aws-cdk-lib/cloud-assembly-schema'; import { CloudAssembly } from 'aws-cdk-lib/cx-api'; import { evaluateCFN } from './evaluate-cfn'; -import { APP_ID, CFN_CONTEXT, TestAppScopedStagingSynthesizer, isAssetManifest, last } from './util'; +import { APP_ID, CFN_CONTEXT, isAssetManifest, last } from './util'; import { AppStagingSynthesizer } from '../lib'; describe(AppStagingSynthesizer, () => { @@ -13,7 +13,7 @@ describe(AppStagingSynthesizer, () => { beforeEach(() => { app = new App({ - defaultStackSynthesizer: TestAppScopedStagingSynthesizer.stackPerEnv(), + defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: APP_ID }), }); stack = new Stack(app, 'Stack', { env: { @@ -52,7 +52,7 @@ describe(AppStagingSynthesizer, () => { bucketName: `cdk-${APP_ID}-staging-000000000000-us-east-1`, objectKey: templateObjectKey, region: 'us-east-1', - assumeRoleArn: `arn:\${AWS::Partition}:iam::000000000000:role/cdk-${APP_ID}-file-publishing-role-us-east-1`, + assumeRoleArn: `arn:\${AWS::Partition}:iam::000000000000:role/cdk-${APP_ID}-file-role-us-east-1`, }, }, }); @@ -60,7 +60,7 @@ describe(AppStagingSynthesizer, () => { test('stack template is in the asset manifest - environment tokens', () => { const app2 = new App({ - defaultStackSynthesizer: TestAppScopedStagingSynthesizer.stackPerEnv(), + defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: APP_ID }), }); const accountToken = Token.asString('111111111111'); const regionToken = Token.asString('us-east-2'); @@ -99,7 +99,7 @@ describe(AppStagingSynthesizer, () => { bucketName: `cdk-${APP_ID}-staging-111111111111-us-east-2`, objectKey: templateObjectKey, region: 'us-east-2', - assumeRoleArn: `arn:\${AWS::Partition}:iam::111111111111:role/cdk-${APP_ID}-file-publishing-role-us-east-2`, + assumeRoleArn: `arn:\${AWS::Partition}:iam::111111111111:role/cdk-${APP_ID}-file-role-us-east-2`, }, }, }); @@ -169,7 +169,7 @@ describe(AppStagingSynthesizer, () => { test('ephemeral assets do not get specified bucketPrefix', () => { // GIVEN app = new App({ - defaultStackSynthesizer: TestAppScopedStagingSynthesizer.stackPerEnv({}), + defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: APP_ID }), }); stack = new Stack(app, 'Stack', { env: { @@ -214,7 +214,8 @@ describe(AppStagingSynthesizer, () => { test('lifecycle rule on ephemeral assets can be customized', () => { // GIVEN app = new App({ - defaultStackSynthesizer: TestAppScopedStagingSynthesizer.stackPerEnv({ + defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ + appId: APP_ID, handoffFileAssetLifetime: Duration.days(1), }), }); @@ -263,7 +264,7 @@ describe(AppStagingSynthesizer, () => { Match.objectLike({ Effect: 'Allow', Principal: { - AWS: 'role', + AWS: Match.anyValue(), }, Action: [ 's3:GetObject*', @@ -369,7 +370,8 @@ describe(AppStagingSynthesizer, () => { test('docker image repositories have lifecycle rule - specified', () => { // GIVEN app = new App({ - defaultStackSynthesizer: TestAppScopedStagingSynthesizer.stackPerEnv({ + defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ + appId: APP_ID, imageAssetVersionCount: 1, }), }); @@ -419,7 +421,7 @@ describe(AppStagingSynthesizer, () => { test('throws if synthesizer props have tokens', () => { expect(() => new App({ - defaultStackSynthesizer: TestAppScopedStagingSynthesizer.stackPerEnv({ + defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: Lazy.string({ produce: () => 'appId' }), }), })).toThrowError(/AppStagingSynthesizer property 'appId' may not contain tokens;/); diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts index 1314c1a30cc1a..2f18980205d9d 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts @@ -1,11 +1,43 @@ import * as fs from 'fs'; import { App, Stack, CfnResource } from 'aws-cdk-lib'; import * as cxschema from 'aws-cdk-lib/cloud-assembly-schema'; -import { APP_ID, CLOUDFORMATION_EXECUTION_ROLE, DEPLOY_ACTION_ROLE, LOOKUP_ROLE, isAssetManifest } from './util'; +import { APP_ID, isAssetManifest } from './util'; import { AppStagingSynthesizer, BootstrapRole } from '../lib'; +const CLOUDFORMATION_EXECUTION_ROLE = 'cloudformation-execution-role'; +const DEPLOY_ACTION_ROLE = 'deploy-action-role'; +const LOOKUP_ROLE = 'lookup-role'; + describe('Boostrap Roles', () => { - test('Can supply existing arns for bootstrapped roles', () => { + test('default bootstrap role name is always under 64 characters', () => { + // GIVEN + const app = new App({ + defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ + appId: 'super long app id that needs to be cut', + }), + }); + const stack = new Stack(app, 'Stack', { + env: { + account: '000000000000', + region: 'us-east-1', + }, + }); + new CfnResource(stack, 'Resource', { + type: 'Some::Resource', + }); + + // WHEN + const asm = app.synth(); + + // THEN + const manifestArtifact = asm.artifacts.filter(isAssetManifest)[0]; + expect(manifestArtifact).toBeDefined(); + const manifest: cxschema.AssetManifest = JSON.parse(fs.readFileSync(manifestArtifact.file, { encoding: 'utf-8' })); + const firstFile: any = (manifest.files ? manifest.files[Object.keys(manifest.files)[0]] : undefined) ?? {}; + expect(firstFile.destinations['000000000000-us-east-1'].assumeRoleArn).toEqual('arn:${AWS::Partition}:iam::000000000000:role/cdk-super-long-app-id-th-file-role-us-east-1'); + }); + + test('can supply existing arns for bootstrapped roles', () => { // GIVEN const app = new App({ defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/default-staging-stack.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/default-staging-stack.test.ts new file mode 100644 index 0000000000000..641bda0c9e387 --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/default-staging-stack.test.ts @@ -0,0 +1,44 @@ +import { DefaultStagingStack } from '../lib'; +import { App } from 'aws-cdk-lib'; + +describe('default staging stack', () => { + describe('appId fails', () => { + test('when appId > 20 characters', () => { + const app = new App(); + expect(() => new DefaultStagingStack(app, 'stack', { + appId: 'a'.repeat(21), + qualifier: 'qualifier', + })).toThrowError(/appId expected no more than 20 characters but got 21 characters./); + }); + + test('when uppercase characters are used', () => { + const app = new App(); + expect(() => new DefaultStagingStack(app, 'stack', { + appId: 'ABCDEF', + qualifier: 'qualifier', + })).toThrowError(/appId only accepts lowercase characters./); + }); + + test('when symbols are used', () => { + const app = new App(); + expect(() => new DefaultStagingStack(app, 'stack', { + appId: 'ca$h', + qualifier: 'qualifier', + })).toThrowError(/appId expects only letters, numbers, and dashes \('-'\)/); + }); + + test('when multiple rules broken at once', () => { + const app = new App(); + const appId = 'AB&C'.repeat(10); + expect(() => new DefaultStagingStack(app, 'stack', { + appId, + qualifier: 'qualifier', + })).toThrowError([ + `appId ${appId} has errors:`, + 'appId expected no more than 20 characters but got 40 characters.', + 'appId only accepts lowercase characters.', + 'appId expects only letters, numbers, and dashes (\'-\')', + ].join('\n')); + }); + }); +}); \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/env-agnostic-test.assets.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/env-agnostic-test.assets.json deleted file mode 100644 index 2aa1e4b20e498..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/env-agnostic-test.assets.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "version": "31.0.0", - "files": { - "68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650": { - "source": { - "path": "asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650", - "packaging": "zip" - }, - "destinations": { - "current_account-current_region": { - "bucketName": "cdk-envagnostic-staging-${AWS::AccountId}-${AWS::Region}", - "objectKey": "68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650.zip", - "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-envagnostic-file-publishing-role-${AWS::Region}" - } - } - }, - "d1e269d7a168aa54c7ccfb772c9004bc0ff1fee705c66cfb6592a07ef81cdf6e": { - "source": { - "path": "env-agnostic-test.template.json", - "packaging": "file" - }, - "destinations": { - "current_account-current_region": { - "bucketName": "cdk-envagnostic-staging-${AWS::AccountId}-${AWS::Region}", - "objectKey": "d1e269d7a168aa54c7ccfb772c9004bc0ff1fee705c66cfb6592a07ef81cdf6e.json", - "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-envagnostic-file-publishing-role-${AWS::Region}" - } - } - } - }, - "dockerImages": { - "16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622": { - "source": { - "directory": "asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622" - }, - "destinations": { - "current_account-current_region": { - "repositoryName": "ecr-asset-1", - "imageTag": "16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622", - "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-envagnostic-asset-publishing-role-${AWS::Region}" - } - } - } - } -} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/env-agnostic-test.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/env-agnostic-test.template.json deleted file mode 100644 index d1eaae9842cf2..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/env-agnostic-test.template.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "Resources": { - "lambdas3ServiceRoleC9EDE33A": { - "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" - ] - ] - } - ] - } - }, - "lambdas342CE2BBD": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "S3Bucket": { - "Fn::Sub": "cdk-envagnostic-staging-${AWS::AccountId}-${AWS::Region}" - }, - "S3Key": "68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650.zip" - }, - "Role": { - "Fn::GetAtt": [ - "lambdas3ServiceRoleC9EDE33A", - "Arn" - ] - }, - "Handler": "index.handler", - "Runtime": "python3.10" - }, - "DependsOn": [ - "lambdas3ServiceRoleC9EDE33A" - ] - }, - "lambdaecrServiceRoleFC667F6F": { - "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" - ] - ] - } - ] - } - }, - "lambdaecr8D55A032": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "ImageUri": { - "Fn::Sub": "${AWS::AccountId}.dkr.ecr.${AWS::Region}.${AWS::URLSuffix}/ecr-asset-1:16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622" - } - }, - "Role": { - "Fn::GetAtt": [ - "lambdaecrServiceRoleFC667F6F", - "Arn" - ] - }, - "PackageType": "Image" - }, - "DependsOn": [ - "lambdaecrServiceRoleFC667F6F" - ] - } - } -} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/integ.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/integ.json deleted file mode 100644 index 36a961f15ddf5..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/integ.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "version": "31.0.0", - "testCases": { - "integ-tests/DefaultTest": { - "stacks": [ - "env-agnostic-test" - ], - "assertionStack": "integ-tests/DefaultTest/DeployAssert", - "assertionStackName": "integtestsDefaultTestDeployAssert44C8D370" - } - } -} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.ts b/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.ts deleted file mode 100644 index 35539f8d541ed..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.ts +++ /dev/null @@ -1,33 +0,0 @@ -import * as path from 'path'; -import * as integ from '@aws-cdk/integ-tests-alpha'; -import { App, Stack } from 'aws-cdk-lib'; -import * as lambda from 'aws-cdk-lib/aws-lambda'; -import { AppStagingSynthesizer } from '../lib'; - -const app = new App(); - -const stack = new Stack(app, 'env-agnostic-test', { - synthesizer: AppStagingSynthesizer.defaultResources({ - appId: 'envAgnostic', - }), -}); - -new lambda.Function(stack, 'lambda-s3', { - code: lambda.AssetCode.fromAsset(path.join(__dirname, 'assets')), - handler: 'index.handler', - runtime: lambda.Runtime.PYTHON_3_10, -}); - -new lambda.Function(stack, 'lambda-ecr', { - code: lambda.EcrImageCode.fromAssetImage(path.join(__dirname, 'assets'), { - assetName: 'ecr-asset-1', - }), - handler: lambda.Handler.FROM_IMAGE, - runtime: lambda.Runtime.FROM_IMAGE, -}); - -new integ.IntegTest(app, 'integ-tests', { - testCases: [stack], -}); - -app.synth(); diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/StagingStack-envAgnostic-ACCOUNT-REGION.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/StagingStack-default-resources-ACCOUNT-REGION.template.json similarity index 81% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/StagingStack-envAgnostic-ACCOUNT-REGION.template.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/StagingStack-default-resources-ACCOUNT-REGION.template.json index 4b13a92207537..eb3be509a12f4 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/StagingStack-envAgnostic-ACCOUNT-REGION.template.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/StagingStack-default-resources-ACCOUNT-REGION.template.json @@ -1,6 +1,6 @@ { "Resources": { - "CdkFilePublishingRole119E2944": { + "CdkFileRoleE26CEABA": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { @@ -34,7 +34,7 @@ "Fn::Join": [ "", [ - "cdk-envagnostic-file-publishing-role-", + "cdk-default-resources-file-role-", { "Ref": "AWS::Region" } @@ -43,7 +43,7 @@ } } }, - "CdkFilePublishingRoleDefaultPolicyE914275E": { + "CdkFileRoleDefaultPolicy621C7E5B": { "Type": "AWS::IAM::Policy", "Properties": { "PolicyDocument": { @@ -104,10 +104,10 @@ ], "Version": "2012-10-17" }, - "PolicyName": "CdkFilePublishingRoleDefaultPolicyE914275E", + "PolicyName": "CdkFileRoleDefaultPolicy621C7E5B", "Roles": [ { - "Ref": "CdkFilePublishingRole119E2944" + "Ref": "CdkFileRoleE26CEABA" } ] } @@ -188,7 +188,7 @@ "BucketKeyAlias69A0886F": { "Type": "AWS::KMS::Alias", "Properties": { - "AliasName": "alias/cdk-envagnostic-staging", + "AliasName": "alias/cdk-default-resources-staging", "TargetKeyId": { "Fn::GetAtt": [ "BucketKey7092080A", @@ -219,7 +219,7 @@ "Fn::Join": [ "", [ - "cdk-envagnostic-staging-", + "cdk-default-resources-staging-", { "Ref": "AWS::AccountId" }, @@ -354,7 +354,7 @@ } } }, - "CdkImagePublishingRole87E59CEF": { + "CdkImageRoleF1394AC3": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { @@ -388,7 +388,7 @@ "Fn::Join": [ "", [ - "cdk-envagnostic-asset-publishing-role-", + "cdk-default-resources-asset-role-", { "Ref": "AWS::Region" } @@ -397,7 +397,7 @@ } } }, - "CdkImagePublishingRoleDefaultPolicy7638FA58": { + "CdkImageRoleDefaultPolicy4A1572DE": { "Type": "AWS::IAM::Policy", "Properties": { "PolicyDocument": { @@ -415,12 +415,20 @@ "ecr:UploadLayerPart" ], "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "ecrasset155BFF0C3", - "Arn" - ] - } + "Resource": [ + { + "Fn::GetAtt": [ + "defaultresourcesecrasset2FBE6B8A9", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "defaultresourcesecrasset9191BD6E", + "Arn" + ] + } + ] }, { "Action": "ecr:GetAuthorizationToken", @@ -430,18 +438,32 @@ ], "Version": "2012-10-17" }, - "PolicyName": "CdkImagePublishingRoleDefaultPolicy7638FA58", + "PolicyName": "CdkImageRoleDefaultPolicy4A1572DE", "Roles": [ { - "Ref": "CdkImagePublishingRole87E59CEF" + "Ref": "CdkImageRoleF1394AC3" } ] } }, - "ecrasset155BFF0C3": { + "defaultresourcesecrasset9191BD6E": { + "Type": "AWS::ECR::Repository", + "Properties": { + "LifecyclePolicy": { + "LifecyclePolicyText": "{\"rules\":[{\"rulePriority\":1,\"description\":\"Garbage collect old image versions and keep the specified number of latest versions\",\"selection\":{\"tagStatus\":\"any\",\"countType\":\"imageCountMoreThan\",\"countNumber\":3},\"action\":{\"type\":\"expire\"}}]}" + }, + "RepositoryName": "default-resources/ecr-asset" + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "defaultresourcesecrasset2FBE6B8A9": { "Type": "AWS::ECR::Repository", "Properties": { - "RepositoryName": "ecr-asset-1" + "LifecyclePolicy": { + "LifecyclePolicyText": "{\"rules\":[{\"rulePriority\":1,\"description\":\"Garbage collect old image versions and keep the specified number of latest versions\",\"selection\":{\"tagStatus\":\"any\",\"countType\":\"imageCountMoreThan\",\"countNumber\":3},\"action\":{\"type\":\"expire\"}}]}" + }, + "RepositoryName": "default-resources/ecr-asset-2" }, "UpdateReplacePolicy": "Retain", "DeletionPolicy": "Retain" diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/Dockerfile b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/Dockerfile similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/Dockerfile rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/Dockerfile diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/index.py b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/index.py similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/index.py rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/index.py diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/Dockerfile b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/Dockerfile similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/Dockerfile rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/Dockerfile diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/index.py b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/index.py similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/index.py rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/index.py diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/cdk.out b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/cdk.out similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/cdk.out rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/cdk.out diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integ.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/integ.json similarity index 86% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integ.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/integ.json index 806047874d9f2..9eeaef0dfe700 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integ.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/integ.json @@ -3,7 +3,7 @@ "testCases": { "integ-tests/DefaultTest": { "stacks": [ - "synth-test" + "synthesize-default-resources" ], "assertionStack": "integ-tests/DefaultTest/DeployAssert", "assertionStackName": "integtestsDefaultTestDeployAssert44C8D370" diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.template.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.template.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.template.json diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/manifest.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/manifest.json similarity index 61% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/manifest.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/manifest.json index 0c40685bb9e5c..ab3b50537bb2e 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/manifest.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/manifest.json @@ -1,145 +1,157 @@ { "version": "31.0.0", "artifacts": { - "env-agnostic-test.assets": { + "synthesize-default-resources.assets": { "type": "cdk:asset-manifest", "properties": { - "file": "env-agnostic-test.assets.json" + "file": "synthesize-default-resources.assets.json" } }, - "env-agnostic-test": { + "synthesize-default-resources": { "type": "aws:cloudformation:stack", "environment": "aws://unknown-account/unknown-region", "properties": { - "templateFile": "env-agnostic-test.template.json", + "templateFile": "synthesize-default-resources.template.json", "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "additionalDependencies": [ - "env-agnostic-test.assets" + "synthesize-default-resources.assets" ], - "stackTemplateAssetObjectUrl": "s3://cdk-envagnostic-staging-${AWS::AccountId}-${AWS::Region}/d1e269d7a168aa54c7ccfb772c9004bc0ff1fee705c66cfb6592a07ef81cdf6e.json", + "stackTemplateAssetObjectUrl": "s3://cdk-default-resources-staging-${AWS::AccountId}-${AWS::Region}/e21d11bec65be920861a56a86066cc88a0241d5cbe8324d0692ca982420e4cb0.json", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", "lookupRole": { "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}" } }, "dependencies": [ - "StagingStack-envAgnostic-ACCOUNT-REGION", - "env-agnostic-test.assets" + "StagingStack-default-resources-ACCOUNT-REGION", + "synthesize-default-resources.assets" ], "metadata": { - "/env-agnostic-test/lambda-s3/ServiceRole/Resource": [ + "/synthesize-default-resources/lambda-s3/ServiceRole/Resource": [ { "type": "aws:cdk:logicalId", "data": "lambdas3ServiceRoleC9EDE33A" } ], - "/env-agnostic-test/lambda-s3/Resource": [ + "/synthesize-default-resources/lambda-s3/Resource": [ { "type": "aws:cdk:logicalId", "data": "lambdas342CE2BBD" } ], - "/env-agnostic-test/lambda-ecr/ServiceRole/Resource": [ + "/synthesize-default-resources/lambda-ecr-1/ServiceRole/Resource": [ { "type": "aws:cdk:logicalId", - "data": "lambdaecrServiceRoleFC667F6F" + "data": "lambdaecr1ServiceRoleA6BBC49F" } ], - "/env-agnostic-test/lambda-ecr/Resource": [ + "/synthesize-default-resources/lambda-ecr-1/Resource": [ { "type": "aws:cdk:logicalId", - "data": "lambdaecr8D55A032" + "data": "lambdaecr1B33A3D15" } ], - "lambdaServiceRole494E4CA6": [ + "/synthesize-default-resources/lambda-ecr-1-copy/ServiceRole/Resource": [ { "type": "aws:cdk:logicalId", - "data": "lambdaServiceRole494E4CA6", - "trace": [ - "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" - ] + "data": "lambdaecr1copyServiceRole2A9FAF5F" } ], - "lambda8B5974B5": [ + "/synthesize-default-resources/lambda-ecr-1-copy/Resource": [ { "type": "aws:cdk:logicalId", - "data": "lambda8B5974B5", - "trace": [ - "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" - ] + "data": "lambdaecr1copyD39CDE9B" + } + ], + "/synthesize-default-resources/lambda-ecr-2/ServiceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "lambdaecr2ServiceRole2EA363D2" + } + ], + "/synthesize-default-resources/lambda-ecr-2/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "lambdaecr2615DAF68" } ] }, - "displayName": "env-agnostic-test" + "displayName": "synthesize-default-resources" }, - "StagingStack-envAgnostic-ACCOUNT-REGION": { + "StagingStack-default-resources-ACCOUNT-REGION": { "type": "aws:cloudformation:stack", "environment": "aws://unknown-account/unknown-region", "properties": { - "templateFile": "StagingStack-envAgnostic-ACCOUNT-REGION.template.json", + "templateFile": "StagingStack-default-resources-ACCOUNT-REGION.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}", - "stackName": "StagingStack-envAgnostic" + "stackName": "StagingStack-default-resources" }, "metadata": { - "/StagingStack-envAgnostic-ACCOUNT-REGION/CdkFilePublishingRole/Resource": [ + "/StagingStack-default-resources-ACCOUNT-REGION/CdkFileRole/Resource": [ { "type": "aws:cdk:logicalId", - "data": "CdkFilePublishingRole119E2944" + "data": "CdkFileRoleE26CEABA" } ], - "/StagingStack-envAgnostic-ACCOUNT-REGION/CdkFilePublishingRole/DefaultPolicy/Resource": [ + "/StagingStack-default-resources-ACCOUNT-REGION/CdkFileRole/DefaultPolicy/Resource": [ { "type": "aws:cdk:logicalId", - "data": "CdkFilePublishingRoleDefaultPolicyE914275E" + "data": "CdkFileRoleDefaultPolicy621C7E5B" } ], - "/StagingStack-envAgnostic-ACCOUNT-REGION/BucketKey/Resource": [ + "/StagingStack-default-resources-ACCOUNT-REGION/BucketKey/Resource": [ { "type": "aws:cdk:logicalId", "data": "BucketKey7092080A" } ], - "/StagingStack-envAgnostic-ACCOUNT-REGION/BucketKey/Alias/Resource": [ + "/StagingStack-default-resources-ACCOUNT-REGION/BucketKey/Alias/Resource": [ { "type": "aws:cdk:logicalId", "data": "BucketKeyAlias69A0886F" } ], - "/StagingStack-envAgnostic-ACCOUNT-REGION/CdkStagingBucket/Resource": [ + "/StagingStack-default-resources-ACCOUNT-REGION/CdkStagingBucket/Resource": [ { "type": "aws:cdk:logicalId", "data": "CdkStagingBucket1636058C" } ], - "/StagingStack-envAgnostic-ACCOUNT-REGION/CdkStagingBucket/Policy/Resource": [ + "/StagingStack-default-resources-ACCOUNT-REGION/CdkStagingBucket/Policy/Resource": [ { "type": "aws:cdk:logicalId", "data": "CdkStagingBucketPolicy42BD1F92" } ], - "/StagingStack-envAgnostic-ACCOUNT-REGION/CdkImagePublishingRole/Resource": [ + "/StagingStack-default-resources-ACCOUNT-REGION/CdkImageRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "CdkImageRoleF1394AC3" + } + ], + "/StagingStack-default-resources-ACCOUNT-REGION/CdkImageRole/DefaultPolicy/Resource": [ { "type": "aws:cdk:logicalId", - "data": "CdkImagePublishingRole87E59CEF" + "data": "CdkImageRoleDefaultPolicy4A1572DE" } ], - "/StagingStack-envAgnostic-ACCOUNT-REGION/CdkImagePublishingRole/DefaultPolicy/Resource": [ + "/StagingStack-default-resources-ACCOUNT-REGION/default-resources--ecr-asset/Resource": [ { "type": "aws:cdk:logicalId", - "data": "CdkImagePublishingRoleDefaultPolicy7638FA58" + "data": "defaultresourcesecrasset9191BD6E" } ], - "/StagingStack-envAgnostic-ACCOUNT-REGION/ecr-asset-1/Resource": [ + "/StagingStack-default-resources-ACCOUNT-REGION/default-resources--ecr-asset-2/Resource": [ { "type": "aws:cdk:logicalId", - "data": "ecrasset155BFF0C3" + "data": "defaultresourcesecrasset2FBE6B8A9" } ] }, - "displayName": "StagingStack-envAgnostic-ACCOUNT-REGION" + "displayName": "StagingStack-default-resources-ACCOUNT-REGION" }, "integtestsDefaultTestDeployAssert44C8D370.assets": { "type": "cdk:asset-manifest", diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/synthesize-default-resources.assets.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/synthesize-default-resources.assets.json new file mode 100644 index 0000000000000..ba7243f295609 --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/synthesize-default-resources.assets.json @@ -0,0 +1,57 @@ +{ + "version": "31.0.0", + "files": { + "68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650": { + "source": { + "path": "asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650", + "packaging": "zip" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-default-resources-staging-${AWS::AccountId}-${AWS::Region}", + "objectKey": "68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650.zip", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-default-resources-file-role-${AWS::Region}" + } + } + }, + "e21d11bec65be920861a56a86066cc88a0241d5cbe8324d0692ca982420e4cb0": { + "source": { + "path": "synthesize-default-resources.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-default-resources-staging-${AWS::AccountId}-${AWS::Region}", + "objectKey": "e21d11bec65be920861a56a86066cc88a0241d5cbe8324d0692ca982420e4cb0.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-default-resources-file-role-${AWS::Region}" + } + } + } + }, + "dockerImages": { + "ecr-asset-16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622": { + "source": { + "directory": "asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622" + }, + "destinations": { + "current_account-current_region": { + "repositoryName": "default-resources/ecr-asset", + "imageTag": "16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-default-resources-asset-role-${AWS::Region}" + } + } + }, + "ecr-asset-2-16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622": { + "source": { + "directory": "asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622" + }, + "destinations": { + "current_account-current_region": { + "repositoryName": "default-resources/ecr-asset-2", + "imageTag": "16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-default-resources-asset-role-${AWS::Region}" + } + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/synthesize-default-resources.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/synthesize-default-resources.template.json new file mode 100644 index 0000000000000..05ac9636afd0b --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/synthesize-default-resources.template.json @@ -0,0 +1,210 @@ +{ + "Resources": { + "lambdas3ServiceRoleC9EDE33A": { + "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" + ] + ] + } + ] + } + }, + "lambdas342CE2BBD": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-default-resources-staging-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650.zip" + }, + "Role": { + "Fn::GetAtt": [ + "lambdas3ServiceRoleC9EDE33A", + "Arn" + ] + }, + "Handler": "index.handler", + "Runtime": "python3.10" + }, + "DependsOn": [ + "lambdas3ServiceRoleC9EDE33A" + ] + }, + "lambdaecr1ServiceRoleA6BBC49F": { + "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" + ] + ] + } + ] + } + }, + "lambdaecr1B33A3D15": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ImageUri": { + "Fn::Sub": "${AWS::AccountId}.dkr.ecr.${AWS::Region}.${AWS::URLSuffix}/default-resources/ecr-asset:16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622" + } + }, + "Role": { + "Fn::GetAtt": [ + "lambdaecr1ServiceRoleA6BBC49F", + "Arn" + ] + }, + "PackageType": "Image" + }, + "DependsOn": [ + "lambdaecr1ServiceRoleA6BBC49F" + ] + }, + "lambdaecr1copyServiceRole2A9FAF5F": { + "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" + ] + ] + } + ] + } + }, + "lambdaecr1copyD39CDE9B": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ImageUri": { + "Fn::Sub": "${AWS::AccountId}.dkr.ecr.${AWS::Region}.${AWS::URLSuffix}/default-resources/ecr-asset:16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622" + } + }, + "Role": { + "Fn::GetAtt": [ + "lambdaecr1copyServiceRole2A9FAF5F", + "Arn" + ] + }, + "PackageType": "Image" + }, + "DependsOn": [ + "lambdaecr1copyServiceRole2A9FAF5F" + ] + }, + "lambdaecr2ServiceRole2EA363D2": { + "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" + ] + ] + } + ] + } + }, + "lambdaecr2615DAF68": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ImageUri": { + "Fn::Sub": "${AWS::AccountId}.dkr.ecr.${AWS::Region}.${AWS::URLSuffix}/default-resources/ecr-asset-2:16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622" + } + }, + "Role": { + "Fn::GetAtt": [ + "lambdaecr2ServiceRole2EA363D2", + "Arn" + ] + }, + "PackageType": "Image" + }, + "DependsOn": [ + "lambdaecr2ServiceRole2EA363D2" + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/tree.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/tree.json similarity index 57% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/tree.json rename to packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/tree.json index 7aa28bc7ccd5f..f25e00615302b 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.env-agnostic-synth.js.snapshot/tree.json +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/tree.json @@ -4,29 +4,29 @@ "id": "App", "path": "", "children": { - "env-agnostic-test": { - "id": "env-agnostic-test", - "path": "env-agnostic-test", + "synthesize-default-resources": { + "id": "synthesize-default-resources", + "path": "synthesize-default-resources", "children": { "lambda-s3": { "id": "lambda-s3", - "path": "env-agnostic-test/lambda-s3", + "path": "synthesize-default-resources/lambda-s3", "children": { "ServiceRole": { "id": "ServiceRole", - "path": "env-agnostic-test/lambda-s3/ServiceRole", + "path": "synthesize-default-resources/lambda-s3/ServiceRole", "children": { "ImportServiceRole": { "id": "ImportServiceRole", - "path": "env-agnostic-test/lambda-s3/ServiceRole/ImportServiceRole", + "path": "synthesize-default-resources/lambda-s3/ServiceRole/ImportServiceRole", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" } }, "Resource": { "id": "Resource", - "path": "env-agnostic-test/lambda-s3/ServiceRole/Resource", + "path": "synthesize-default-resources/lambda-s3/ServiceRole/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::IAM::Role", "aws:cdk:cloudformation:props": { @@ -59,51 +59,51 @@ } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" } }, "Code": { "id": "Code", - "path": "env-agnostic-test/lambda-s3/Code", + "path": "synthesize-default-resources/lambda-s3/Code", "children": { "Stage": { "id": "Stage", - "path": "env-agnostic-test/lambda-s3/Code/Stage", + "path": "synthesize-default-resources/lambda-s3/Code/Stage", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.AssetStaging", + "version": "0.0.0" } }, "AssetBucket": { "id": "AssetBucket", - "path": "env-agnostic-test/lambda-s3/Code/AssetBucket", + "path": "synthesize-default-resources/lambda-s3/Code/AssetBucket", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_s3.BucketBase", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_s3_assets.Asset", + "version": "0.0.0" } }, "Resource": { "id": "Resource", - "path": "env-agnostic-test/lambda-s3/Resource", + "path": "synthesize-default-resources/lambda-s3/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::Lambda::Function", "aws:cdk:cloudformation:props": { "code": { "s3Bucket": { - "Fn::Sub": "cdk-envagnostic-staging-${AWS::AccountId}-${AWS::Region}" + "Fn::Sub": "cdk-default-resources-staging-${AWS::AccountId}-${AWS::Region}" }, "s3Key": "68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650.zip" }, @@ -118,35 +118,35 @@ } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_lambda.CfnFunction", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_lambda.Function", + "version": "0.0.0" } }, - "lambda-ecr": { - "id": "lambda-ecr", - "path": "env-agnostic-test/lambda-ecr", + "lambda-ecr-1": { + "id": "lambda-ecr-1", + "path": "synthesize-default-resources/lambda-ecr-1", "children": { "ServiceRole": { "id": "ServiceRole", - "path": "env-agnostic-test/lambda-ecr/ServiceRole", + "path": "synthesize-default-resources/lambda-ecr-1/ServiceRole", "children": { "ImportServiceRole": { "id": "ImportServiceRole", - "path": "env-agnostic-test/lambda-ecr/ServiceRole/ImportServiceRole", + "path": "synthesize-default-resources/lambda-ecr-1/ServiceRole/ImportServiceRole", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" } }, "Resource": { "id": "Resource", - "path": "env-agnostic-test/lambda-ecr/ServiceRole/Resource", + "path": "synthesize-default-resources/lambda-ecr-1/ServiceRole/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::IAM::Role", "aws:cdk:cloudformation:props": { @@ -179,56 +179,56 @@ } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" } }, "AssetImage": { "id": "AssetImage", - "path": "env-agnostic-test/lambda-ecr/AssetImage", + "path": "synthesize-default-resources/lambda-ecr-1/AssetImage", "children": { "Staging": { "id": "Staging", - "path": "env-agnostic-test/lambda-ecr/AssetImage/Staging", + "path": "synthesize-default-resources/lambda-ecr-1/AssetImage/Staging", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.AssetStaging", + "version": "0.0.0" } }, "Repository": { "id": "Repository", - "path": "env-agnostic-test/lambda-ecr/AssetImage/Repository", + "path": "synthesize-default-resources/lambda-ecr-1/AssetImage/Repository", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_ecr.RepositoryBase", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_ecr_assets.DockerImageAsset", + "version": "0.0.0" } }, "Resource": { "id": "Resource", - "path": "env-agnostic-test/lambda-ecr/Resource", + "path": "synthesize-default-resources/lambda-ecr-1/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::Lambda::Function", "aws:cdk:cloudformation:props": { "code": { "imageUri": { - "Fn::Sub": "${AWS::AccountId}.dkr.ecr.${AWS::Region}.${AWS::URLSuffix}/ecr-asset-1:16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622" + "Fn::Sub": "${AWS::AccountId}.dkr.ecr.${AWS::Region}.${AWS::URLSuffix}/default-resources/ecr-asset:16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622" } }, "role": { "Fn::GetAtt": [ - "lambdaecrServiceRoleFC667F6F", + "lambdaecr1ServiceRoleA6BBC49F", "Arn" ] }, @@ -236,41 +236,277 @@ } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_lambda.CfnFunction", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.Function", + "version": "0.0.0" + } + }, + "lambda-ecr-1-copy": { + "id": "lambda-ecr-1-copy", + "path": "synthesize-default-resources/lambda-ecr-1-copy", + "children": { + "ServiceRole": { + "id": "ServiceRole", + "path": "synthesize-default-resources/lambda-ecr-1-copy/ServiceRole", + "children": { + "ImportServiceRole": { + "id": "ImportServiceRole", + "path": "synthesize-default-resources/lambda-ecr-1-copy/ServiceRole/ImportServiceRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "synthesize-default-resources/lambda-ecr-1-copy/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-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "AssetImage": { + "id": "AssetImage", + "path": "synthesize-default-resources/lambda-ecr-1-copy/AssetImage", + "children": { + "Staging": { + "id": "Staging", + "path": "synthesize-default-resources/lambda-ecr-1-copy/AssetImage/Staging", + "constructInfo": { + "fqn": "aws-cdk-lib.AssetStaging", + "version": "0.0.0" + } + }, + "Repository": { + "id": "Repository", + "path": "synthesize-default-resources/lambda-ecr-1-copy/AssetImage/Repository", + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecr.RepositoryBase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecr_assets.DockerImageAsset", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "synthesize-default-resources/lambda-ecr-1-copy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Function", + "aws:cdk:cloudformation:props": { + "code": { + "imageUri": { + "Fn::Sub": "${AWS::AccountId}.dkr.ecr.${AWS::Region}.${AWS::URLSuffix}/default-resources/ecr-asset:16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622" + } + }, + "role": { + "Fn::GetAtt": [ + "lambdaecr1copyServiceRole2A9FAF5F", + "Arn" + ] + }, + "packageType": "Image" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.CfnFunction", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.Function", + "version": "0.0.0" + } + }, + "lambda-ecr-2": { + "id": "lambda-ecr-2", + "path": "synthesize-default-resources/lambda-ecr-2", + "children": { + "ServiceRole": { + "id": "ServiceRole", + "path": "synthesize-default-resources/lambda-ecr-2/ServiceRole", + "children": { + "ImportServiceRole": { + "id": "ImportServiceRole", + "path": "synthesize-default-resources/lambda-ecr-2/ServiceRole/ImportServiceRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "synthesize-default-resources/lambda-ecr-2/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-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "AssetImage": { + "id": "AssetImage", + "path": "synthesize-default-resources/lambda-ecr-2/AssetImage", + "children": { + "Staging": { + "id": "Staging", + "path": "synthesize-default-resources/lambda-ecr-2/AssetImage/Staging", + "constructInfo": { + "fqn": "aws-cdk-lib.AssetStaging", + "version": "0.0.0" + } + }, + "Repository": { + "id": "Repository", + "path": "synthesize-default-resources/lambda-ecr-2/AssetImage/Repository", + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecr.RepositoryBase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecr_assets.DockerImageAsset", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "synthesize-default-resources/lambda-ecr-2/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Function", + "aws:cdk:cloudformation:props": { + "code": { + "imageUri": { + "Fn::Sub": "${AWS::AccountId}.dkr.ecr.${AWS::Region}.${AWS::URLSuffix}/default-resources/ecr-asset-2:16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622" + } + }, + "role": { + "Fn::GetAtt": [ + "lambdaecr2ServiceRole2EA363D2", + "Arn" + ] + }, + "packageType": "Image" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.CfnFunction", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_lambda.Function", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" } }, - "StagingStack-envAgnostic-ACCOUNT-REGION": { - "id": "StagingStack-envAgnostic-ACCOUNT-REGION", - "path": "StagingStack-envAgnostic-ACCOUNT-REGION", + "StagingStack-default-resources-ACCOUNT-REGION": { + "id": "StagingStack-default-resources-ACCOUNT-REGION", + "path": "StagingStack-default-resources-ACCOUNT-REGION", "children": { - "CdkFilePublishingRole": { - "id": "CdkFilePublishingRole", - "path": "StagingStack-envAgnostic-ACCOUNT-REGION/CdkFilePublishingRole", + "CdkFileRole": { + "id": "CdkFileRole", + "path": "StagingStack-default-resources-ACCOUNT-REGION/CdkFileRole", "children": { - "ImportCdkFilePublishingRole": { - "id": "ImportCdkFilePublishingRole", - "path": "StagingStack-envAgnostic-ACCOUNT-REGION/CdkFilePublishingRole/ImportCdkFilePublishingRole", + "ImportCdkFileRole": { + "id": "ImportCdkFileRole", + "path": "StagingStack-default-resources-ACCOUNT-REGION/CdkFileRole/ImportCdkFileRole", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" } }, "Resource": { "id": "Resource", - "path": "StagingStack-envAgnostic-ACCOUNT-REGION/CdkFilePublishingRole/Resource", + "path": "StagingStack-default-resources-ACCOUNT-REGION/CdkFileRole/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::IAM::Role", "aws:cdk:cloudformation:props": { @@ -305,7 +541,7 @@ "Fn::Join": [ "", [ - "cdk-envagnostic-file-publishing-role-", + "cdk-default-resources-file-role-", { "Ref": "AWS::Region" } @@ -315,17 +551,17 @@ } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" } }, "DefaultPolicy": { "id": "DefaultPolicy", - "path": "StagingStack-envAgnostic-ACCOUNT-REGION/CdkFilePublishingRole/DefaultPolicy", + "path": "StagingStack-default-resources-ACCOUNT-REGION/CdkFileRole/DefaultPolicy", "children": { "Resource": { "id": "Resource", - "path": "StagingStack-envAgnostic-ACCOUNT-REGION/CdkFilePublishingRole/DefaultPolicy/Resource", + "path": "StagingStack-default-resources-ACCOUNT-REGION/CdkFileRole/DefaultPolicy/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::IAM::Policy", "aws:cdk:cloudformation:props": { @@ -387,38 +623,38 @@ ], "Version": "2012-10-17" }, - "policyName": "CdkFilePublishingRoleDefaultPolicyE914275E", + "policyName": "CdkFileRoleDefaultPolicy621C7E5B", "roles": [ { - "Ref": "CdkFilePublishingRole119E2944" + "Ref": "CdkFileRoleE26CEABA" } ] } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_iam.Policy", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" } }, "BucketKey": { "id": "BucketKey", - "path": "StagingStack-envAgnostic-ACCOUNT-REGION/BucketKey", + "path": "StagingStack-default-resources-ACCOUNT-REGION/BucketKey", "children": { "Resource": { "id": "Resource", - "path": "StagingStack-envAgnostic-ACCOUNT-REGION/BucketKey/Resource", + "path": "StagingStack-default-resources-ACCOUNT-REGION/BucketKey/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::KMS::Key", "aws:cdk:cloudformation:props": { @@ -491,21 +727,21 @@ } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_kms.CfnKey", + "version": "0.0.0" } }, "Alias": { "id": "Alias", - "path": "StagingStack-envAgnostic-ACCOUNT-REGION/BucketKey/Alias", + "path": "StagingStack-default-resources-ACCOUNT-REGION/BucketKey/Alias", "children": { "Resource": { "id": "Resource", - "path": "StagingStack-envAgnostic-ACCOUNT-REGION/BucketKey/Alias/Resource", + "path": "StagingStack-default-resources-ACCOUNT-REGION/BucketKey/Alias/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::KMS::Alias", "aws:cdk:cloudformation:props": { - "aliasName": "alias/cdk-envagnostic-staging", + "aliasName": "alias/cdk-default-resources-staging", "targetKeyId": { "Fn::GetAtt": [ "BucketKey7092080A", @@ -515,29 +751,29 @@ } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_kms.CfnAlias", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_kms.Alias", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_kms.Key", + "version": "0.0.0" } }, "CdkStagingBucket": { "id": "CdkStagingBucket", - "path": "StagingStack-envAgnostic-ACCOUNT-REGION/CdkStagingBucket", + "path": "StagingStack-default-resources-ACCOUNT-REGION/CdkStagingBucket", "children": { "Resource": { "id": "Resource", - "path": "StagingStack-envAgnostic-ACCOUNT-REGION/CdkStagingBucket/Resource", + "path": "StagingStack-default-resources-ACCOUNT-REGION/CdkStagingBucket/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::S3::Bucket", "aws:cdk:cloudformation:props": { @@ -560,7 +796,7 @@ "Fn::Join": [ "", [ - "cdk-envagnostic-staging-", + "cdk-default-resources-staging-", { "Ref": "AWS::AccountId" }, @@ -592,17 +828,17 @@ } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_s3.CfnBucket", + "version": "0.0.0" } }, "Policy": { "id": "Policy", - "path": "StagingStack-envAgnostic-ACCOUNT-REGION/CdkStagingBucket/Policy", + "path": "StagingStack-default-resources-ACCOUNT-REGION/CdkStagingBucket/Policy", "children": { "Resource": { "id": "Resource", - "path": "StagingStack-envAgnostic-ACCOUNT-REGION/CdkStagingBucket/Policy/Resource", + "path": "StagingStack-default-resources-ACCOUNT-REGION/CdkStagingBucket/Policy/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::S3::BucketPolicy", "aws:cdk:cloudformation:props": { @@ -706,37 +942,37 @@ } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_s3.CfnBucketPolicy", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_s3.BucketPolicy", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_s3.Bucket", + "version": "0.0.0" } }, - "CdkImagePublishingRole": { - "id": "CdkImagePublishingRole", - "path": "StagingStack-envAgnostic-ACCOUNT-REGION/CdkImagePublishingRole", + "CdkImageRole": { + "id": "CdkImageRole", + "path": "StagingStack-default-resources-ACCOUNT-REGION/CdkImageRole", "children": { - "ImportCdkImagePublishingRole": { - "id": "ImportCdkImagePublishingRole", - "path": "StagingStack-envAgnostic-ACCOUNT-REGION/CdkImagePublishingRole/ImportCdkImagePublishingRole", + "ImportCdkImageRole": { + "id": "ImportCdkImageRole", + "path": "StagingStack-default-resources-ACCOUNT-REGION/CdkImageRole/ImportCdkImageRole", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" } }, "Resource": { "id": "Resource", - "path": "StagingStack-envAgnostic-ACCOUNT-REGION/CdkImagePublishingRole/Resource", + "path": "StagingStack-default-resources-ACCOUNT-REGION/CdkImageRole/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::IAM::Role", "aws:cdk:cloudformation:props": { @@ -771,7 +1007,7 @@ "Fn::Join": [ "", [ - "cdk-envagnostic-asset-publishing-role-", + "cdk-default-resources-asset-role-", { "Ref": "AWS::Region" } @@ -781,17 +1017,17 @@ } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" } }, "DefaultPolicy": { "id": "DefaultPolicy", - "path": "StagingStack-envAgnostic-ACCOUNT-REGION/CdkImagePublishingRole/DefaultPolicy", + "path": "StagingStack-default-resources-ACCOUNT-REGION/CdkImageRole/DefaultPolicy", "children": { "Resource": { "id": "Resource", - "path": "StagingStack-envAgnostic-ACCOUNT-REGION/CdkImagePublishingRole/DefaultPolicy/Resource", + "path": "StagingStack-default-resources-ACCOUNT-REGION/CdkImageRole/DefaultPolicy/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::IAM::Policy", "aws:cdk:cloudformation:props": { @@ -810,12 +1046,20 @@ "ecr:UploadLayerPart" ], "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "ecrasset155BFF0C3", - "Arn" - ] - } + "Resource": [ + { + "Fn::GetAtt": [ + "defaultresourcesecrasset2FBE6B8A9", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "defaultresourcesecrasset9191BD6E", + "Arn" + ] + } + ] }, { "Action": "ecr:GetAuthorizationToken", @@ -825,59 +1069,89 @@ ], "Version": "2012-10-17" }, - "policyName": "CdkImagePublishingRoleDefaultPolicy7638FA58", + "policyName": "CdkImageRoleDefaultPolicy4A1572DE", "roles": [ { - "Ref": "CdkImagePublishingRole87E59CEF" + "Ref": "CdkImageRoleF1394AC3" } ] } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_iam.Policy", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" } }, - "ecr-asset-1": { - "id": "ecr-asset-1", - "path": "StagingStack-envAgnostic-ACCOUNT-REGION/ecr-asset-1", + "default-resources--ecr-asset": { + "id": "default-resources--ecr-asset", + "path": "StagingStack-default-resources-ACCOUNT-REGION/default-resources--ecr-asset", "children": { "Resource": { "id": "Resource", - "path": "StagingStack-envAgnostic-ACCOUNT-REGION/ecr-asset-1/Resource", + "path": "StagingStack-default-resources-ACCOUNT-REGION/default-resources--ecr-asset/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::ECR::Repository", "aws:cdk:cloudformation:props": { - "repositoryName": "ecr-asset-1" + "lifecyclePolicy": { + "lifecyclePolicyText": "{\"rules\":[{\"rulePriority\":1,\"description\":\"Garbage collect old image versions and keep the specified number of latest versions\",\"selection\":{\"tagStatus\":\"any\",\"countType\":\"imageCountMoreThan\",\"countNumber\":3},\"action\":{\"type\":\"expire\"}}]}" + }, + "repositoryName": "default-resources/ecr-asset" } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_ecr.CfnRepository", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecr.Repository", + "version": "0.0.0" + } + }, + "default-resources--ecr-asset-2": { + "id": "default-resources--ecr-asset-2", + "path": "StagingStack-default-resources-ACCOUNT-REGION/default-resources--ecr-asset-2", + "children": { + "Resource": { + "id": "Resource", + "path": "StagingStack-default-resources-ACCOUNT-REGION/default-resources--ecr-asset-2/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ECR::Repository", + "aws:cdk:cloudformation:props": { + "lifecyclePolicy": { + "lifecyclePolicyText": "{\"rules\":[{\"rulePriority\":1,\"description\":\"Garbage collect old image versions and keep the specified number of latest versions\",\"selection\":{\"tagStatus\":\"any\",\"countType\":\"imageCountMoreThan\",\"countNumber\":3},\"action\":{\"type\":\"expire\"}}]}" + }, + "repositoryName": "default-resources/ecr-asset-2" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecr.CfnRepository", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.aws_ecr.Repository", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" } }, "integ-tests": { @@ -893,7 +1167,7 @@ "path": "integ-tests/DefaultTest/Default", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.270" + "version": "10.2.9" } }, "DeployAssert": { @@ -904,22 +1178,22 @@ "id": "BootstrapVersion", "path": "integ-tests/DefaultTest/DeployAssert/BootstrapVersion", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" } }, "CheckBootstrapVersion": { "id": "CheckBootstrapVersion", "path": "integ-tests/DefaultTest/DeployAssert/CheckBootstrapVersion", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" } } }, @@ -939,13 +1213,13 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.270" + "version": "10.2.9" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.ts b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.ts new file mode 100644 index 0000000000000..e8f9aa1e27682 --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.ts @@ -0,0 +1,51 @@ +import * as path from 'path'; +import * as integ from '@aws-cdk/integ-tests-alpha'; +import { App, Stack } from 'aws-cdk-lib'; +import * as lambda from 'aws-cdk-lib/aws-lambda'; +import { AppStagingSynthesizer } from '../lib'; + +const app = new App(); + +const stack = new Stack(app, 'synthesize-default-resources', { + synthesizer: AppStagingSynthesizer.defaultResources({ + appId: 'default-resources', + }), +}); + +new lambda.Function(stack, 'lambda-s3', { + code: lambda.AssetCode.fromAsset(path.join(__dirname, 'assets')), + handler: 'index.handler', + runtime: lambda.Runtime.PYTHON_3_10, +}); + +new lambda.Function(stack, 'lambda-ecr-1', { + code: lambda.EcrImageCode.fromAssetImage(path.join(__dirname, 'assets'), { + assetName: 'ecr-asset', + }), + handler: lambda.Handler.FROM_IMAGE, + runtime: lambda.Runtime.FROM_IMAGE, +}); + +// This lambda will share the same published asset as lambda-ecr-1 +new lambda.Function(stack, 'lambda-ecr-1-copy', { + code: lambda.EcrImageCode.fromAssetImage(path.join(__dirname, 'assets'), { + assetName: 'ecr-asset', + }), + handler: lambda.Handler.FROM_IMAGE, + runtime: lambda.Runtime.FROM_IMAGE, +}); + +// This lambda will use a different published asset as lambda-ecr-1 +new lambda.Function(stack, 'lambda-ecr-2', { + code: lambda.EcrImageCode.fromAssetImage(path.join(__dirname, 'assets'), { + assetName: 'ecr-asset-2', + }), + handler: lambda.Handler.FROM_IMAGE, + runtime: lambda.Runtime.FROM_IMAGE, +}); + +new integ.IntegTest(app, 'integ-tests', { + testCases: [stack], +}); + +app.synth(); diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack-synthassets-489318732371-us-east-1.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack-synthassets-489318732371-us-east-1.template.json deleted file mode 100644 index f45ae29d14ca0..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/StagingStack-synthassets-489318732371-us-east-1.template.json +++ /dev/null @@ -1,345 +0,0 @@ -{ - "Resources": { - "CdkFilePublishingRole119E2944": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "AWS": "arn:aws:iam::489318732371:root" - } - } - ], - "Version": "2012-10-17" - }, - "RoleName": "cdk-synthassets-file-publishing-role-us-east-1" - } - }, - "CdkFilePublishingRoleDefaultPolicyE914275E": { - "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": [ - "CdkStagingBucket1636058C", - "Arn" - ] - }, - { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "CdkStagingBucket1636058C", - "Arn" - ] - }, - "/*" - ] - ] - } - ] - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:GenerateDataKey*", - "kms:ReEncrypt*" - ], - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "BucketKey7092080A", - "Arn" - ] - } - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "CdkFilePublishingRoleDefaultPolicyE914275E", - "Roles": [ - { - "Ref": "CdkFilePublishingRole119E2944" - } - ] - } - }, - "BucketKey7092080A": { - "Type": "AWS::KMS::Key", - "Properties": { - "KeyPolicy": { - "Statement": [ - { - "Action": "kms:*", - "Effect": "Allow", - "Principal": { - "AWS": "arn:aws:iam::489318732371:root" - }, - "Resource": "*" - }, - { - "Action": [ - "kms:CancelKeyDeletion", - "kms:Create*", - "kms:Delete*", - "kms:Describe*", - "kms:Disable*", - "kms:Enable*", - "kms:Get*", - "kms:List*", - "kms:Put*", - "kms:Revoke*", - "kms:ScheduleKeyDeletion", - "kms:TagResource", - "kms:UntagResource", - "kms:Update*" - ], - "Effect": "Allow", - "Principal": { - "AWS": "arn:aws:iam::489318732371:root" - }, - "Resource": "*" - } - ], - "Version": "2012-10-17" - } - }, - "UpdateReplacePolicy": "Retain", - "DeletionPolicy": "Retain" - }, - "BucketKeyAlias69A0886F": { - "Type": "AWS::KMS::Alias", - "Properties": { - "AliasName": "alias/cdk-synthassets-staging", - "TargetKeyId": { - "Fn::GetAtt": [ - "BucketKey7092080A", - "Arn" - ] - } - } - }, - "CdkStagingBucket1636058C": { - "Type": "AWS::S3::Bucket", - "Properties": { - "BucketEncryption": { - "ServerSideEncryptionConfiguration": [ - { - "ServerSideEncryptionByDefault": { - "KMSMasterKeyID": { - "Fn::GetAtt": [ - "BucketKey7092080A", - "Arn" - ] - }, - "SSEAlgorithm": "aws:kms" - } - } - ] - }, - "BucketName": "cdk-synthassets-staging-489318732371-us-east-1", - "LifecycleConfiguration": { - "Rules": [ - { - "NoncurrentVersionExpiration": { - "NoncurrentDays": 365 - }, - "Status": "Enabled" - }, - { - "ExpirationInDays": 30, - "Prefix": "handoff/", - "Status": "Enabled" - } - ] - }, - "VersioningConfiguration": { - "Status": "Enabled" - } - }, - "UpdateReplacePolicy": "Retain", - "DeletionPolicy": "Retain" - }, - "CdkStagingBucketPolicy42BD1F92": { - "Type": "AWS::S3::BucketPolicy", - "Properties": { - "Bucket": { - "Ref": "CdkStagingBucket1636058C" - }, - "PolicyDocument": { - "Statement": [ - { - "Action": "s3:*", - "Condition": { - "Bool": { - "aws:SecureTransport": "false" - } - }, - "Effect": "Deny", - "Principal": { - "AWS": "*" - }, - "Resource": [ - { - "Fn::GetAtt": [ - "CdkStagingBucket1636058C", - "Arn" - ] - }, - { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "CdkStagingBucket1636058C", - "Arn" - ] - }, - "/*" - ] - ] - } - ] - }, - { - "Action": [ - "s3:GetBucket*", - "s3:GetObject*", - "s3:List*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::489318732371:role/cdk-hnb659fds-deploy-role-489318732371-us-east-1" - ] - ] - } - }, - "Resource": [ - { - "Fn::GetAtt": [ - "CdkStagingBucket1636058C", - "Arn" - ] - }, - { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "CdkStagingBucket1636058C", - "Arn" - ] - }, - "/*" - ] - ] - } - ] - } - ], - "Version": "2012-10-17" - } - } - }, - "CdkImagePublishingRole87E59CEF": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "AWS": "arn:aws:iam::489318732371:root" - } - } - ], - "Version": "2012-10-17" - }, - "RoleName": "cdk-synthassets-asset-publishing-role-us-east-1" - } - }, - "CdkImagePublishingRoleDefaultPolicy7638FA58": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": [ - "ecr:BatchCheckLayerAvailability", - "ecr:BatchGetImage", - "ecr:CompleteLayerUpload", - "ecr:DescribeImages", - "ecr:DescribeRepositories", - "ecr:GetDownloadUrlForLayer", - "ecr:InitiateLayerUpload", - "ecr:PutImage", - "ecr:UploadLayerPart" - ], - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "ecrasset9899265E", - "Arn" - ] - } - }, - { - "Action": "ecr:GetAuthorizationToken", - "Effect": "Allow", - "Resource": "*" - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "CdkImagePublishingRoleDefaultPolicy7638FA58", - "Roles": [ - { - "Ref": "CdkImagePublishingRole87E59CEF" - } - ] - } - }, - "ecrasset9899265E": { - "Type": "AWS::ECR::Repository", - "Properties": { - "RepositoryName": "ecr-asset" - }, - "UpdateReplacePolicy": "Retain", - "DeletionPolicy": "Retain" - } - } -} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/Dockerfile b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/Dockerfile deleted file mode 100644 index 4a015204a5983..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/Dockerfile +++ /dev/null @@ -1,2 +0,0 @@ -FROM public.ecr.aws/lambda/python:3.10 -CMD echo hello world \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/index.py b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/index.py deleted file mode 100644 index ed0f110e2e61e..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/index.py +++ /dev/null @@ -1 +0,0 @@ -print('hello') \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/Dockerfile b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/Dockerfile deleted file mode 100644 index 4a015204a5983..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/Dockerfile +++ /dev/null @@ -1,2 +0,0 @@ -FROM public.ecr.aws/lambda/python:3.10 -CMD echo hello world \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/index.py b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/index.py deleted file mode 100644 index ed0f110e2e61e..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/index.py +++ /dev/null @@ -1 +0,0 @@ -print('hello') \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/cdk.out b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/cdk.out deleted file mode 100644 index 7925065efbcc4..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/cdk.out +++ /dev/null @@ -1 +0,0 @@ -{"version":"31.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json deleted file mode 100644 index 7526fee9ff76c..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "version": "31.0.0", - "files": { - "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { - "source": { - "path": "integtestsDefaultTestDeployAssert44C8D370.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/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.template.json deleted file mode 100644 index ad9d0fb73d1dd..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.template.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "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/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json deleted file mode 100644 index 6209d2d7b7354..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/manifest.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "version": "31.0.0", - "artifacts": { - "synth-test.assets": { - "type": "cdk:asset-manifest", - "properties": { - "file": "synth-test.assets.json" - } - }, - "synth-test": { - "type": "aws:cloudformation:stack", - "environment": "aws://489318732371/us-east-1", - "properties": { - "templateFile": "synth-test.template.json", - "validateOnSynth": false, - "assumeRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-hnb659fds-deploy-role-489318732371-us-east-1", - "additionalDependencies": [ - "synth-test.assets" - ], - "stackTemplateAssetObjectUrl": "s3://cdk-synthassets-staging-489318732371-us-east-1/cfc9d13f0af5f1be9708022bc174c46ec539fe35b16b31cf5b337f70ddb47bc6.json", - "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-hnb659fds-cfn-exec-role-489318732371-us-east-1", - "lookupRole": { - "arn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-hnb659fds-lookup-role-489318732371-us-east-1" - } - }, - "dependencies": [ - "StagingStack-synthassets-489318732371-us-east-1", - "synth-test.assets" - ], - "metadata": { - "/synth-test/lambda-s3/ServiceRole/Resource": [ - { - "type": "aws:cdk:logicalId", - "data": "lambdas3ServiceRoleC9EDE33A" - } - ], - "/synth-test/lambda-s3/Resource": [ - { - "type": "aws:cdk:logicalId", - "data": "lambdas342CE2BBD" - } - ], - "/synth-test/lambda-ecr/ServiceRole/Resource": [ - { - "type": "aws:cdk:logicalId", - "data": "lambdaecrServiceRoleFC667F6F" - } - ], - "/synth-test/lambda-ecr/Resource": [ - { - "type": "aws:cdk:logicalId", - "data": "lambdaecr8D55A032" - } - ], - "lambdaServiceRole494E4CA6": [ - { - "type": "aws:cdk:logicalId", - "data": "lambdaServiceRole494E4CA6", - "trace": [ - "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" - ] - } - ], - "lambda8B5974B5": [ - { - "type": "aws:cdk:logicalId", - "data": "lambda8B5974B5", - "trace": [ - "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" - ] - } - ] - }, - "displayName": "synth-test" - }, - "StagingStack-synthassets-489318732371-us-east-1": { - "type": "aws:cloudformation:stack", - "environment": "aws://489318732371/us-east-1", - "properties": { - "templateFile": "StagingStack-synthassets-489318732371-us-east-1.template.json", - "validateOnSynth": false, - "assumeRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-hnb659fds-deploy-role-489318732371-us-east-1", - "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-hnb659fds-cfn-exec-role-489318732371-us-east-1", - "stackName": "StagingStack-synthassets" - }, - "metadata": { - "/StagingStack-synthassets-489318732371-us-east-1/CdkFilePublishingRole/Resource": [ - { - "type": "aws:cdk:logicalId", - "data": "CdkFilePublishingRole119E2944" - } - ], - "/StagingStack-synthassets-489318732371-us-east-1/CdkFilePublishingRole/DefaultPolicy/Resource": [ - { - "type": "aws:cdk:logicalId", - "data": "CdkFilePublishingRoleDefaultPolicyE914275E" - } - ], - "/StagingStack-synthassets-489318732371-us-east-1/BucketKey/Resource": [ - { - "type": "aws:cdk:logicalId", - "data": "BucketKey7092080A" - } - ], - "/StagingStack-synthassets-489318732371-us-east-1/BucketKey/Alias/Resource": [ - { - "type": "aws:cdk:logicalId", - "data": "BucketKeyAlias69A0886F" - } - ], - "/StagingStack-synthassets-489318732371-us-east-1/CdkStagingBucket/Resource": [ - { - "type": "aws:cdk:logicalId", - "data": "CdkStagingBucket1636058C" - } - ], - "/StagingStack-synthassets-489318732371-us-east-1/CdkStagingBucket/Policy/Resource": [ - { - "type": "aws:cdk:logicalId", - "data": "CdkStagingBucketPolicy42BD1F92" - } - ], - "/StagingStack-synthassets-489318732371-us-east-1/CdkImagePublishingRole/Resource": [ - { - "type": "aws:cdk:logicalId", - "data": "CdkImagePublishingRole87E59CEF" - } - ], - "/StagingStack-synthassets-489318732371-us-east-1/CdkImagePublishingRole/DefaultPolicy/Resource": [ - { - "type": "aws:cdk:logicalId", - "data": "CdkImagePublishingRoleDefaultPolicy7638FA58" - } - ], - "/StagingStack-synthassets-489318732371-us-east-1/ecr-asset/Resource": [ - { - "type": "aws:cdk:logicalId", - "data": "ecrasset9899265E" - } - ] - }, - "displayName": "StagingStack-synthassets-489318732371-us-east-1" - }, - "integtestsDefaultTestDeployAssert44C8D370.assets": { - "type": "cdk:asset-manifest", - "properties": { - "file": "integtestsDefaultTestDeployAssert44C8D370.assets.json", - "requiresBootstrapStackVersion": 6, - "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" - } - }, - "integtestsDefaultTestDeployAssert44C8D370": { - "type": "aws:cloudformation:stack", - "environment": "aws://unknown-account/unknown-region", - "properties": { - "templateFile": "integtestsDefaultTestDeployAssert44C8D370.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": [ - "integtestsDefaultTestDeployAssert44C8D370.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": [ - "integtestsDefaultTestDeployAssert44C8D370.assets" - ], - "metadata": { - "/integ-tests/DefaultTest/DeployAssert/BootstrapVersion": [ - { - "type": "aws:cdk:logicalId", - "data": "BootstrapVersion" - } - ], - "/integ-tests/DefaultTest/DeployAssert/CheckBootstrapVersion": [ - { - "type": "aws:cdk:logicalId", - "data": "CheckBootstrapVersion" - } - ] - }, - "displayName": "integ-tests/DefaultTest/DeployAssert" - }, - "Tree": { - "type": "cdk:tree", - "properties": { - "file": "tree.json" - } - } - } -} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synth-test.assets.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synth-test.assets.json deleted file mode 100644 index dbfe70324a21a..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synth-test.assets.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "version": "31.0.0", - "files": { - "68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650": { - "source": { - "path": "asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650", - "packaging": "zip" - }, - "destinations": { - "489318732371-us-east-1": { - "bucketName": "cdk-synthassets-staging-489318732371-us-east-1", - "objectKey": "68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650.zip", - "region": "us-east-1", - "assumeRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-synthassets-file-publishing-role-us-east-1" - } - } - }, - "cfc9d13f0af5f1be9708022bc174c46ec539fe35b16b31cf5b337f70ddb47bc6": { - "source": { - "path": "synth-test.template.json", - "packaging": "file" - }, - "destinations": { - "489318732371-us-east-1": { - "bucketName": "cdk-synthassets-staging-489318732371-us-east-1", - "objectKey": "cfc9d13f0af5f1be9708022bc174c46ec539fe35b16b31cf5b337f70ddb47bc6.json", - "region": "us-east-1", - "assumeRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-synthassets-file-publishing-role-us-east-1" - } - } - } - }, - "dockerImages": { - "16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622": { - "source": { - "directory": "asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622" - }, - "destinations": { - "489318732371-us-east-1": { - "repositoryName": "ecr-asset", - "imageTag": "16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622", - "region": "us-east-1", - "assumeRoleArn": "arn:${AWS::Partition}:iam::489318732371:role/cdk-synthassets-asset-publishing-role-us-east-1" - } - } - } - } -} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synth-test.template.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synth-test.template.json deleted file mode 100644 index 7115b687f43f5..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/synth-test.template.json +++ /dev/null @@ -1,106 +0,0 @@ -{ - "Resources": { - "lambdas3ServiceRoleC9EDE33A": { - "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" - ] - ] - } - ] - } - }, - "lambdas342CE2BBD": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "S3Bucket": "cdk-synthassets-staging-489318732371-us-east-1", - "S3Key": "68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650.zip" - }, - "Role": { - "Fn::GetAtt": [ - "lambdas3ServiceRoleC9EDE33A", - "Arn" - ] - }, - "Handler": "index.handler", - "Runtime": "python3.10" - }, - "DependsOn": [ - "lambdas3ServiceRoleC9EDE33A" - ] - }, - "lambdaecrServiceRoleFC667F6F": { - "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" - ] - ] - } - ] - } - }, - "lambdaecr8D55A032": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "ImageUri": { - "Fn::Sub": "489318732371.dkr.ecr.us-east-1.${AWS::URLSuffix}/ecr-asset:16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622" - } - }, - "Role": { - "Fn::GetAtt": [ - "lambdaecrServiceRoleFC667F6F", - "Arn" - ] - }, - "PackageType": "Image" - }, - "DependsOn": [ - "lambdaecrServiceRoleFC667F6F" - ] - } - } -} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json deleted file mode 100644 index 3f5e077678c1b..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.js.snapshot/tree.json +++ /dev/null @@ -1,844 +0,0 @@ -{ - "version": "tree-0.1", - "tree": { - "id": "App", - "path": "", - "children": { - "synth-test": { - "id": "synth-test", - "path": "synth-test", - "children": { - "lambda-s3": { - "id": "lambda-s3", - "path": "synth-test/lambda-s3", - "children": { - "ServiceRole": { - "id": "ServiceRole", - "path": "synth-test/lambda-s3/ServiceRole", - "children": { - "ImportServiceRole": { - "id": "ImportServiceRole", - "path": "synth-test/lambda-s3/ServiceRole/ImportServiceRole", - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - }, - "Resource": { - "id": "Resource", - "path": "synth-test/lambda-s3/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": "constructs.Construct", - "version": "10.1.270" - } - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - }, - "Code": { - "id": "Code", - "path": "synth-test/lambda-s3/Code", - "children": { - "Stage": { - "id": "Stage", - "path": "synth-test/lambda-s3/Code/Stage", - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - }, - "AssetBucket": { - "id": "AssetBucket", - "path": "synth-test/lambda-s3/Code/AssetBucket", - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - }, - "Resource": { - "id": "Resource", - "path": "synth-test/lambda-s3/Resource", - "attributes": { - "aws:cdk:cloudformation:type": "AWS::Lambda::Function", - "aws:cdk:cloudformation:props": { - "code": { - "s3Bucket": "cdk-synthassets-staging-489318732371-us-east-1", - "s3Key": "68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650.zip" - }, - "role": { - "Fn::GetAtt": [ - "lambdas3ServiceRoleC9EDE33A", - "Arn" - ] - }, - "handler": "index.handler", - "runtime": "python3.10" - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - }, - "lambda-ecr": { - "id": "lambda-ecr", - "path": "synth-test/lambda-ecr", - "children": { - "ServiceRole": { - "id": "ServiceRole", - "path": "synth-test/lambda-ecr/ServiceRole", - "children": { - "ImportServiceRole": { - "id": "ImportServiceRole", - "path": "synth-test/lambda-ecr/ServiceRole/ImportServiceRole", - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - }, - "Resource": { - "id": "Resource", - "path": "synth-test/lambda-ecr/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": "constructs.Construct", - "version": "10.1.270" - } - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - }, - "AssetImage": { - "id": "AssetImage", - "path": "synth-test/lambda-ecr/AssetImage", - "children": { - "Staging": { - "id": "Staging", - "path": "synth-test/lambda-ecr/AssetImage/Staging", - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - }, - "Repository": { - "id": "Repository", - "path": "synth-test/lambda-ecr/AssetImage/Repository", - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - }, - "Resource": { - "id": "Resource", - "path": "synth-test/lambda-ecr/Resource", - "attributes": { - "aws:cdk:cloudformation:type": "AWS::Lambda::Function", - "aws:cdk:cloudformation:props": { - "code": { - "imageUri": { - "Fn::Sub": "489318732371.dkr.ecr.us-east-1.${AWS::URLSuffix}/ecr-asset:16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622" - } - }, - "role": { - "Fn::GetAtt": [ - "lambdaecrServiceRoleFC667F6F", - "Arn" - ] - }, - "packageType": "Image" - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - }, - "StagingStack-synthassets-489318732371-us-east-1": { - "id": "StagingStack-synthassets-489318732371-us-east-1", - "path": "StagingStack-synthassets-489318732371-us-east-1", - "children": { - "CdkFilePublishingRole": { - "id": "CdkFilePublishingRole", - "path": "StagingStack-synthassets-489318732371-us-east-1/CdkFilePublishingRole", - "children": { - "ImportCdkFilePublishingRole": { - "id": "ImportCdkFilePublishingRole", - "path": "StagingStack-synthassets-489318732371-us-east-1/CdkFilePublishingRole/ImportCdkFilePublishingRole", - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - }, - "Resource": { - "id": "Resource", - "path": "StagingStack-synthassets-489318732371-us-east-1/CdkFilePublishingRole/Resource", - "attributes": { - "aws:cdk:cloudformation:type": "AWS::IAM::Role", - "aws:cdk:cloudformation:props": { - "assumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "AWS": "arn:aws:iam::489318732371:root" - } - } - ], - "Version": "2012-10-17" - }, - "roleName": "cdk-synthassets-file-publishing-role-us-east-1" - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - }, - "DefaultPolicy": { - "id": "DefaultPolicy", - "path": "StagingStack-synthassets-489318732371-us-east-1/CdkFilePublishingRole/DefaultPolicy", - "children": { - "Resource": { - "id": "Resource", - "path": "StagingStack-synthassets-489318732371-us-east-1/CdkFilePublishingRole/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": [ - "CdkStagingBucket1636058C", - "Arn" - ] - }, - { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "CdkStagingBucket1636058C", - "Arn" - ] - }, - "/*" - ] - ] - } - ] - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:GenerateDataKey*", - "kms:ReEncrypt*" - ], - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "BucketKey7092080A", - "Arn" - ] - } - } - ], - "Version": "2012-10-17" - }, - "policyName": "CdkFilePublishingRoleDefaultPolicyE914275E", - "roles": [ - { - "Ref": "CdkFilePublishingRole119E2944" - } - ] - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - }, - "BucketKey": { - "id": "BucketKey", - "path": "StagingStack-synthassets-489318732371-us-east-1/BucketKey", - "children": { - "Resource": { - "id": "Resource", - "path": "StagingStack-synthassets-489318732371-us-east-1/BucketKey/Resource", - "attributes": { - "aws:cdk:cloudformation:type": "AWS::KMS::Key", - "aws:cdk:cloudformation:props": { - "keyPolicy": { - "Statement": [ - { - "Action": "kms:*", - "Effect": "Allow", - "Principal": { - "AWS": "arn:aws:iam::489318732371:root" - }, - "Resource": "*" - }, - { - "Action": [ - "kms:CancelKeyDeletion", - "kms:Create*", - "kms:Delete*", - "kms:Describe*", - "kms:Disable*", - "kms:Enable*", - "kms:Get*", - "kms:List*", - "kms:Put*", - "kms:Revoke*", - "kms:ScheduleKeyDeletion", - "kms:TagResource", - "kms:UntagResource", - "kms:Update*" - ], - "Effect": "Allow", - "Principal": { - "AWS": "arn:aws:iam::489318732371:root" - }, - "Resource": "*" - } - ], - "Version": "2012-10-17" - } - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - }, - "Alias": { - "id": "Alias", - "path": "StagingStack-synthassets-489318732371-us-east-1/BucketKey/Alias", - "children": { - "Resource": { - "id": "Resource", - "path": "StagingStack-synthassets-489318732371-us-east-1/BucketKey/Alias/Resource", - "attributes": { - "aws:cdk:cloudformation:type": "AWS::KMS::Alias", - "aws:cdk:cloudformation:props": { - "aliasName": "alias/cdk-synthassets-staging", - "targetKeyId": { - "Fn::GetAtt": [ - "BucketKey7092080A", - "Arn" - ] - } - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - }, - "CdkStagingBucket": { - "id": "CdkStagingBucket", - "path": "StagingStack-synthassets-489318732371-us-east-1/CdkStagingBucket", - "children": { - "Resource": { - "id": "Resource", - "path": "StagingStack-synthassets-489318732371-us-east-1/CdkStagingBucket/Resource", - "attributes": { - "aws:cdk:cloudformation:type": "AWS::S3::Bucket", - "aws:cdk:cloudformation:props": { - "bucketEncryption": { - "serverSideEncryptionConfiguration": [ - { - "serverSideEncryptionByDefault": { - "sseAlgorithm": "aws:kms", - "kmsMasterKeyId": { - "Fn::GetAtt": [ - "BucketKey7092080A", - "Arn" - ] - } - } - } - ] - }, - "bucketName": "cdk-synthassets-staging-489318732371-us-east-1", - "lifecycleConfiguration": { - "rules": [ - { - "noncurrentVersionExpiration": { - "noncurrentDays": 365 - }, - "status": "Enabled" - }, - { - "expirationInDays": 30, - "prefix": "handoff/", - "status": "Enabled" - } - ] - }, - "versioningConfiguration": { - "status": "Enabled" - } - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - }, - "Policy": { - "id": "Policy", - "path": "StagingStack-synthassets-489318732371-us-east-1/CdkStagingBucket/Policy", - "children": { - "Resource": { - "id": "Resource", - "path": "StagingStack-synthassets-489318732371-us-east-1/CdkStagingBucket/Policy/Resource", - "attributes": { - "aws:cdk:cloudformation:type": "AWS::S3::BucketPolicy", - "aws:cdk:cloudformation:props": { - "bucket": { - "Ref": "CdkStagingBucket1636058C" - }, - "policyDocument": { - "Statement": [ - { - "Action": "s3:*", - "Condition": { - "Bool": { - "aws:SecureTransport": "false" - } - }, - "Effect": "Deny", - "Principal": { - "AWS": "*" - }, - "Resource": [ - { - "Fn::GetAtt": [ - "CdkStagingBucket1636058C", - "Arn" - ] - }, - { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "CdkStagingBucket1636058C", - "Arn" - ] - }, - "/*" - ] - ] - } - ] - }, - { - "Action": [ - "s3:GetBucket*", - "s3:GetObject*", - "s3:List*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::489318732371:role/cdk-hnb659fds-deploy-role-489318732371-us-east-1" - ] - ] - } - }, - "Resource": [ - { - "Fn::GetAtt": [ - "CdkStagingBucket1636058C", - "Arn" - ] - }, - { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "CdkStagingBucket1636058C", - "Arn" - ] - }, - "/*" - ] - ] - } - ] - } - ], - "Version": "2012-10-17" - } - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - }, - "CdkImagePublishingRole": { - "id": "CdkImagePublishingRole", - "path": "StagingStack-synthassets-489318732371-us-east-1/CdkImagePublishingRole", - "children": { - "ImportCdkImagePublishingRole": { - "id": "ImportCdkImagePublishingRole", - "path": "StagingStack-synthassets-489318732371-us-east-1/CdkImagePublishingRole/ImportCdkImagePublishingRole", - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - }, - "Resource": { - "id": "Resource", - "path": "StagingStack-synthassets-489318732371-us-east-1/CdkImagePublishingRole/Resource", - "attributes": { - "aws:cdk:cloudformation:type": "AWS::IAM::Role", - "aws:cdk:cloudformation:props": { - "assumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "AWS": "arn:aws:iam::489318732371:root" - } - } - ], - "Version": "2012-10-17" - }, - "roleName": "cdk-synthassets-asset-publishing-role-us-east-1" - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - }, - "DefaultPolicy": { - "id": "DefaultPolicy", - "path": "StagingStack-synthassets-489318732371-us-east-1/CdkImagePublishingRole/DefaultPolicy", - "children": { - "Resource": { - "id": "Resource", - "path": "StagingStack-synthassets-489318732371-us-east-1/CdkImagePublishingRole/DefaultPolicy/Resource", - "attributes": { - "aws:cdk:cloudformation:type": "AWS::IAM::Policy", - "aws:cdk:cloudformation:props": { - "policyDocument": { - "Statement": [ - { - "Action": [ - "ecr:BatchCheckLayerAvailability", - "ecr:BatchGetImage", - "ecr:CompleteLayerUpload", - "ecr:DescribeImages", - "ecr:DescribeRepositories", - "ecr:GetDownloadUrlForLayer", - "ecr:InitiateLayerUpload", - "ecr:PutImage", - "ecr:UploadLayerPart" - ], - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "ecrasset9899265E", - "Arn" - ] - } - }, - { - "Action": "ecr:GetAuthorizationToken", - "Effect": "Allow", - "Resource": "*" - } - ], - "Version": "2012-10-17" - }, - "policyName": "CdkImagePublishingRoleDefaultPolicy7638FA58", - "roles": [ - { - "Ref": "CdkImagePublishingRole87E59CEF" - } - ] - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - }, - "ecr-asset": { - "id": "ecr-asset", - "path": "StagingStack-synthassets-489318732371-us-east-1/ecr-asset", - "children": { - "Resource": { - "id": "Resource", - "path": "StagingStack-synthassets-489318732371-us-east-1/ecr-asset/Resource", - "attributes": { - "aws:cdk:cloudformation:type": "AWS::ECR::Repository", - "aws:cdk:cloudformation:props": { - "repositoryName": "ecr-asset" - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - }, - "integ-tests": { - "id": "integ-tests", - "path": "integ-tests", - "children": { - "DefaultTest": { - "id": "DefaultTest", - "path": "integ-tests/DefaultTest", - "children": { - "Default": { - "id": "Default", - "path": "integ-tests/DefaultTest/Default", - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - }, - "DeployAssert": { - "id": "DeployAssert", - "path": "integ-tests/DefaultTest/DeployAssert", - "children": { - "BootstrapVersion": { - "id": "BootstrapVersion", - "path": "integ-tests/DefaultTest/DeployAssert/BootstrapVersion", - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - }, - "CheckBootstrapVersion": { - "id": "CheckBootstrapVersion", - "path": "integ-tests/DefaultTest/DeployAssert/CheckBootstrapVersion", - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - } - }, - "constructInfo": { - "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", - "version": "0.0.0" - } - }, - "Tree": { - "id": "Tree", - "path": "Tree", - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - } - }, - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.270" - } - } -} \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts deleted file mode 100644 index 52fff546e5ba5..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synthesizer.ts +++ /dev/null @@ -1,39 +0,0 @@ -import * as path from 'path'; -import * as integ from '@aws-cdk/integ-tests-alpha'; -import { App, Stack } from 'aws-cdk-lib'; -import * as lambda from 'aws-cdk-lib/aws-lambda'; -import { AppStagingSynthesizer } from '../lib'; - -const app = new App(); - -const stack = new Stack(app, 'synth-test', { - synthesizer: AppStagingSynthesizer.defaultResources({ - appId: 'szynthassets', - }), - env: { - account: '489318732371', - region: 'us-east-1', - }, -}); - -new lambda.Function(stack, 'lambda-s3', { - code: lambda.AssetCode.fromAsset(path.join(__dirname, 'assets')), - handler: 'index.handler', - runtime: lambda.Runtime.PYTHON_3_10, -}); - -for (let i = 0; i< 10; i++) { - new lambda.Function(stack, 'lambda-ecr'+i, { - code: lambda.EcrImageCode.fromAssetImage(path.join(__dirname, 'assets'), { - assetName: 'ecr-assets'+i, - }), - handler: lambda.Handler.FROM_IMAGE, - runtime: lambda.Runtime.FROM_IMAGE, - }); -} - -new integ.IntegTest(app, 'integ-tests', { - testCases: [stack], -}); - -app.synth(); diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/per-env-staging-factory.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/per-env-staging-factory.test.ts index cddb6298c540e..a5001aaa9f2f4 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/per-env-staging-factory.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/per-env-staging-factory.test.ts @@ -1,6 +1,6 @@ import { App, Stack } from 'aws-cdk-lib'; -import { APP_ID, CLOUDFORMATION_EXECUTION_ROLE, LOOKUP_ROLE, DEPLOY_ACTION_ROLE } from './util'; -import { AppStagingSynthesizer, BootstrapRole } from '../lib'; +import { APP_ID } from './util'; +import { AppStagingSynthesizer } from '../lib'; describe('per environment cache', () => { test('same app, same env', () => { @@ -8,11 +8,6 @@ describe('per environment cache', () => { const app = new App({ defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: APP_ID, - deploymentRoles: { - cloudFormationExecutionRole: BootstrapRole.fromRoleArn(CLOUDFORMATION_EXECUTION_ROLE), - lookupRole: BootstrapRole.fromRoleArn(LOOKUP_ROLE), - deploymentRole: BootstrapRole.fromRoleArn(DEPLOY_ACTION_ROLE), - }, }), }); new Stack(app, 'Stack1', { @@ -41,11 +36,6 @@ describe('per environment cache', () => { const app = new App({ defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: APP_ID, - deploymentRoles: { - cloudFormationExecutionRole: BootstrapRole.fromRoleArn(CLOUDFORMATION_EXECUTION_ROLE), - lookupRole: BootstrapRole.fromRoleArn(LOOKUP_ROLE), - deploymentRole: BootstrapRole.fromRoleArn(DEPLOY_ACTION_ROLE), - }, }), }); new Stack(app, 'Stack1', { @@ -74,11 +64,6 @@ describe('per environment cache', () => { const app = new App({ defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: APP_ID, - deploymentRoles: { - cloudFormationExecutionRole: BootstrapRole.fromRoleArn(CLOUDFORMATION_EXECUTION_ROLE), - lookupRole: BootstrapRole.fromRoleArn(LOOKUP_ROLE), - deploymentRole: BootstrapRole.fromRoleArn(DEPLOY_ACTION_ROLE), - }, }), }); new Stack(app, 'Stack1', { diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/util.ts b/packages/@aws-cdk/app-staging-synthesizer/test/util.ts index b84b3382e3c01..b90fbbe98b7e7 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/util.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/util.ts @@ -1,5 +1,4 @@ import * as cxapi from 'aws-cdk-lib/cx-api'; -import { DefaultResourcesOptions, AppStagingSynthesizer, BootstrapRole } from '../lib'; export const CFN_CONTEXT = { 'AWS::Region': 'the_region', @@ -7,9 +6,6 @@ export const CFN_CONTEXT = { 'AWS::URLSuffix': 'domain.aws', }; export const APP_ID = 'appid'; -export const CLOUDFORMATION_EXECUTION_ROLE = 'role'; -export const DEPLOY_ACTION_ROLE = 'role'; -export const LOOKUP_ROLE = 'role'; export function isAssetManifest(x: cxapi.CloudArtifact): x is cxapi.AssetManifestArtifact { return x instanceof cxapi.AssetManifestArtifact; @@ -18,17 +14,3 @@ export function isAssetManifest(x: cxapi.CloudArtifact): x is cxapi.AssetManifes export function last(xs?: A[]): A | undefined { return xs ? xs[xs.length - 1] : undefined; } - -export class TestAppScopedStagingSynthesizer { - public static stackPerEnv(props: Partial = {}): AppStagingSynthesizer { - return AppStagingSynthesizer.defaultResources({ - appId: props.appId ?? APP_ID, - deploymentRoles: { - cloudFormationExecutionRole: BootstrapRole.fromRoleArn(CLOUDFORMATION_EXECUTION_ROLE), - deploymentRole: BootstrapRole.fromRoleArn(DEPLOY_ACTION_ROLE), - lookupRole: BootstrapRole.fromRoleArn(LOOKUP_ROLE), - }, - ...props, - }); - } -} \ No newline at end of file From 39d95e70a6a4e2432c97d664b714b6e18804744c Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 10 May 2023 19:14:07 -0400 Subject: [PATCH 101/120] readme updates, wip --- .../app-staging-synthesizer/README.md | 132 ++++++++++-------- .../lib/app-staging-synthesizer.ts | 4 +- .../rosetta/default.ts-fixture | 11 ++ 3 files changed, 89 insertions(+), 58 deletions(-) create mode 100644 packages/@aws-cdk/app-staging-synthesizer/rosetta/default.ts-fixture diff --git a/packages/@aws-cdk/app-staging-synthesizer/README.md b/packages/@aws-cdk/app-staging-synthesizer/README.md index 98542ccbefc5b..0f7c33b5c0939 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/README.md +++ b/packages/@aws-cdk/app-staging-synthesizer/README.md @@ -45,8 +45,6 @@ The Staging Stack will contain, on a per-need basis, - An ECR Repository _per_ image (and it's revisions). - IAM roles with access to the Bucket and Repositories. -> The Staging Stack is being actively worked on right now and currently does not support image assets. - ```mermaid graph TD A(Bootstrap Stack) --- B(CloudFormationExecutionRole
DeploymentActionRole
LookupRole) @@ -81,41 +79,45 @@ This will ensure that a Staging Stack will be created next to the CDK App to hol ### Using the Default Staging Stack per Environment -The most common use case will be to use the built-in `DefaultStagingStack` on a per-environment basis. -That means that in each environment the CDK App is deployed to, the synthesizer will create a -Staging Stack to store its resources. To use this kind of synthesizer, use -`AppStagingSynthesizer.stackPerEnv()`. +The most common use case will be to use the built-in default resources. In this scenario, the +synthesizer will create a new Staging Stack in each environment the CDK App is deployed to store +its staging resources. To use this kind of synthesizer, use `AppStagingSynthesizer.defaultResources()`. ```ts -import { App } from 'aws-cdk-lib'; -import { AppStagingSynthesizer } from '@aws-cdk/app-staging-synthesizer'; - const app = new App({ - defaultSynthesizer: AppStagingSynthesizer.stackPerEnv({ + defaultSynthesizer: AppStagingSynthesizer.defaultResources({ appId: 'my-app-id', }), }); ``` +Every CDK App that uses the `DefaultStagingStack` must include an `appId`. This should +be an identifier unique to the app and is used to differentiate staging resources associated +with the app. + ### Using a Custom Staging Stack per Environment -To use a custom stack, but still on a per-environment basis, use `AppStagingSynthesizer.customFactory()`. +Use `AppStagingSynthesizer.customFactory()` to supply a custom staging stack on a per-environment basis. This has the benefit of providing a custom Staging Stack that can be created in every environment the CDK App is deployed to. ```ts -import { App } from 'aws-cdk-lib'; -import { AppStagingSynthesizer } from '@aws-cdk/app-staging-synthesizer'; +import { IStagingStack } from '@aws-cdk/app-staging-synthesizer'; + +class CustomStagingStack implements IStagingStack { + // ... +} const app = new App({ defaultSynthesizer: AppStagingSynthesizer.customFactory({ - stagingStackFactory: { - stagingStackFactory(boundStack: Stack) { - const app = App.of(boundStack); + factory: { + obtainStagingResources(stack, context) { + const app = App.of(stack); if (!App.isApp(app)) { - throw new Error(`Stack ${boundStack.stackName} must be part of an App`); + throw new Error(`Stack ${stack.stackName} must be part of an App`); } - return new CustomStagingStack(app, 'StagingStack', { appId: 'my-app-id' }), + + return new CustomStagingStack(app, `CustomStagingStack-${appId}-${context.environmentString}`, { appId: 'my-app-id' }), }, }, oncePerEnv: true, // by default @@ -125,40 +127,39 @@ const app = new App({ ### Using an Existing Staging Stack -If you need to pass in an existing stack as the Staging Stack to the CDK App, use -`AppStagingSynthesizer.customStack()`. Make sure that the custom stack you provide implements -`IStagingStack`. +Use `AppStagingSynthesizer.customResources()` to supply an existing stack as the Staging Stack. +Make sure that the custom stack you provide implements `IStagingStack`. ```ts -import { App, Stack } from 'aws-cdk-lib'; -import { AppStagingSynthesizer, IStagingStack } from '@aws-cdk/app-staging-synthesizer'; +import { IStagingStack } from '@aws-cdk/app-staging-synthesizer'; class CustomStagingStack implements IStagingStack { // ... } +const resourceApp = new App(); +const resources = new CustomStagingStack(resourceApp, 'CustomStagingStack'); + const app = new App({ - defaultSynthesizer: AppStagingSynthesizer.customStack(new CustomStagingStack(this, 'StagingStack')), + defaultSynthesizer: AppStagingSynthesizer.customResources({ + resources, + }), }); ``` ## Default Staging Stack -> The Default Staging Stack is being actively worked on right now and currently does not support image assets. - -The default Staging Stack includes all the staging resources necessary for CDK Assets. The below example +The Default Staging Stack includes all the staging resources necessary for CDK Assets. The below example is of a CDK App using the `AppStagingSynthesizer` and creating a file asset for the Lambda Function -source code. As part of the `DefaultStagingStack`, an s3 bucket and iam role will be created that will be -used to upload the asset to s3. +source code. As part of the `DefaultStagingStack`, an S3 bucket and IAM role will be created that will be +used to upload the asset to S3. ```ts import * as path from 'path'; import * as lambda from 'aws-cdk-lib/aws-lambda'; -import { App, Stack } from 'aws-cdk-lib'; -import { AppStagingSynthesizer } from 'aws-cdk-lib/app-staging-synthesizer'; const app = new App({ - defaultSynthesizer: new AppStagingSynthesizer.stackPerEnv({appId: 'my-app-id'}), + defaultSynthesizer: new AppStagingSynthesizer.defaultResources({ appId: 'my-app-id' }), }); const stack = new Stack(app, 'my-stack'); @@ -172,38 +173,56 @@ new lambda.Function(stack, 'lambda', { app.synth(); ``` -Every CDK App that uses the `DefaultStagingStack` must include an `appId`. This should -be an identifier unique to the app and is used to differentiate staging resources associated -with the app. - ### Custom Roles You can customize some or all of the roles you'd like to use in the synthesizer as well, if all you need is to supply custom roles (and not change anything else in the `DefaultStagingStack`): ```ts -import { App } from 'aws-cdk-lib'; -import { AppStagingSynthesizer, BootstrapRole } from 'aws-cdk-lib/app-staging-synthesizer'; +const app = new App({ + defaultSynthesizer: new AppStagingSynthesizer.defaultResources({ + appId: 'my-app-id', + deploymentRoles: { + cloudFormationExecutionRole: BoostrapRole.fromRoleArn('arn:aws:iam::123456789012:role/Execute'), + deploymentActionRole: BootstrapRole.fromRoleArn('arn:aws:iam::123456789012:role/Deploy'), + lookupRole: BoostrapRole.fromRoleArn('arn:aws:iam::123456789012:role/Lookup'), + }, + }), +}); +``` +Or, you can ask to use the CLI credentials that exist at deploy-time: + +```ts const app = new App({ - defaultSynthesizer: new AppStagingSynthesizer.stackFromEnv({ + defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: 'my-app-id', - roles: { - cloudFormationExecutionRole: BoostrapRole.fromRoleArn('arn'), - deploymentActionRole: BootstrapRole.fromRoleArn('arn'), - lookupRole: BoostrapRole.fromRoleArn('arn'), - fileAssetPublishingRole: BootstrapRole.fromRoleArn('arn'), - imageAssetPublishingRole: BootstrapRole.fromRoleArn('arn'), + deploymentRoles: { + cloudFormationExecutionRole: BootstrapRole.cliCredentials(), + lookupRole: BootstrapRole.cliCredentials(), + deploymentRole: BootstrapRole.cliCredentials(), }, }), }); ``` -### Ephemeral Assets +You can also specify an existing IAM role for the `fileAssetPublishingRole` or `imageAssetPublishingRole`: + +```ts +const app = new App({ + defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ + appId: 'my-app-id', + fileAssetPublishingRole: BootstrapRole.fromRoleArn('arn:aws:iam::123456789012:role/S3Access'), + imageAssetPublishingRole: BootstrapRole.fromRoleArn('arn:aws:iam::123456789012:role/ECRAccess'), + }), +}); +``` + +### Ephemeral S3 Assets Some assets that get put into the staging S3 Bucket are ephemeral - they are only necessary during CloudFormation deployment and not after. As long as you know what assets are ephemeral, -you can tag them as such and they will be marked with an `eph-` prefix when they are staged. +you can tag them as such and they will be marked with an `handoff/` prefix when they are staged. This allows configuration of a lifecycle rule specifically for ephemeral assets. A good example is a Lambda Function asset. The asset is only useful in the S3 Bucket at deploy @@ -220,18 +239,19 @@ new lambda.Function(stack, 'lambda', { }); ``` -This means that the asset will go into the S3 Bucket with the prefix `eph-`. It will also be -subject to the lifecycle rule set on ephemeral assets. By default, the rule is `expiration: Duration.days(10)`. -You can specify your own rule like this: +This means that the asset will go into the S3 Bucket with the prefix `handoff/`. It will also be +subject to the lifecycle rule set on ephemeral assets. By default, we store ephemeral assets for +30 days. ```ts const app = new App({ - defaultStackSynthesizer: TestAppScopedStagingSynthesizer.stackPerEnv({ - ephemeralFileAssetLifecycleRule: { - prefix: 'eph-', // required - objectSizeGreaterThan: 10000, - expiration: Duration.days(1), - }, + defaultStackSynthesizer: TestAppScopedStagingSynthesizer.defaultResources({ + appId: 'my-app-id', + handoffFileAssetLifetime: Duration.days(100), }), }); ``` + +### Lifecycle Rules on ECR Repositories + +### Extending the Default Staging Stack diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts index 1e15aee6c1c35..42086372923f9 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts @@ -15,7 +15,7 @@ import { import { StringSpecializer, translateCfnTokenToAssetToken } from 'aws-cdk-lib/core/lib/helpers-internal'; import { BootstrapRole, BootstrapRoles } from './bootstrap-roles'; import { DefaultStagingStack, DefaultStagingStackOptions } from './default-staging-stack'; -import { PerEnvironmenStagingFactory } from './per-env-staging-factory'; +import { PerEnvironmenStagingFactory as PerEnvironmentStagingFactory } from './per-env-staging-factory'; import { AppScopedGlobal } from './private/app-global'; import { validateNoTokens } from './private/no-tokens'; import { IStagingStack, IStagingStackFactory, ObtainStagingResourcesContext } from './staging-stack'; @@ -157,7 +157,7 @@ export class AppStagingSynthesizer extends StackSynthesizer implements IReusable */ public static customFactory(options: CustomFactoryOptions) { const oncePerEnv = options.oncePerEnv ?? true; - const factory = oncePerEnv ? new PerEnvironmenStagingFactory(options.factory) : options.factory; + const factory = oncePerEnv ? new PerEnvironmentStagingFactory(options.factory) : options.factory; return new AppStagingSynthesizer({ factory, diff --git a/packages/@aws-cdk/app-staging-synthesizer/rosetta/default.ts-fixture b/packages/@aws-cdk/app-staging-synthesizer/rosetta/default.ts-fixture new file mode 100644 index 0000000000000..e86db62607ab6 --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/rosetta/default.ts-fixture @@ -0,0 +1,11 @@ +// Fixture with packages imported, but nothing else +import { App, Stack } from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import { AppStagingSynthesizer, BootstrapRole } from '@aws-cdk/app-staging-synthesizer'; + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + /// here + } +} From 505227fa0f5e28f52567509854aec407a459ffb8 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 11 May 2023 10:37:49 -0400 Subject: [PATCH 102/120] rename IStagingStack to IStagingResources --- .../app-staging-synthesizer/README.md | 24 +++++++++++-------- .../lib/app-staging-synthesizer.ts | 14 +++++------ .../lib/default-staging-stack.ts | 10 ++++---- .../lib/per-env-staging-factory.ts | 12 +++++----- .../lib/staging-stack.ts | 14 +++++------ 5 files changed, 39 insertions(+), 35 deletions(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer/README.md b/packages/@aws-cdk/app-staging-synthesizer/README.md index 0f7c33b5c0939..a57d2ba38940c 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/README.md +++ b/packages/@aws-cdk/app-staging-synthesizer/README.md @@ -17,10 +17,14 @@ This library includes constructs aimed at replacing the current model of bootstrapping and providing greater control of the bootstrap experience to the CDK user. The important constructs in this library -are the `IStagingStack`, a framework for an app-level bootstrap stack that handles file assets and -docker assets and the `DefaultStagingStack`, which is a works-out-of-the-box implementation of the -interface. Additionally, there is an `AppStagingSynthesizer` that will synthesize CDK applications -built with this new model of bootstrapping. +are as follows: + +- the `IStagingResources` interface: a framework for an app-level bootstrap stack that handles + file assets and docker assets. +- the `DefaultStagingStack`, which is a works-out-of-the-box implementation of the `IStagingResources` + interface. +- the `AppStagingSynthesizer`, a new CDK synthesizer that will synthesize CDK applications with + the staging resources provided. ## Bootstrap Model @@ -42,7 +46,7 @@ S3 Bucket, ECR Repositories, and associated IAM roles. It works like this: The Staging Stack will contain, on a per-need basis, - 1 S3 Bucket with KMS encryption for all file assets in the CDK App. -- An ECR Repository _per_ image (and it's revisions). +- An ECR Repository _per_ image (and its revisions). - IAM roles with access to the Bucket and Repositories. ```mermaid @@ -102,9 +106,9 @@ This has the benefit of providing a custom Staging Stack that can be created in is deployed to. ```ts -import { IStagingStack } from '@aws-cdk/app-staging-synthesizer'; +import { IStagingResources } from '@aws-cdk/app-staging-synthesizer'; -class CustomStagingStack implements IStagingStack { +class CustomStagingStack extends Stack implements IStagingResources { // ... } @@ -128,12 +132,12 @@ const app = new App({ ### Using an Existing Staging Stack Use `AppStagingSynthesizer.customResources()` to supply an existing stack as the Staging Stack. -Make sure that the custom stack you provide implements `IStagingStack`. +Make sure that the custom stack you provide implements `IStagingResources`. ```ts -import { IStagingStack } from '@aws-cdk/app-staging-synthesizer'; +import { IStagingResources } from '@aws-cdk/app-staging-synthesizer'; -class CustomStagingStack implements IStagingStack { +class CustomStagingStack extends Stack implements IStagingResources { // ... } diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts index 42086372923f9..b145e2d6ebeeb 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts @@ -15,10 +15,10 @@ import { import { StringSpecializer, translateCfnTokenToAssetToken } from 'aws-cdk-lib/core/lib/helpers-internal'; import { BootstrapRole, BootstrapRoles } from './bootstrap-roles'; import { DefaultStagingStack, DefaultStagingStackOptions } from './default-staging-stack'; -import { PerEnvironmenStagingFactory as PerEnvironmentStagingFactory } from './per-env-staging-factory'; +import { PerEnvironmentStagingFactory as PerEnvironmentStagingFactory } from './per-env-staging-factory'; import { AppScopedGlobal } from './private/app-global'; import { validateNoTokens } from './private/no-tokens'; -import { IStagingStack, IStagingStackFactory, ObtainStagingResourcesContext } from './staging-stack'; +import { IStagingResources, IStagingResourcesFactory, ObtainStagingResourcesContext } from './staging-stack'; const AGNOSTIC_STACKS = new AppScopedGlobal(() => new Set()); const ENV_AWARE_STACKS = new AppScopedGlobal(() => new Set()); @@ -63,7 +63,7 @@ export interface CustomFactoryOptions extends AppStagingSynthesizerOptions { /** * The factory that will be used to return staging resources for each stack */ - readonly factory: IStagingStackFactory; + readonly factory: IStagingResourcesFactory; /** * Reuse the answer from the factory for stacks in the same environment @@ -80,7 +80,7 @@ export interface CustomResourcesOptions extends AppStagingSynthesizerOptions { /** * Use these exact staging resources for every stack that this synthesizer is used for */ - readonly resources: IStagingStack; + readonly resources: IStagingResources; } /** @@ -91,7 +91,7 @@ interface AppStagingSynthesizerProps extends AppStagingSynthesizerOptions { * A factory method that creates an IStagingStack when given the stack the * synthesizer is binding. */ - readonly factory: IStagingStackFactory; + readonly factory: IStagingResourcesFactory; } /** @@ -276,7 +276,7 @@ interface BoundAppStagingSynthesizerProps { /** * The resources we end up using for this synthesizer */ - readonly stagingResources: IStagingStack; + readonly stagingResources: IStagingResources; /** * The deploy role @@ -295,7 +295,7 @@ interface BoundAppStagingSynthesizerProps { } class BoundAppStagingSynthesizer extends StackSynthesizer implements IBoundAppStagingSynthesizer { - private readonly stagingStack: IStagingStack; + private readonly stagingStack: IStagingResources; private readonly assetManifest = new AssetManifestBuilder(); private readonly qualifier: string; diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts index 78b7dfb20e744..869c9ca197578 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts @@ -18,12 +18,12 @@ import * as kms from 'aws-cdk-lib/aws-kms'; import * as s3 from 'aws-cdk-lib/aws-s3'; import { StringSpecializer } from 'aws-cdk-lib/core/lib/helpers-internal'; import { BootstrapRole } from './bootstrap-roles'; -import { FileStagingLocation, IStagingStack, IStagingStackFactory, ImageStagingLocation } from './staging-stack'; +import { FileStagingLocation, IStagingResources, IStagingResourcesFactory, ImageStagingLocation } from './staging-stack'; const EPHEMERAL_PREFIX = 'handoff/'; /** - * User configurable options to the DefaultStagingStack + * User configurable options to the DefaultStagingStack. */ export interface DefaultStagingStackOptions { /** @@ -110,13 +110,13 @@ export interface DefaultStagingStackProps extends DefaultStagingStackOptions, St } /** - * A default Staging Stack + * A default Staging Stack that implements IStagingResources. */ -export class DefaultStagingStack extends Stack implements IStagingStack { +export class DefaultStagingStack extends Stack implements IStagingResources { /** * Return a factory that will create DefaultStagingStacks */ - public static factory(options: DefaultStagingStackOptions): IStagingStackFactory { + public static factory(options: DefaultStagingStackOptions): IStagingResourcesFactory { const appId = options.appId.toLocaleLowerCase().replace(/[^a-z0-9-]/g, '-').slice(0, 20); return { obtainStagingResources(stack, context) { diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/per-env-staging-factory.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/per-env-staging-factory.ts index 7ecd5416d97dc..3d3c8fd5c50b2 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/per-env-staging-factory.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/per-env-staging-factory.ts @@ -1,6 +1,6 @@ import { Stack } from 'aws-cdk-lib'; import { AppScopedGlobal } from './private/app-global'; -import { IStagingStack, IStagingStackFactory, ObtainStagingResourcesContext } from './staging-stack'; +import { IStagingResources, IStagingResourcesFactory, ObtainStagingResourcesContext } from './staging-stack'; /** * Per-environment cache @@ -8,15 +8,15 @@ import { IStagingStack, IStagingStackFactory, ObtainStagingResourcesContext } fr * This is a global because we might have multiple instances of this class * in the app, but we want to cache across all of them. */ -const ENVIRONMENT_CACHE = new AppScopedGlobal(() => new Map()); +const ENVIRONMENT_CACHE = new AppScopedGlobal(() => new Map()); /** - * Wraps another IStagingStack factory, and caches the result on a per-environment basis + * Wraps another IStagingResources factory, and caches the result on a per-environment basis. */ -export class PerEnvironmenStagingFactory implements IStagingStackFactory { - constructor(private readonly wrapped: IStagingStackFactory) { } +export class PerEnvironmentStagingFactory implements IStagingResourcesFactory { + constructor(private readonly wrapped: IStagingResourcesFactory) { } - public obtainStagingResources(stack: Stack, context: ObtainStagingResourcesContext): IStagingStack { + public obtainStagingResources(stack: Stack, context: ObtainStagingResourcesContext): IStagingResources { const cacheKey = context.environmentString; const cache = ENVIRONMENT_CACHE.for(stack); diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/staging-stack.ts index 13915169025fe..9cc24dbb3bce5 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/staging-stack.ts @@ -57,9 +57,9 @@ export interface ImageStagingLocation { } /** - * Information on how a Staging Stack should look. + * Staging Resource interface. */ -export interface IStagingStack extends IConstruct { +export interface IStagingResources extends IConstruct { /** * Return staging resource information for a file asset. */ @@ -72,13 +72,13 @@ export interface IStagingStack extends IConstruct { } /** - * Staging Stack Factory interface. + * Staging Resource Factory interface. * * The function included in this class will be called by the synthesizer - * to create or reference an IStagingStack that has the necessary - * staging resources for the Stack. + * to create or reference an IStagingResources construct that has the necessary + * staging resources for the stack. */ -export interface IStagingStackFactory { +export interface IStagingResourcesFactory { /** * Return an object that will manage staging resources for the given stack * @@ -90,7 +90,7 @@ export interface IStagingStackFactory { * * @param stack - stack to return an appropriate IStagingStack for */ - obtainStagingResources(stack: Stack, context: ObtainStagingResourcesContext): IStagingStack; + obtainStagingResources(stack: Stack, context: ObtainStagingResourcesContext): IStagingResources; } /** From 01baa7bc0f4d594e5f3fa6dffc4a62bf045dbf1e Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 11 May 2023 11:46:45 -0400 Subject: [PATCH 103/120] more readme --- .../app-staging-synthesizer/README.md | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer/README.md b/packages/@aws-cdk/app-staging-synthesizer/README.md index a57d2ba38940c..5cda242d83f1a 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/README.md +++ b/packages/@aws-cdk/app-staging-synthesizer/README.md @@ -249,7 +249,7 @@ subject to the lifecycle rule set on ephemeral assets. By default, we store ephe ```ts const app = new App({ - defaultStackSynthesizer: TestAppScopedStagingSynthesizer.defaultResources({ + defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: 'my-app-id', handoffFileAssetLifetime: Duration.days(100), }), @@ -258,4 +258,25 @@ const app = new App({ ### Lifecycle Rules on ECR Repositories -### Extending the Default Staging Stack +By default, we store a maximum of 3 revisions of a particular docker image asset. This allows +for smooth faciliation of rollback scenarios where we may reference previous versions of an +image. When more than 3 revisions of an asset exist in the ECR repository, the oldest one is +purged. + +To change the number of revisions stored, use `imageAssetVersionCount`: + +```ts +const app = new App({ + defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ + appId: 'my-app-id', + imageAssetVersionCount: 10, + }), +}); +``` + +## Extending the Default Staging Stack + +The exposed API of this synthesizer is purposefully opinionated. Should the feature set be +too restrictive, `AppStagingSynthesizer` is built to be subclassable. For example, say we want +to add a feature where we _do not_ apply lifecycle rules to any assets that have the prefix +"`permanent`" \ No newline at end of file From 28e3324a52482944609eb996cf89873c495fc863 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 11 May 2023 11:46:52 -0400 Subject: [PATCH 104/120] wip integ test --- .../app-staging-synthesizer/test/integ.custom-staging-stack.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.custom-staging-stack.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.custom-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/test/integ.custom-staging-stack.ts new file mode 100644 index 0000000000000..e69de29bb2d1d From d38dc36f7e65496e9acd26faea3eaaf07ad9011c Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 11 May 2023 11:47:20 -0400 Subject: [PATCH 105/120] wip integ test --- .../@aws-cdk/app-staging-synthesizer/README.md | 11 ++++++++++- .../test/integ.custom-staging-stack.ts | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer/README.md b/packages/@aws-cdk/app-staging-synthesizer/README.md index 5cda242d83f1a..75848abe62600 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/README.md +++ b/packages/@aws-cdk/app-staging-synthesizer/README.md @@ -279,4 +279,13 @@ const app = new App({ The exposed API of this synthesizer is purposefully opinionated. Should the feature set be too restrictive, `AppStagingSynthesizer` is built to be subclassable. For example, say we want to add a feature where we _do not_ apply lifecycle rules to any assets that have the prefix -"`permanent`" \ No newline at end of file +"`permanent`". While that doesn't exist as a property of `AppStagingSynthesizer`, we can easily +extend it to include what we want: + +```ts +interface CustomStagingStackOptions extends DefaultStagingStackOptions {} + +class CustomStagingStack extends DefaultStagingStack { + +} +``` \ No newline at end of file diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.custom-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/test/integ.custom-staging-stack.ts index e69de29bb2d1d..1eb64d81a9726 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.custom-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/integ.custom-staging-stack.ts @@ -0,0 +1,14 @@ +import { DefaultStagingStack, DefaultStagingStackOptions, IStagingResourcesFactory } from '../lib'; + +interface PermanentAssetsStagingStackOptions extends DefaultStagingStackOptions {} + +class PermanentAssetsStagingStack extends DefaultStagingStack { + public static factory(options: PermanentAssetsStagingStackOptions): IStagingResourcesFactory { + const factory = DefaultStagingStack.factory(options); + return { + obtainStagingResources(stack, context) { + + }, + } + } +} \ No newline at end of file From f9443f2c4b79a114ae2200307ff2e989d50d083b Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Mon, 15 May 2023 10:31:44 +0200 Subject: [PATCH 106/120] Undo some changes that are now on the ot her branch --- .../aws-cdk-lib/cx-api/lib/cloud-assembly.ts | 6 +- .../cx-api/test/cloud-assembly.test.ts | 7 - .../lib/api/cloudformation-deployments.ts | 81 ++-- packages/aws-cdk/lib/cdk-toolkit.ts | 81 ++-- packages/aws-cdk/lib/deploy.ts | 115 +++-- packages/aws-cdk/package.json | 1 + .../api/cloudformation-deployments.test.ts | 53 ++- packages/aws-cdk/test/cdk-toolkit.test.ts | 93 +++-- packages/aws-cdk/test/deploy.test.ts | 394 +++++------------- 9 files changed, 328 insertions(+), 503 deletions(-) diff --git a/packages/aws-cdk-lib/cx-api/lib/cloud-assembly.ts b/packages/aws-cdk-lib/cx-api/lib/cloud-assembly.ts index a0b85075c60a5..61fc3cae92b77 100644 --- a/packages/aws-cdk-lib/cx-api/lib/cloud-assembly.ts +++ b/packages/aws-cdk-lib/cx-api/lib/cloud-assembly.ts @@ -1,13 +1,13 @@ import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; +import * as cxschema from '../../cloud-assembly-schema'; +import { LoadManifestOptions } from '../../cloud-assembly-schema'; import { CloudFormationStackArtifact } from './artifacts/cloudformation-artifact'; import { NestedCloudAssemblyArtifact } from './artifacts/nested-cloud-assembly-artifact'; import { TreeCloudArtifact } from './artifacts/tree-cloud-artifact'; import { CloudArtifact } from './cloud-artifact'; import { topologicalSort } from './toposort'; -import * as cxschema from '../../cloud-assembly-schema'; -import { LoadManifestOptions } from '../../cloud-assembly-schema'; /** * The name of the root manifest file of the assembly. @@ -228,7 +228,7 @@ export class CloudAssembly { } } - return topologicalSort(result, x => x.id, x => x._dependencyIDs); // TODO: remove redundant toposort + return topologicalSort(result, x => x.id, x => x._dependencyIDs); } } diff --git a/packages/aws-cdk-lib/cx-api/test/cloud-assembly.test.ts b/packages/aws-cdk-lib/cx-api/test/cloud-assembly.test.ts index b3965026a98fb..804d1e43e16b6 100644 --- a/packages/aws-cdk-lib/cx-api/test/cloud-assembly.test.ts +++ b/packages/aws-cdk-lib/cx-api/test/cloud-assembly.test.ts @@ -168,13 +168,6 @@ test('can read assembly with asset manifest', () => { expect(assembly.artifacts).toHaveLength(2); }); -test('can toposort assembly with asset dependency', () => { - const assembly = new CloudAssembly(path.join(FIXTURES, 'asset-depends')); - expect(assembly.stacks).toHaveLength(2); - expect(assembly.artifacts).toHaveLength(3); - expect(assembly.artifacts[0].id).toEqual('StagingStack'); -}); - test('getStackArtifact retrieves a stack by artifact id from a nested assembly', () => { const assembly = new CloudAssembly(path.join(FIXTURES, 'nested-assemblies')); diff --git a/packages/aws-cdk/lib/api/cloudformation-deployments.ts b/packages/aws-cdk/lib/api/cloudformation-deployments.ts index 5c7cd60c0ef75..58c98179164da 100644 --- a/packages/aws-cdk/lib/api/cloudformation-deployments.ts +++ b/packages/aws-cdk/lib/api/cloudformation-deployments.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-console */ import * as cxapi from '@aws-cdk/cx-api'; import { AssetManifest } from 'cdk-assets'; import { Mode } from './aws-auth/credentials'; @@ -95,7 +94,6 @@ export async function prepareSdkWithLookupRoleFor( // only print out the warnings if the lookupRole exists AND there is a required // bootstrap version, otherwise the warnings will print `undefined` if (stack.lookupRole && stack.lookupRole.requiresBootstrapStackVersion) { - warning('wtf'); warning(warningMessage); warning(upgradeMessage); } @@ -265,7 +263,7 @@ export interface DeployStackOptions { readonly assetParallelism?: boolean; } -interface AssetOptions { +export interface BuildStackAssetsOptions { /** * Stack with assets to build. */ @@ -284,16 +282,21 @@ interface AssetOptions { * @default - Current role */ readonly roleArn?: string; -} -export interface BuildStackAssetsOptions extends AssetOptions { /** * Options to pass on to `buildAssets()` function */ readonly buildOptions?: BuildAssetsOptions; } -interface PublishStackAssetsOptions extends AssetOptions { +interface PublishStackAssetsOptions { + /** + * Whether to build assets before publishing. + * + * @default true To remain backward compatible. + */ + readonly buildAssets?: boolean; + /** * Options to pass on to `publishAsests()` function */ @@ -412,6 +415,16 @@ export class CloudFormationDeployments { const toolkitInfo = await ToolkitInfo.lookup(resolvedEnvironment, stackSdk, options.toolkitStackName); + // Publish any assets before doing the actual deploy (do not publish any assets on import operation) + if (options.resourcesToImport === undefined) { + await this.publishStackAssets(options.stack, toolkitInfo, { + buildAssets: options.buildAssets ?? true, + publishOptions: { + parallel: options.assetParallelism, + }, + }); + } + // Do a verification of the bootstrap stack version await this.validateBootstrapStackVersion( options.stack.stackName, @@ -520,36 +533,48 @@ export class CloudFormationDeployments { }; } - private async prepareAndValidateAssets(asset: cxapi.AssetManifestArtifact, options: AssetOptions) { - console.log('prepareandvalidateassets'); + /** + * Build a stack's assets. + */ + public async buildStackAssets(options: BuildStackAssetsOptions) { const { stackSdk, resolvedEnvironment } = await this.prepareSdkFor(options.stack, options.roleArn); - console.log('stacksdk'); const toolkitInfo = await ToolkitInfo.lookup(resolvedEnvironment, stackSdk, options.toolkitStackName); - console.log(toolkitInfo); + const stackEnv = await this.sdkProvider.resolveEnvironment(options.stack.environment); - console.log(stackEnv); - await this.validateBootstrapStackVersion( - options.stack.stackName, - asset.requiresBootstrapStackVersion, - asset.bootstrapStackVersionSsmParameter, - toolkitInfo); + const assetArtifacts = options.stack.dependencies.filter(cxapi.AssetManifestArtifact.isAssetManifestArtifact); - const manifest = AssetManifest.fromFile(asset.file); + for (const assetArtifact of assetArtifacts) { + await this.validateBootstrapStackVersion( + options.stack.stackName, + assetArtifact.requiresBootstrapStackVersion, + assetArtifact.bootstrapStackVersionSsmParameter, + toolkitInfo); - return { manifest, stackEnv }; + const manifest = AssetManifest.fromFile(assetArtifact.file); + await buildAssets(manifest, this.sdkProvider, stackEnv, options.buildOptions); + } } - public async buildAssets(asset: cxapi.AssetManifestArtifact, options: BuildStackAssetsOptions) { - console.log('build assets'); - console.log('build parallelism', options.buildOptions?.parallel); - const { manifest, stackEnv } = await this.prepareAndValidateAssets(asset, options); - await buildAssets(manifest, this.sdkProvider, stackEnv, options.buildOptions); - } + /** + * Publish all asset manifests that are referenced by the given stack + */ + private async publishStackAssets(stack: cxapi.CloudFormationStackArtifact, toolkitInfo: ToolkitInfo, options: PublishStackAssetsOptions = {}) { + const stackEnv = await this.sdkProvider.resolveEnvironment(stack.environment); + const assetArtifacts = stack.dependencies.filter(cxapi.AssetManifestArtifact.isAssetManifestArtifact); - public async publishAssets(asset: cxapi.AssetManifestArtifact, options: PublishStackAssetsOptions) { - console.log('publish parallelism', options.publishOptions?.parallel); - const { manifest, stackEnv } = await this.prepareAndValidateAssets(asset, options); - await publishAssets(manifest, this.sdkProvider, stackEnv, options.publishOptions); + for (const assetArtifact of assetArtifacts) { + await this.validateBootstrapStackVersion( + stack.stackName, + assetArtifact.requiresBootstrapStackVersion, + assetArtifact.bootstrapStackVersionSsmParameter, + toolkitInfo); + + const manifest = AssetManifest.fromFile(assetArtifact.file); + await publishAssets(manifest, this.sdkProvider, stackEnv, { + ...options.publishOptions, + buildAssets: options.buildAssets ?? true, + }); + } } /** diff --git a/packages/aws-cdk/lib/cdk-toolkit.ts b/packages/aws-cdk/lib/cdk-toolkit.ts index 2005e2273d9c8..7f2024b71736d 100644 --- a/packages/aws-cdk/lib/cdk-toolkit.ts +++ b/packages/aws-cdk/lib/cdk-toolkit.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-console */ import * as path from 'path'; import { format } from 'util'; import * as cxapi from '@aws-cdk/cx-api'; @@ -16,7 +15,8 @@ import { HotswapMode } from './api/hotswap/common'; import { findCloudWatchLogGroups } from './api/logs/find-cloudwatch-logs'; import { CloudWatchLogEventMonitor } from './api/logs/logs-monitor'; import { StackActivityProgress } from './api/util/cloudformation/stack-activity-monitor'; -import { deployArtifacts } from './deploy'; +import { buildAllStackAssets } from './build'; +import { deployStacks } from './deploy'; import { printSecurityDiff, printStackDiff, RequireApproval } from './diff'; import { ResourceImporter } from './import'; import { data, debug, error, highlight, print, success, warning } from './logging'; @@ -24,7 +24,6 @@ import { deserializeStructure, serializeStructure } from './serialize'; import { Configuration, PROJECT_CONFIG } from './settings'; import { numberFromBool, partition } from './util'; import { validateSnsTopicArn } from './util/validate-notification-arn'; -import { AssetBuildNode, AssetPublishNode, StackNode } from './util/work-graph-types'; import { environmentsFromDescriptors, globEnvironmentsFromStacks, looksLikeGlob } from '../lib/api/cxapp/environments'; export interface CdkToolkitProps { @@ -187,44 +186,24 @@ export class CdkToolkit { } const stacks = stackCollection.stackArtifacts; - const cloudArtifacts = stackCollection.assembly.assembly.artifacts; - const assetBuildTime = options.assetBuildTime ?? AssetBuildTime.ALL_BEFORE_DEPLOY; // TODO: deal with this + const assetBuildTime = options.assetBuildTime ?? AssetBuildTime.ALL_BEFORE_DEPLOY; const stackOutputs: { [key: string]: any } = { }; const outputsFile = options.outputsFile; - const buildAsset = async (assetNode: AssetBuildNode) => { - print('%s: building assets...\n', chalk.bold(assetNode.parentStack.displayName)); - console.log('%s: building assets...\n', chalk.bold(assetNode.parentStack.displayName)); - await this.props.cloudFormation.buildAssets(assetNode.asset, { - stack: assetNode.parentStack, - roleArn: options.roleArn, - toolkitStackName: options.toolkitStackName, - buildOptions: { - parallel: options.assetParallelism, - }, - }); - print('\n%s: assets built\n', chalk.bold(assetNode.parentStack.displayName)); - console.log('\n%s: assets built\n', chalk.bold(assetNode.parentStack.displayName)); - }; - - const publishAsset = async (assetNode: AssetPublishNode) => { - print('%s: publishing assets...\n', chalk.bold(assetNode.parentStack.displayName)); - console.log('%s: publishing assets...\n', chalk.bold(assetNode.parentStack.displayName)); - await this.props.cloudFormation.publishAssets(assetNode.asset, { - stack: assetNode.parentStack, - roleArn: options.roleArn, - toolkitStackName: options.toolkitStackName, - publishOptions: { - parallel: options.assetParallelism, - }, - }); - print('\n%s: assets published\n', chalk.bold(assetNode.parentStack.displayName)); - console.log('\n%s: assets published\n', chalk.bold(assetNode.parentStack.displayName)); - }; + if (assetBuildTime === AssetBuildTime.ALL_BEFORE_DEPLOY) { + // Prebuild all assets + try { + await buildAllStackAssets(stackCollection.stackArtifacts, { + buildStackAssets: (a) => this.buildAllAssetsForSingleStack(a, options), + }); + } catch (e) { + error('\n ❌ Building assets failed: %s', e); + throw e; + } + } - const deployStack = async (assetNode: StackNode) => { - const stack = assetNode.stack; + const deployStack = async (stack: cxapi.CloudFormationStackArtifact) => { if (stackCollection.stackCount !== 1) { highlight(stack.displayName); } if (!stack.environment) { @@ -302,7 +281,7 @@ export class CdkToolkit { rollback: options.rollback, hotswap: options.hotswap, extraUserAgent: options.extraUserAgent, - buildAssets: assetBuildTime !== AssetBuildTime.ALL_BEFORE_DEPLOY, // TODO: remove this + buildAssets: assetBuildTime !== AssetBuildTime.ALL_BEFORE_DEPLOY, assetParallelism: options.assetParallelism, }); @@ -357,15 +336,7 @@ export class CdkToolkit { } try { - await deployArtifacts(cloudArtifacts, { - concurrency, - callbacks: { - deployStack, - buildAsset, - publishAsset, - }, - prebuildAssets: assetBuildTime !== AssetBuildTime.ALL_BEFORE_DEPLOY, - }); + await deployStacks(stacks, { concurrency, deployStack }); } catch (e) { error('\n ❌ Deployment failed: %s', e); throw e; @@ -811,6 +782,24 @@ export class CdkToolkit { // just continue - deploy will show the error } } + + private async buildAllAssetsForSingleStack(stack: cxapi.CloudFormationStackArtifact, options: Pick): Promise { + // Check whether the stack has an asset manifest before trying to build and publish. + if (!stack.dependencies.some(cxapi.AssetManifestArtifact.isAssetManifestArtifact)) { + return; + } + + print('%s: building assets...\n', chalk.bold(stack.displayName)); + await this.props.cloudFormation.buildStackAssets({ + stack, + roleArn: options.roleArn, + toolkitStackName: options.toolkitStackName, + buildOptions: { + parallel: options.assetParallelism, + }, + }); + print('\n%s: assets built\n', chalk.bold(stack.displayName)); + } } export interface DiffOptions { diff --git a/packages/aws-cdk/lib/deploy.ts b/packages/aws-cdk/lib/deploy.ts index 0609487126a28..76bd1b579664f 100644 --- a/packages/aws-cdk/lib/deploy.ts +++ b/packages/aws-cdk/lib/deploy.ts @@ -1,84 +1,69 @@ -/* eslint-disable no-console */ import * as cxapi from '@aws-cdk/cx-api'; -import { WorkGraph } from './util/work-graph'; -import { AssetBuildNode, AssetPublishNode, StackNode, WorkNode, WorkType } from './util/work-graph-types'; - -interface CallbackActions { - deployStack: (stackNode: StackNode) => Promise; - buildAsset: (assetNode: AssetBuildNode) => Promise; - publishAsset: (assetNode: AssetPublishNode) => Promise; -} +import PQueue from 'p-queue'; type Options = { concurrency: number; - callbacks: CallbackActions; - prebuildAssets?: boolean; + deployStack: (stack: cxapi.CloudFormationStackArtifact) => Promise; }; -export const deployArtifacts = async (artifacts: cxapi.CloudArtifact[], { - concurrency, - callbacks, - prebuildAssets = true, -}: Options): Promise => { - const graph = WorkGraph.fromCloudArtifacts(artifacts, prebuildAssets); - console.log(graph.toString()); +type DeploymentState = 'pending' | 'queued' | 'deploying' | 'completed' | 'failed' | 'skipped'; - await forAllArtifacts(concurrency, async (x: WorkNode) => { - console.log('fn called'); - // Execute this function with as much parallelism as possible - switch (x.type) { - case WorkType.STACK_DEPLOY: - await callbacks.deployStack(x as StackNode); - break; - case WorkType.ASSET_BUILD: - await callbacks.buildAsset(x as AssetBuildNode); - break; - case WorkType.ASSET_PUBLISH: - await callbacks.publishAsset(x as AssetPublishNode); - break; - } - }); +export const deployStacks = async (stacks: cxapi.CloudFormationStackArtifact[], { concurrency, deployStack }: Options): Promise => { + const queue = new PQueue({ concurrency }); + const deploymentStates = stacks.reduce((acc, stack) => ({ ...acc, [stack.id]: 'pending' as const }), {} as Record); - function forAllArtifacts(n: number, fn: (x: WorkNode) => Promise): Promise { - console.log('forallartiacts'); - return new Promise((ok, fail) => { - let active = 0; + const isStackUnblocked = (stack: cxapi.CloudFormationStackArtifact) => + stack.dependencies + .map(({ id }) => id) + .filter((id) => !id.endsWith('.assets')) + .every((id) => !deploymentStates[id] || deploymentStates[id] === 'completed'); // Dependency not selected or already finished - start(); + const hasAnyStackFailed = (states: Record) => Object.values(states).includes('failed'); - function start() { - console.log('start'); - while (graph.hasNext() && active < n) { - console.log('startingone'); - startOne(graph.next()!); - } + const deploymentErrors: Error[] = []; - if (graph.done() && active === 0) { - ok(); - } + const enqueueStackDeploys = () => { + stacks.forEach(async (stack) => { + if (deploymentStates[stack.id] === 'pending' && isStackUnblocked(stack)) { + deploymentStates[stack.id] = 'queued'; - // wait for other active deploys to finish before failing - if (graph.hasFailed() && active === 0) { - fail(graph.error); - } - } + await queue.add(async () => { + // Do not start new deployments if any has already failed + if (hasAnyStackFailed(deploymentStates)) { + deploymentStates[stack.id] = 'skipped'; + return; + } + + deploymentStates[stack.id] = 'deploying'; - function startOne(x: WorkNode) { - console.log('startOne'); - active++; - void fn(x).then(() => { - console.log('fn finised'); - active--; - graph.deployed(x); - start(); + await deployStack(stack).catch((err) => { + // By recording the failure immediately as the queued task exits, we prevent the next + // queued task from starting (its 'hasAnyStackFailed' will return 'true'). + deploymentStates[stack.id] = 'failed'; + throw err; + }); + + deploymentStates[stack.id] = 'completed'; + enqueueStackDeploys(); }).catch((err) => { - active--; - // By recording the failure immediately as the queued task exits, we prevent the next - // queued task from starting. - graph.failed(x, err); - start(); + deploymentStates[stack.id] = 'failed'; + deploymentErrors.push(err); }); } }); + }; + + enqueueStackDeploys(); + + await queue.onIdle(); + + if (deploymentErrors.length) { + throw Error(`Stack Deployments Failed: ${deploymentErrors}`); + } + + // We shouldn't be able to get here, but check it anyway + const neverUnblocked = Object.entries(deploymentStates).filter(([_, s]) => s === 'pending').map(([n, _]) => n); + if (neverUnblocked.length > 0) { + throw new Error(`The following stacks never became unblocked: ${neverUnblocked.join(', ')}. Please report this at https://github.com/aws/aws-cdk/issues`); } }; diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index 413d05f0ae089..71f39f9a4cb01 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -109,6 +109,7 @@ "glob": "^7.2.3", "json-diff": "^0.10.0", "minimatch": "^9.0.0", + "p-queue": "^6.6.2", "promptly": "^3.2.0", "proxy-agent": "^5.0.0", "semver": "^7.3.8", diff --git a/packages/aws-cdk/test/api/cloudformation-deployments.test.ts b/packages/aws-cdk/test/api/cloudformation-deployments.test.ts index a86208fa587a2..baae814a09aef 100644 --- a/packages/aws-cdk/test/api/cloudformation-deployments.test.ts +++ b/packages/aws-cdk/test/api/cloudformation-deployments.test.ts @@ -15,7 +15,7 @@ import { deployStack } from '../../lib/api/deploy-stack'; import { HotswapMode } from '../../lib/api/hotswap/common'; import { EcrRepositoryInfo, ToolkitInfo } from '../../lib/api/toolkit-info'; import { CloudFormationStack } from '../../lib/api/util/cloudformation'; -import { /*buildAssets,*/ publishAssets } from '../../lib/util/asset-publishing'; +import { buildAssets, publishAssets } from '../../lib/util/asset-publishing'; import { testStack } from '../util'; import { mockBootstrapStack, MockSdkProvider } from '../util/mock-sdk'; @@ -884,32 +884,31 @@ test('readCurrentTemplateWithNestedStacks() succesfully ignores stacks without m }); }); -// eslint-disable-next-line jest/no-commented-out-tests -// test('building assets', async () => { -// // GIVEN -// const stack = testStackWithAssetManifest(); - -// // WHEN -// await deployments.buildStackAssets({ -// stack, -// }); - -// // THEN -// const expectedAssetManifest = expect.objectContaining({ -// directory: stack.assembly.directory, -// manifest: expect.objectContaining({ -// files: expect.objectContaining({ -// fake: expect.anything(), -// }), -// }), -// }); -// const expectedEnvironment = expect.objectContaining({ -// account: 'account', -// name: 'aws://account/region', -// region: 'region', -// }); -// expect(buildAssets).toBeCalledWith(expectedAssetManifest, sdkProvider, expectedEnvironment, undefined); -// }); +test('building assets', async () => { + // GIVEN + const stack = testStackWithAssetManifest(); + + // WHEN + await deployments.buildStackAssets({ + stack, + }); + + // THEN + const expectedAssetManifest = expect.objectContaining({ + directory: stack.assembly.directory, + manifest: expect.objectContaining({ + files: expect.objectContaining({ + fake: expect.anything(), + }), + }), + }); + const expectedEnvironment = expect.objectContaining({ + account: 'account', + name: 'aws://account/region', + region: 'region', + }); + expect(buildAssets).toBeCalledWith(expectedAssetManifest, sdkProvider, expectedEnvironment, undefined); +}); function pushStackResourceSummaries(stackName: string, ...items: CloudFormation.StackResourceSummary[]) { if (!currentCfnStackResources[stackName]) { diff --git a/packages/aws-cdk/test/cdk-toolkit.test.ts b/packages/aws-cdk/test/cdk-toolkit.test.ts index 40c892672a22c..aa5d65c75b964 100644 --- a/packages/aws-cdk/test/cdk-toolkit.test.ts +++ b/packages/aws-cdk/test/cdk-toolkit.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable import/order */ // We need to mock the chokidar library, used by 'cdk watch' const mockChokidarWatcherOn = jest.fn(); const fakeChokidarWatcher = { @@ -58,14 +59,14 @@ import * as path from 'path'; import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import { Manifest } from '@aws-cdk/cloud-assembly-schema'; import * as cxapi from '@aws-cdk/cx-api'; -import { instanceMockFrom, MockCloudExecutable, TestStackArtifact } from './util'; +import { instanceMockFrom, MockCloudExecutable, TestStackArtifact, withMocked } from './util'; import { MockSdkProvider } from './util/mock-sdk'; import { Bootstrapper } from '../lib/api/bootstrap'; import { CloudFormationDeployments, DeployStackOptions, DestroyStackOptions } from '../lib/api/cloudformation-deployments'; import { DeployStackResult } from '../lib/api/deploy-stack'; import { HotswapMode } from '../lib/api/hotswap/common'; import { Template } from '../lib/api/util/cloudformation'; -import { CdkToolkit, Tag } from '../lib/cdk-toolkit'; +import { CdkToolkit, Tag, AssetBuildTime } from '../lib/cdk-toolkit'; import { RequireApproval } from '../lib/diff'; import { flatten } from '../lib/util'; @@ -584,37 +585,63 @@ describe('deploy', () => { expect(mockSynthesize).not.toHaveBeenCalled(); }); - // eslint-disable-next-line jest/no-commented-out-tests - // test('can disable asset parallelism', async () => { - // // GIVEN - // cloudExecutable = new MockCloudExecutable({ - // stacks: [MockStack.MOCK_STACK_WITH_ASSET], - // }); - // const fakeCloudFormation = new FakeCloudFormation({}); - - // const toolkit = new CdkToolkit({ - // cloudExecutable, - // configuration: cloudExecutable.configuration, - // sdkProvider: cloudExecutable.sdkProvider, - // cloudFormation: fakeCloudFormation, - // }); - - // // WHEN - // // Not the best test but following this through to the asset publishing library fails - // await withMocked(fakeCloudFormation, 'buildAssets', async (mockBuildAssets) => { - // await toolkit.deploy({ - // selector: { patterns: ['Test-Stack-Asset'] }, - // assetParallelism: false, - // hotswap: HotswapMode.FULL_DEPLOYMENT, - // }); - - // expect(mockBuildAssets).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({ - // buildOptions: expect.objectContaining({ - // parallel: false, - // }), - // })); - // }); - // }); + test('can disable asset parallelism', async () => { + // GIVEN + cloudExecutable = new MockCloudExecutable({ + stacks: [MockStack.MOCK_STACK_WITH_ASSET], + }); + const fakeCloudFormation = new FakeCloudFormation({}); + + const toolkit = new CdkToolkit({ + cloudExecutable, + configuration: cloudExecutable.configuration, + sdkProvider: cloudExecutable.sdkProvider, + cloudFormation: fakeCloudFormation, + }); + + // WHEN + // Not the best test but following this through to the asset publishing library fails + await withMocked(fakeCloudFormation, 'buildStackAssets', async (mockBuildStackAssets) => { + await toolkit.deploy({ + selector: { patterns: ['Test-Stack-Asset'] }, + assetParallelism: false, + hotswap: HotswapMode.FULL_DEPLOYMENT, + }); + + expect(mockBuildStackAssets).toHaveBeenCalledWith(expect.objectContaining({ + buildOptions: expect.objectContaining({ + parallel: false, + }), + })); + }); + }); + + test('can disable asset prebuild', async () => { + // GIVEN + cloudExecutable = new MockCloudExecutable({ + stacks: [MockStack.MOCK_STACK_WITH_ASSET], + }); + const fakeCloudFormation = new FakeCloudFormation({}); + + const toolkit = new CdkToolkit({ + cloudExecutable, + configuration: cloudExecutable.configuration, + sdkProvider: cloudExecutable.sdkProvider, + cloudFormation: fakeCloudFormation, + }); + + // WHEN + // Not the best test but following this through to the asset publishing library fails + await withMocked(fakeCloudFormation, 'buildStackAssets', async (mockBuildStackAssets) => { + await toolkit.deploy({ + selector: { patterns: ['Test-Stack-Asset'] }, + assetBuildTime: AssetBuildTime.JUST_IN_TIME, + hotswap: HotswapMode.FULL_DEPLOYMENT, + }); + + expect(mockBuildStackAssets).not.toHaveBeenCalled(); + }); + }); }); }); diff --git a/packages/aws-cdk/test/deploy.test.ts b/packages/aws-cdk/test/deploy.test.ts index 12012c7a74541..a161867b8db48 100644 --- a/packages/aws-cdk/test/deploy.test.ts +++ b/packages/aws-cdk/test/deploy.test.ts @@ -1,22 +1,12 @@ -import * as path from 'path'; +/* eslint-disable import/order */ import * as cxapi from '@aws-cdk/cx-api'; -import { deployArtifacts } from '../lib/deploy'; -import { AssetBuildNode, AssetPublishNode, StackNode } from '../lib/util/work-graph-types'; +import { deployStacks } from '../lib/deploy'; -const ASSET_MANIFEST_ARTIFACT_SYM = Symbol.for('@aws-cdk/cx-api.AssetManifestArtifact'); -const CLOUDFORMATION_STACK_ARTIFACT_SYM = Symbol.for('@aws-cdk/cx-api.CloudFormationStackArtifact'); -const TREE_CLOUD_ARTIFACT_SYM = Symbol.for('@aws-cdk/cx-api.TreeCloudArtifact'); -const NESTED_ASSEMBLY_ARTIFACT = Symbol.for('@aws-cdk/cx-api.NestedCloudAssemblyArtifact'); - -type Artifact = cxapi.CloudArtifact; type Stack = cxapi.CloudFormationStackArtifact; -type Asset = cxapi.AssetManifestArtifact; -type Tree = cxapi.TreeCloudArtifact; -type Nested = cxapi.NestedCloudAssemblyArtifact; const sleep = async (duration: number) => new Promise((resolve) => setTimeout(() => resolve(), duration)); -// Not great to have actual sleeps in the tests, but they mostly just exist to give the async workflow +// Not great to have actual sleeps in the tests, but they mostly just exist to give 'p-queue' // a chance to start new tasks. const SLOW = 200; @@ -25,370 +15,186 @@ const SLOW = 200; * - stack.name = deployment duration * - stack.displayName = error message */ -describe('DeployAssets', () => { - const actionedAssets: string[] = []; - const callbacks = { - deployStack: async (x: StackNode) => { - const errorMessage = x.stack.displayName; - const timeout = Number(x.stack.name) || 0; +describe('DeployStacks', () => { + const deployedStacks: string[] = []; + const deployStack = async ({ id, displayName, name }: Stack) => { + const errorMessage = displayName; + const timeout = Number(name) || 0; - await sleep(timeout); + await sleep(timeout); - // Special case for testing NestedCloudAssemblyArtifacts - if (errorMessage && !errorMessage.startsWith('Nested')) { - throw Error(errorMessage); - } + if (errorMessage) { + throw Error(errorMessage); + } - actionedAssets.push(x.id); - }, - buildAsset: async({ id }: AssetBuildNode) => { - actionedAssets.push(id); - }, - publishAsset: async({ id }: AssetPublishNode) => { - actionedAssets.push(id); - }, + deployedStacks.push(id); }; beforeEach(() => { - actionedAssets.splice(0); + deployedStacks.splice(0); }); // Success test.each([ // Concurrency 1 { scenario: 'No Stacks', concurrency: 1, toDeploy: [], expected: [] }, - { scenario: 'A', concurrency: 1, toDeploy: createArtifacts([{ id: 'A', type: 'stack' }]), expected: ['A'] }, - { scenario: 'A, B', concurrency: 1, toDeploy: createArtifacts([{ id: 'A', type: 'stack' }, { id: 'B', type: 'stack' }]), expected: ['A', 'B'] }, - { scenario: 'A -> B', concurrency: 1, toDeploy: createArtifacts([{ id: 'A', type: 'stack' }, { id: 'B', type: 'stack', stackDependencies: ['A'] }]), expected: ['A', 'B'] }, - { scenario: '[unsorted] A -> B', concurrency: 1, toDeploy: createArtifacts([{ id: 'B', type: 'stack', stackDependencies: ['A'] }, { id: 'A', type: 'stack' }]), expected: ['A', 'B'] }, - { scenario: 'A -> B -> C', concurrency: 1, toDeploy: createArtifacts([{ id: 'A', type: 'stack' }, { id: 'B', type: 'stack', stackDependencies: ['A'] }, { id: 'C', type: 'stack', stackDependencies: ['B'] }]), expected: ['A', 'B', 'C'] }, - { scenario: 'A -> B, A -> C', concurrency: 1, toDeploy: createArtifacts([{ id: 'A', type: 'stack' }, { id: 'B', type: 'stack', stackDependencies: ['A'] }, { id: 'C', type: 'stack', stackDependencies: ['A'] }]), expected: ['A', 'B', 'C'] }, + { scenario: 'A', concurrency: 1, toDeploy: [{ id: 'A', dependencies: [] }], expected: ['A'] }, + { scenario: 'A, B', concurrency: 1, toDeploy: [{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [] }], expected: ['A', 'B'] }, + { scenario: 'A -> B', concurrency: 1, toDeploy: [{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [{ id: 'A' }] }], expected: ['A', 'B'] }, + { scenario: '[unsorted] A -> B', concurrency: 1, toDeploy: [{ id: 'B', dependencies: [{ id: 'A' }] }, { id: 'A', dependencies: [] }], expected: ['A', 'B'] }, + { scenario: 'A -> B -> C', concurrency: 1, toDeploy: [{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [{ id: 'A' }] }, { id: 'C', dependencies: [{ id: 'B' }] }], expected: ['A', 'B', 'C'] }, + { scenario: 'A -> B, A -> C', concurrency: 1, toDeploy: [{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [{ id: 'A' }] }, { id: 'C', dependencies: [{ id: 'A' }] }], expected: ['A', 'B', 'C'] }, { scenario: 'A (slow), B', concurrency: 1, - toDeploy: createArtifacts([ - { id: 'A', type: 'stack', name: SLOW }, - { id: 'B', type: 'stack' }, - ]), + toDeploy: [ + { id: 'A', dependencies: [], name: SLOW }, + { id: 'B', dependencies: [] }, + ], expected: ['A', 'B'], }, { scenario: 'A -> B, C -> D', concurrency: 1, - toDeploy: createArtifacts([ - { id: 'A', type: 'stack' }, - { id: 'B', type: 'stack', stackDependencies: ['A'] }, - { id: 'C', type: 'stack' }, - { id: 'D', type: 'stack', stackDependencies: ['C'] }, - ]), + toDeploy: [ + { id: 'A', dependencies: [] }, + { id: 'B', dependencies: [{ id: 'A' }] }, + { id: 'C', dependencies: [] }, + { id: 'D', dependencies: [{ id: 'C' }] }, + ], expected: ['A', 'C', 'B', 'D'], }, { scenario: 'A (slow) -> B, C -> D', concurrency: 1, - toDeploy: createArtifacts([ - { id: 'A', type: 'stack', name: SLOW }, - { id: 'B', type: 'stack', stackDependencies: ['A'] }, - { id: 'C', type: 'stack' }, - { id: 'D', type: 'stack', stackDependencies: ['C'] }, - ]), + toDeploy: [ + { id: 'A', dependencies: [], name: SLOW }, + { id: 'B', dependencies: [{ id: 'A' }] }, + { id: 'C', dependencies: [] }, + { id: 'D', dependencies: [{ id: 'C' }] }, + ], expected: ['A', 'C', 'B', 'D'], }, - // With Assets - { - scenario: 'A -> a', - concurrency: 1, - toDeploy: createArtifacts([ - { id: 'A', type: 'stack', assetDependencies: ['a'] }, - { id: 'a', type: 'asset' }, - ]), - expected: ['a-build', 'a-publish', 'A'], - }, - { - scenario: 'A -> [a, B]', - concurrency: 1, - toDeploy: createArtifacts([ - { id: 'A', type: 'stack', stackDependencies: ['B'], assetDependencies: ['a'] }, - { id: 'B', type: 'stack' }, - { id: 'a', type: 'asset', name: SLOW }, - ]), - expected: ['B', 'a-build', 'a-publish', 'A'], - }, - { - scenario: 'A -> a, B -> b', - concurrency: 1, - toDeploy: createArtifacts([ - { id: 'A', type: 'stack', assetDependencies: ['a'] }, - { id: 'B', type: 'stack', assetDependencies: ['b'] }, - { id: 'a', type: 'asset' }, - { id: 'b', type: 'asset' }, - ]), - expected: ['a-build', 'b-build', 'a-publish', 'b-publish', 'A', 'B'], - }, - { - scenario: 'A, B -> b -> A', - concurrency: 1, - toDeploy: createArtifacts([ - { id: 'A', type: 'stack' }, - { id: 'B', type: 'stack', assetDependencies: ['b'] }, - { id: 'b', type: 'asset', stackDependencies: ['A'] }, - ]), - expected: ['A', 'b-build', 'b-publish', 'B'], - }, // Concurrency 2 { scenario: 'No Stacks', concurrency: 2, toDeploy: [], expected: [] }, - { scenario: 'A', concurrency: 2, toDeploy: createArtifacts([{ id: 'A', type: 'stack' }]), expected: ['A'] }, - { scenario: 'A, B', concurrency: 2, toDeploy: createArtifacts([{ id: 'A', type: 'stack' }, { id: 'B', type: 'stack' }]), expected: ['A', 'B'] }, - { scenario: 'A -> B', concurrency: 2, toDeploy: createArtifacts([{ id: 'A', type: 'stack' }, { id: 'B', type: 'stack', stackDependencies: ['A'] }]), expected: ['A', 'B'] }, - { scenario: '[unsorted] A -> B', concurrency: 2, toDeploy: createArtifacts([{ id: 'B', type: 'stack', stackDependencies: ['A'] }, { id: 'A', type: 'stack' }]), expected: ['A', 'B'] }, - { scenario: 'A -> B -> C', concurrency: 2, toDeploy: createArtifacts([{ id: 'A', type: 'stack' }, { id: 'B', type: 'stack', stackDependencies: ['A'] }, { id: 'C', type: 'stack', stackDependencies: ['B'] }]), expected: ['A', 'B', 'C'] }, - { scenario: 'A -> B, A -> C', concurrency: 2, toDeploy: createArtifacts([{ id: 'A', type: 'stack' }, { id: 'B', type: 'stack', stackDependencies: ['A'] }, { id: 'C', type: 'stack', stackDependencies: ['A'] }]), expected: ['A', 'B', 'C'] }, + { scenario: 'A', concurrency: 2, toDeploy: [{ id: 'A', dependencies: [] }], expected: ['A'] }, + { scenario: 'A, B', concurrency: 2, toDeploy: [{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [] }], expected: ['A', 'B'] }, + { scenario: 'A -> B', concurrency: 2, toDeploy: [{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [{ id: 'A' }] }], expected: ['A', 'B'] }, + { scenario: '[unsorted] A -> B', concurrency: 2, toDeploy: [{ id: 'B', dependencies: [{ id: 'A' }] }, { id: 'A', dependencies: [] }], expected: ['A', 'B'] }, + { scenario: 'A -> B -> C', concurrency: 2, toDeploy: [{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [{ id: 'A' }] }, { id: 'C', dependencies: [{ id: 'B' }] }], expected: ['A', 'B', 'C'] }, + { scenario: 'A -> B, A -> C', concurrency: 2, toDeploy: [{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [{ id: 'A' }] }, { id: 'C', dependencies: [{ id: 'A' }] }], expected: ['A', 'B', 'C'] }, { scenario: 'A, B', concurrency: 2, - toDeploy: createArtifacts([ - { id: 'A', type: 'stack', name: SLOW }, - { id: 'B', type: 'stack' }, - ]), + toDeploy: [ + { id: 'A', dependencies: [], name: SLOW }, + { id: 'B', dependencies: [] }, + ], expected: ['B', 'A'], }, { scenario: 'A -> B, C -> D', concurrency: 2, - toDeploy: createArtifacts([ - { id: 'A', type: 'stack' }, - { id: 'B', type: 'stack', stackDependencies: ['A'] }, - { id: 'C', type: 'stack' }, - { id: 'D', type: 'stack', stackDependencies: ['C'] }, - ]), + toDeploy: [ + { id: 'A', dependencies: [] }, + { id: 'B', dependencies: [{ id: 'A' }] }, + { id: 'C', dependencies: [] }, + { id: 'D', dependencies: [{ id: 'C' }] }, + ], expected: ['A', 'C', 'B', 'D'], }, { scenario: 'A (slow) -> B, C -> D', concurrency: 2, - toDeploy: createArtifacts([ - { id: 'A', type: 'stack', name: SLOW }, - { id: 'B', type: 'stack', stackDependencies: ['A'] }, - { id: 'C', type: 'stack' }, - { id: 'D', type: 'stack', stackDependencies: ['C'] }, - ]), + toDeploy: [ + { id: 'A', dependencies: [], name: SLOW }, + { id: 'B', dependencies: [{ id: 'A' }] }, + { id: 'C', dependencies: [] }, + { id: 'D', dependencies: [{ id: 'C' }] }, + ], expected: ['C', 'D', 'A', 'B'], }, { scenario: 'A -> B, A not selected', concurrency: 1, - toDeploy: createArtifacts([ - { id: 'B', type: 'stack', stackDependencies: ['A'] }, - ]), + toDeploy: [ + { id: 'B', dependencies: [{ id: 'A' }] }, + ], expected: ['B'], }, - // With Assets - { - scenario: 'A -> a', - concurrency: 2, - toDeploy: createArtifacts([ - { id: 'A', type: 'stack', assetDependencies: ['a'] }, - { id: 'a', type: 'asset' }, - ]), - expected: ['a-build', 'a-publish', 'A'], - }, - { - scenario: 'A -> [a, B]', - concurrency: 2, - toDeploy: createArtifacts([ - { id: 'A', type: 'stack', stackDependencies: ['B'], assetDependencies: ['a'] }, - { id: 'B', type: 'stack', name: SLOW }, - { id: 'a', type: 'asset' }, - ]), - expected: ['a-build', 'a-publish', 'B', 'A'], - }, - { - scenario: 'A -> a, B -> b', - concurrency: 2, - toDeploy: createArtifacts([ - { id: 'A', type: 'stack', assetDependencies: ['a'] }, - { id: 'B', type: 'stack', assetDependencies: ['b'] }, - { id: 'a', type: 'asset' }, - { id: 'b', type: 'asset' }, - ]), - expected: ['a-build', 'b-build', 'a-publish', 'b-publish', 'A', 'B'], - }, - { - scenario: 'A, B -> b -> A', - concurrency: 2, - toDeploy: createArtifacts([ - { id: 'A', type: 'stack' }, - { id: 'B', type: 'stack', assetDependencies: ['b'] }, - { id: 'b', type: 'asset', stackDependencies: ['A'] }, - ]), - expected: ['A', 'b-build', 'b-publish', 'B'], - }, - { - scenario: 'A, B -> [b, c], b -> A', - concurrency: 2, - toDeploy: createArtifacts([ - { id: 'A', type: 'stack', name: SLOW }, - { id: 'B', type: 'stack', assetDependencies: ['b', 'c'] }, - { id: 'b', type: 'asset', stackDependencies: ['A'] }, - { id: 'c', type: 'asset' }, - ]), - expected: ['c-build', 'c-publish', 'A', 'b-build', 'b-publish', 'B'], - }, ])('Success - Concurrency: $concurrency - $scenario', async ({ concurrency, expected, toDeploy }) => { - await expect(deployArtifacts(toDeploy, { concurrency, callbacks, prebuildAssets: true })).resolves.toBeUndefined(); + await expect(deployStacks(toDeploy as unknown as Stack[], { concurrency, deployStack })).resolves.toBeUndefined(); - expect(actionedAssets).toStrictEqual(expected); + expect(deployedStacks).toStrictEqual(expected); }); // Failure test.each([ // Concurrency 1 - { scenario: 'A (error)', concurrency: 1, toDeploy: createArtifacts([{ id: 'A', type: 'stack', displayName: 'A' }]), expectedError: 'A', expectedStacks: [] }, - { scenario: 'A (error), B', concurrency: 1, toDeploy: createArtifacts([{ id: 'A', type: 'stack', displayName: 'A' }, { id: 'B', type: 'stack' }]), expectedError: 'A', expectedStacks: [] }, - { scenario: 'A, B (error)', concurrency: 1, toDeploy: createArtifacts([{ id: 'A', type: 'stack' }, { id: 'B', type: 'stack', displayName: 'B' }]), expectedError: 'B', expectedStacks: ['A'] }, - { scenario: 'A (error) -> B', concurrency: 1, toDeploy: createArtifacts([{ id: 'A', type: 'stack', displayName: 'A' }, { id: 'B', type: 'stack', stackDependencies: ['A'] }]), expectedError: 'A', expectedStacks: [] }, - { scenario: '[unsorted] A (error) -> B', concurrency: 1, toDeploy: createArtifacts([{ id: 'B', type: 'stack', stackDependencies: ['A'] }, { id: 'A', type: 'stack', displayName: 'A' }]), expectedError: 'A', expectedStacks: [] }, + { scenario: 'A (error)', concurrency: 1, toDeploy: [{ id: 'A', dependencies: [], displayName: 'A' }], expectedError: 'A', expectedStacks: [] }, + { scenario: 'A (error), B', concurrency: 1, toDeploy: [{ id: 'A', dependencies: [], displayName: 'A' }, { id: 'B', dependencies: [] }], expectedError: 'A', expectedStacks: [] }, + { scenario: 'A, B (error)', concurrency: 1, toDeploy: [{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [], displayName: 'B' }], expectedError: 'B', expectedStacks: ['A'] }, + { scenario: 'A (error) -> B', concurrency: 1, toDeploy: [{ id: 'A', dependencies: [], displayName: 'A' }, { id: 'B', dependencies: [{ id: 'A' }] }], expectedError: 'A', expectedStacks: [] }, + { scenario: '[unsorted] A (error) -> B', concurrency: 1, toDeploy: [{ id: 'B', dependencies: [{ id: 'A' }] }, { id: 'A', dependencies: [], displayName: 'A' }], expectedError: 'A', expectedStacks: [] }, { scenario: 'A (error) -> B, C -> D', concurrency: 1, - toDeploy: createArtifacts([ - { id: 'A', type: 'stack', displayName: 'A' }, - { id: 'B', type: 'stack', stackDependencies: ['A'] }, - { id: 'C', type: 'stack' }, - { id: 'D', type: 'stack', stackDependencies: ['C'] }, - ]), + toDeploy: [ + { id: 'A', dependencies: [], displayName: 'A' }, + { id: 'B', dependencies: [{ id: 'A' }] }, + { id: 'C', dependencies: [] }, + { id: 'D', dependencies: [{ id: 'C' }] }, + ], expectedError: 'A', expectedStacks: [], }, { scenario: 'A -> B, C (error) -> D', concurrency: 1, - toDeploy: createArtifacts([ - { id: 'A', type: 'stack' }, - { id: 'B', type: 'stack', stackDependencies: ['A'] }, - { id: 'C', type: 'stack', displayName: 'C', name: SLOW }, - { id: 'D', type: 'stack', stackDependencies: ['C'] }, - ]), + toDeploy: [ + { id: 'A', dependencies: [] }, + { id: 'B', dependencies: [{ id: 'A' }] }, + { id: 'C', dependencies: [], displayName: 'C', name: SLOW }, + { id: 'D', dependencies: [{ id: 'C' }] }, + ], expectedError: 'C', expectedStacks: ['A'], }, // Concurrency 2 - { scenario: 'A (error)', concurrency: 2, toDeploy: createArtifacts([{ id: 'A', type: 'stack', displayName: 'A' }]), expectedError: 'A', expectedStacks: [] }, - { scenario: 'A (error), B', concurrency: 2, toDeploy: createArtifacts([{ id: 'A', type: 'stack', displayName: 'A' }, { id: 'B', type: 'stack' }]), expectedError: 'A', expectedStacks: ['B'] }, - { scenario: 'A, B (error)', concurrency: 2, toDeploy: createArtifacts([{ id: 'A', type: 'stack' }, { id: 'B', type: 'stack', displayName: 'B' }]), expectedError: 'B', expectedStacks: ['A'] }, - { scenario: 'A (error) -> B', concurrency: 2, toDeploy: createArtifacts([{ id: 'A', type: 'stack', displayName: 'A' }, { id: 'B', type: 'stack', stackDependencies: ['A'] }]), expectedError: 'A', expectedStacks: [] }, - { scenario: '[unsorted] A (error) -> B', concurrency: 2, toDeploy: createArtifacts([{ id: 'B', type: 'stack', stackDependencies: ['A'] }, { id: 'A', type: 'stack', displayName: 'A' }]), expectedError: 'A', expectedStacks: [] }, + { scenario: 'A (error)', concurrency: 2, toDeploy: [{ id: 'A', dependencies: [], displayName: 'A' }], expectedError: 'A', expectedStacks: [] }, + { scenario: 'A (error), B', concurrency: 2, toDeploy: [{ id: 'A', dependencies: [], displayName: 'A' }, { id: 'B', dependencies: [] }], expectedError: 'A', expectedStacks: ['B'] }, + { scenario: 'A, B (error)', concurrency: 2, toDeploy: [{ id: 'A', dependencies: [] }, { id: 'B', dependencies: [], displayName: 'B' }], expectedError: 'B', expectedStacks: ['A'] }, + { scenario: 'A (error) -> B', concurrency: 2, toDeploy: [{ id: 'A', dependencies: [], displayName: 'A' }, { id: 'B', dependencies: [{ id: 'A' }] }], expectedError: 'A', expectedStacks: [] }, + { scenario: '[unsorted] A (error) -> B', concurrency: 2, toDeploy: [{ id: 'B', dependencies: [{ id: 'A' }] }, { id: 'A', dependencies: [], displayName: 'A' }], expectedError: 'A', expectedStacks: [] }, { scenario: 'A (error) -> B, C -> D', concurrency: 2, - toDeploy: createArtifacts([ - { id: 'A', type: 'stack', displayName: 'A' }, - { id: 'B', type: 'stack', stackDependencies: ['A'] }, - { id: 'C', type: 'stack' }, - { id: 'D', type: 'stack', stackDependencies: ['C'] }, - ]), + toDeploy: [ + { id: 'A', dependencies: [], displayName: 'A' }, + { id: 'B', dependencies: [{ id: 'A' }] }, + { id: 'C', dependencies: [] }, + { id: 'D', dependencies: [{ id: 'C' }] }, + ], expectedError: 'A', expectedStacks: ['C'], }, { scenario: 'A -> B, C (error) -> D', concurrency: 2, - toDeploy: createArtifacts([ - { id: 'A', type: 'stack' }, - { id: 'B', type: 'stack', stackDependencies: ['A'] }, - { id: 'C', type: 'stack', displayName: 'C', name: SLOW }, - { id: 'D', type: 'stack', stackDependencies: ['C'] }, - ]), + toDeploy: [ + { id: 'A', dependencies: [] }, + { id: 'B', dependencies: [{ id: 'A' }] }, + { id: 'C', dependencies: [], displayName: 'C', name: SLOW }, + { id: 'D', dependencies: [{ id: 'C' }] }, + ], expectedError: 'C', expectedStacks: ['A', 'B'], }, ])('Failure - Concurrency: $concurrency - $scenario', async ({ concurrency, expectedError, toDeploy, expectedStacks }) => { - // eslint-disable-next-line max-len - await expect(deployArtifacts(toDeploy, { concurrency, callbacks, prebuildAssets: true })).rejects.toThrowError(expectedError); - - expect(actionedAssets).toStrictEqual(expectedStacks); - }); - - test('can disable prebuild assets', async () => { - const toDeploy = createArtifacts([ - { id: 'A', type: 'stack', name: SLOW }, - { id: 'B', type: 'stack', stackDependencies: ['A'], assetDependencies: ['b'] }, - { id: 'b', type: 'asset' }, - ]); - await expect(deployArtifacts(toDeploy, { concurrency: 2, callbacks, prebuildAssets: false })).resolves.toBeUndefined(); + await expect(deployStacks(toDeploy as unknown as Stack[], { concurrency, deployStack })).rejects.toThrowError(expectedError); - // asset build waits for slow stack A deployment - expect(actionedAssets).toStrictEqual(['A', 'b-build', 'b-publish', 'B']); + expect(deployedStacks).toStrictEqual(expectedStacks); }); - - test('tree metadata is ignored', async () => { - const toDeploy = createArtifacts([ - { id: '$', type: 'tree' }, - ]); - await expect(deployArtifacts(toDeploy, { concurrency: 1, callbacks })).resolves.toBeUndefined(); - - expect(actionedAssets).toStrictEqual([]); - }); - - test('nested assembly is accepted', async () => { - const toDeploy = createArtifacts([ - { id: 'Stage', type: 'nested' }, - ]); - await expect(deployArtifacts(toDeploy, { concurrency: 1, callbacks })).resolves.toBeUndefined(); - - expect(actionedAssets).toStrictEqual(['StageA.assets-build', 'StageA.assets-publish', 'NestedStageA']); - }); -}); - -interface TestArtifact { - stackDependencies?: string[]; - assetDependencies?: string[]; - id: string; - type: 'stack' | 'asset' | 'tree'| 'nested'; - name?: number; - displayName?: string; -} - -function createArtifact(artifact: TestArtifact): Artifact { - const stackDeps: Artifact[] = artifact.stackDependencies?.map((id) => createArtifact({ id, type: 'stack' })) ?? []; - const assetDeps: Artifact[] = artifact.assetDependencies?.map((id) => createArtifact({ id, type: 'asset' })) ?? []; - - const art = { - id: artifact.id, - dependencies: stackDeps.concat(assetDeps), - name: artifact.name, - displayName: artifact.displayName, - }; - switch (artifact.type) { - case 'stack': - return { - ...art, - [CLOUDFORMATION_STACK_ARTIFACT_SYM]: true, - } as unknown as Stack; - case 'asset': - return { - ...art, - [ASSET_MANIFEST_ARTIFACT_SYM]: true, - } as unknown as Asset; - case 'tree': - return { - type: 'cdk:tree', - properties: { - file: 'tree.json', - }, - [TREE_CLOUD_ARTIFACT_SYM]: true, - } as unknown as Tree; - case 'nested': - return { - type: 'cdk:cloud-assembly', - directoryName: path.join(__dirname, 'stage-manifest'), - [NESTED_ASSEMBLY_ARTIFACT]: true, - } as unknown as Nested; - } -} - -function createArtifacts(artifacts: TestArtifact[]): Artifact[] { - return artifacts.map((art) => createArtifact(art)); -} +}); \ No newline at end of file From 5162dca603e224bbab6e553d0aa5635953425b70 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Mon, 15 May 2023 10:35:20 +0200 Subject: [PATCH 107/120] Remove some more files --- .../cx-api/test/cloud-assembly.test.ts | 7 + packages/aws-cdk/lib/util/work-graph-types.ts | 80 -------- packages/aws-cdk/lib/util/work-graph.ts | 193 ------------------ .../aws-cdk/test/stage-manifest/manifest.json | 24 --- 4 files changed, 7 insertions(+), 297 deletions(-) delete mode 100644 packages/aws-cdk/lib/util/work-graph-types.ts delete mode 100644 packages/aws-cdk/lib/util/work-graph.ts delete mode 100644 packages/aws-cdk/test/stage-manifest/manifest.json diff --git a/packages/aws-cdk-lib/cx-api/test/cloud-assembly.test.ts b/packages/aws-cdk-lib/cx-api/test/cloud-assembly.test.ts index 804d1e43e16b6..b3965026a98fb 100644 --- a/packages/aws-cdk-lib/cx-api/test/cloud-assembly.test.ts +++ b/packages/aws-cdk-lib/cx-api/test/cloud-assembly.test.ts @@ -168,6 +168,13 @@ test('can read assembly with asset manifest', () => { expect(assembly.artifacts).toHaveLength(2); }); +test('can toposort assembly with asset dependency', () => { + const assembly = new CloudAssembly(path.join(FIXTURES, 'asset-depends')); + expect(assembly.stacks).toHaveLength(2); + expect(assembly.artifacts).toHaveLength(3); + expect(assembly.artifacts[0].id).toEqual('StagingStack'); +}); + test('getStackArtifact retrieves a stack by artifact id from a nested assembly', () => { const assembly = new CloudAssembly(path.join(FIXTURES, 'nested-assemblies')); diff --git a/packages/aws-cdk/lib/util/work-graph-types.ts b/packages/aws-cdk/lib/util/work-graph-types.ts deleted file mode 100644 index 6c1dbb4275f48..0000000000000 --- a/packages/aws-cdk/lib/util/work-graph-types.ts +++ /dev/null @@ -1,80 +0,0 @@ -import * as cxapi from '@aws-cdk/cx-api'; - -export enum DeploymentState { - PENDING = 'pending', - QUEUED = 'queued', - DEPLOYING = 'deploying', - COMPLETED = 'completed', - FAILED = 'failed', - SKIPPED = 'skipped', -}; - -interface WorkNodeOptions { - readonly id: string; - readonly dependencies: string[]; -} - -export enum WorkType { - STACK_DEPLOY = 'stack', - ASSET_BUILD = 'asset-build', - ASSET_PUBLISH = 'asset-publish', -} - -export abstract class WorkNode { - public readonly id: string; - public readonly dependencies: string[]; - public deploymentState: DeploymentState; - abstract type: WorkType; - - constructor(options: WorkNodeOptions) { - this.id = options.id; - this.dependencies = options.dependencies; - this.deploymentState = DeploymentState.PENDING; - } -} - -export interface StackNodeOptions extends WorkNodeOptions { - readonly stack: cxapi.CloudFormationStackArtifact; -} - -export class StackNode extends WorkNode { - public readonly stack: cxapi.CloudFormationStackArtifact; - public readonly type = WorkType.STACK_DEPLOY; - - constructor(options: StackNodeOptions) { - super(options); - this.stack = options.stack; - } -} - -interface AssetOptions extends WorkNodeOptions { - readonly asset: cxapi.AssetManifestArtifact; - readonly parentStack: cxapi.CloudFormationStackArtifact; -} -export interface AssetBuildNodeOptions extends AssetOptions {} - -export class AssetBuildNode extends WorkNode { - public readonly asset: cxapi.AssetManifestArtifact; - public readonly parentStack: cxapi.CloudFormationStackArtifact; - public readonly type = WorkType.ASSET_BUILD; - - constructor(options: AssetBuildNodeOptions) { - super(options); - this.asset = options.asset; - this.parentStack = options.parentStack; - } -} - -export class AssetPublishNode extends WorkNode { - public readonly asset: cxapi.AssetManifestArtifact; - public readonly parentStack: cxapi.CloudFormationStackArtifact; - public readonly type = WorkType.ASSET_PUBLISH; - - constructor(options: AssetBuildNodeOptions) { - super(options); - this.asset = options.asset; - this.parentStack = options.parentStack; - } -} - -export interface PartialAssetNodeOptions extends Omit {} diff --git a/packages/aws-cdk/lib/util/work-graph.ts b/packages/aws-cdk/lib/util/work-graph.ts deleted file mode 100644 index 580dedf4b3b90..0000000000000 --- a/packages/aws-cdk/lib/util/work-graph.ts +++ /dev/null @@ -1,193 +0,0 @@ -/* eslint-disable no-console */ -import * as cxapi from '@aws-cdk/cx-api'; -import { WorkNode, DeploymentState, AssetBuildNode, AssetPublishNode, PartialAssetNodeOptions, StackNode } from './work-graph-types'; - -export class WorkGraph { - public static fromCloudArtifacts(artifacts: cxapi.CloudArtifact[], prebuildAssets: boolean) { - const graph = new WorkGraph(); - - // Associated stack will be lazily added for Assets - const partialAssetNodes: PartialAssetNodeOptions[] = []; - const parentStacks: Record = {}; - - for (const artifact of artifacts) { - if (cxapi.AssetManifestArtifact.isAssetManifestArtifact(artifact)) { - partialAssetNodes.push({ - id: `${artifact.id}`, - dependencies: getDepIds(artifact.dependencies), - asset: artifact, - }); - } else if (cxapi.CloudFormationStackArtifact.isCloudFormationStackArtifact(artifact)) { - graph.addNodes(new StackNode({ - id: artifact.id, - dependencies: getDepIds(artifact.dependencies), - stack: artifact, - })); - updateParentStacks(artifact); - } else if (cxapi.TreeCloudArtifact.isTreeCloudArtifact(artifact)) { - // ignore tree artifacts - continue; - } else if (cxapi.NestedCloudAssemblyArtifact.isNestedCloudAssemblyArtifact(artifact)) { - console.log('NESTED!!!'); - const assembly = new cxapi.CloudAssembly(artifact.fullPath); - const nestedGraph = WorkGraph.fromCloudArtifacts(assembly.artifacts, prebuildAssets); - graph.addNodes(...Object.values(nestedGraph.nodes)); - } - } - - // post-process parent stacks of each asset because we only know - // this information after all artifacts have been processed. - for (const assetNode of partialAssetNodes) { - const stack = parentStacks[assetNode.id]; - if (!stack) { - throw new Error(`No stack associated with ${assetNode.id} artifact. Something in the source code or asset manifest is wrong.`); - } - graph.addNodes(...[ - new AssetBuildNode({ - id: `${assetNode.id}-build`, - // If we disable prebuild, then assets inherit dependencies from their parent stack - dependencies: assetNode.dependencies.concat(!prebuildAssets ? onlyStackDeps(graph.getNode(stack.id).dependencies) : []), - asset: assetNode.asset, - parentStack: stack, - }), - new AssetPublishNode({ - id: `${assetNode.id}-publish`, - dependencies: [`${assetNode.id}-build`], // only depend on the build asset step - asset: assetNode.asset, - parentStack: stack, - }), - ]); - } - - // Ensure all dependencies actually exist. This protects against scenarios such as the following: - // StackA depends on StackB, but StackB is not selected to deploy. The dependency is redundant - // and will be dropped. - // This assumes the manifest comes uncorrupted so we will not fail if a dependency is not found. - for (const node of Object.values(graph.nodes)) { - if (node.type !== 'stack') { continue; } - const removeDeps = []; - for (const dep of node.dependencies) { - if (graph.nodes[dep] === undefined) { - removeDeps.push(dep); - } - } - removeDeps.forEach((d) => { - const i = node.dependencies.indexOf(d); - node.dependencies.splice(i, 1); - }); - } - - return graph; - - function updateParentStacks(stack: cxapi.CloudFormationStackArtifact) { - const assetArtifacts = stack.dependencies.filter(cxapi.AssetManifestArtifact.isAssetManifestArtifact); - for (const art of assetArtifacts) { - parentStacks[art.id] = stack; - } - } - - function getDepIds(deps: cxapi.CloudArtifact[]): string[] { - const ids = []; - for (const artifact of deps) { - if (cxapi.AssetManifestArtifact.isAssetManifestArtifact(artifact)) { - // Depend on only the publish step. The publish step will depend on the build step on its own. - ids.push(`${artifact.id}-publish`); - } else { - ids.push(artifact.id); - } - } - return ids; - } - - function onlyStackDeps(ids: string[]): string[] { - return ids.filter((i) => !i.endsWith('publish')); - } - } - - private readonly nodes: Record; - private readonly readyPool: Array = []; - public error?: Error; - - public constructor(nodes: Record = {}) { - this.nodes = nodes; - } - - public addNodes(...nodes: WorkNode[]) { - for (const node of nodes) { - this.nodes[node.id] = node; - } - } - - public done(): boolean { - return Object.values(this.nodes).every((n) => [DeploymentState.COMPLETED, DeploymentState.DEPLOYING].includes(n.deploymentState)); - } - - public hasFailed(): boolean { - return Object.values(this.nodes).some((n) => n.deploymentState === DeploymentState.FAILED); - } - - public hasNext(): boolean { - this.updateReadyPool(); - return this.readyPool.length > 0; - } - - public next(): WorkNode | undefined { - this.updateReadyPool(); - if (this.readyPool.length > 0) { - const node = this.readyPool.shift()!; - // we experienced a failed deployment elsewhere - if (node.deploymentState !== DeploymentState.QUEUED) { return undefined; } - node.deploymentState = DeploymentState.DEPLOYING; - return node; - } - return undefined; - } - - public deployed(node: WorkNode) { - node.deploymentState = DeploymentState.COMPLETED; - } - - public failed(node: WorkNode, error?: Error) { - console.log('managing failure'); - this.error = error; - node.deploymentState = DeploymentState.FAILED; - this.skipRest(); - } - - public toString() { - const n = []; - for (const [id, node] of Object.entries(this.nodes)) { - n.push([id, node.type, node.dependencies]); - } - return n; - } - - private updateReadyPool() { - for (const node of Object.values(this.nodes)) { - if (node.deploymentState === DeploymentState.PENDING && - (node.dependencies.length === 0 || node.dependencies.every((id) => this.nodes[id].deploymentState === 'completed'))) { - node.deploymentState = DeploymentState.QUEUED; - this.readyPool.push(node); - } - } - for (let i = 0; i < this.readyPool.length; i++) { - const node = this.readyPool[i]; - if (node.deploymentState !== DeploymentState.QUEUED) { - this.readyPool.splice(i, 1); - } - } - } - - private skipRest() { - for (const node of Object.values(this.nodes)) { - if ([DeploymentState.QUEUED, DeploymentState.PENDING].includes(node.deploymentState)) { - node.deploymentState = DeploymentState.SKIPPED; - } - } - } - - private getNode(id: string) { - return this.nodes[id]; - } -} - diff --git a/packages/aws-cdk/test/stage-manifest/manifest.json b/packages/aws-cdk/test/stage-manifest/manifest.json deleted file mode 100644 index 4eb5c5743979e..0000000000000 --- a/packages/aws-cdk/test/stage-manifest/manifest.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "version": "31.0.0", - "artifacts": { - "StageA.assets": { - "type": "cdk:asset-manifest", - "properties": { - "file": "StageA.assets.json" - } - }, - "NestedStageA": { - "type": "aws:cloudformation:stack", - "environment": "aws://unknown-account/unknown-region", - "properties": { - "templateFile": "StageA.template.json", - "additionalDependencies": [ - "StageA.assets" - ] - }, - "dependencies": [ - "StageA.assets" - ] - } - } -} \ No newline at end of file From 34e0cf6bad931babbb50240a627e1ea3affdd162 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy <36202692+kaizencc@users.noreply.github.com> Date: Mon, 15 May 2023 17:42:15 -0400 Subject: [PATCH 108/120] Apply suggestions from code review Co-authored-by: Rico Hermans --- packages/@aws-cdk/app-staging-synthesizer/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer/README.md b/packages/@aws-cdk/app-staging-synthesizer/README.md index 75848abe62600..67ef6de4d019a 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/README.md +++ b/packages/@aws-cdk/app-staging-synthesizer/README.md @@ -35,12 +35,12 @@ graph TD A(Bootstrap Stack) --- B(CloudFormationExecutionRole
DeploymentActionRole
LookupRole
FilePublishingRole
ImagePublishingRole
StagingBucket
ContainerAssetsRepository
FileAssetsBucketEncryptionKey) ``` -Your CDK Applicaiton utilizes some of these resources when deploying. For example, if you have a file asset, +Your CDK Application utilizes these resources when deploying. For example, if you have a file asset, it gets uploaded to the `StagingBucket` using the `FilePublishingRole` when you run `cdk deploy`. -This library introduces an alternate model to bootstrapping, by splitting out essential CloudFormation iam roles +This library introduces an alternate model to bootstrapping, by splitting out essential CloudFormation IAM roles and staging resources. There will still be a Bootstrap Stack, but this will only contain IAM roles necessary for -CloudFormation deployment. Each CDK App will instead be in charge of it's own staging resources, including the +CloudFormation deployment. Each CDK App will instead be in charge of its own staging resources, including the S3 Bucket, ECR Repositories, and associated IAM roles. It works like this: The Staging Stack will contain, on a per-need basis, From 5a910bb66ba8a852025b0f96b2676ca1a6621fb3 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Mon, 15 May 2023 21:23:10 -0400 Subject: [PATCH 109/120] pr feedback wip --- .../app-staging-synthesizer/README.md | 171 +++++++++++++----- .../rosetta/custom-staging.ts-fixture | 35 ++++ .../test/default-staging-stack.test.ts | 2 +- .../test/integ.custom-staging-stack.ts | 14 -- 4 files changed, 163 insertions(+), 59 deletions(-) create mode 100644 packages/@aws-cdk/app-staging-synthesizer/rosetta/custom-staging.ts-fixture delete mode 100644 packages/@aws-cdk/app-staging-synthesizer/test/integ.custom-staging-stack.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/README.md b/packages/@aws-cdk/app-staging-synthesizer/README.md index 67ef6de4d019a..7862a8860a65b 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/README.md +++ b/packages/@aws-cdk/app-staging-synthesizer/README.md @@ -30,13 +30,47 @@ are as follows: Our current bootstrap model looks like this, when you run `cdk bootstrap aws:///` : -```mermaid -graph TD - A(Bootstrap Stack) --- B(CloudFormationExecutionRole
DeploymentActionRole
LookupRole
FilePublishingRole
ImagePublishingRole
StagingBucket
ContainerAssetsRepository
FileAssetsBucketEncryptionKey) +```text +┌───────────────────────────────────┐┌────────────────────────┐┌────────────────────────┐ +│ ││ ││ │ +│ ││ ││ │ +│ ┌───────────────┐ ││ ┌──────────────┐ ││ ┌──────────────┐ │ +│ │Bootstrap Stack│ ││ │ CDK App 1 │ ││ │ CDK App 2 │ │ +│ └───────────────┘ ││ └──────────────┘ ││ └──────────────┘ │ +│ ││ ││ │ +│ ││ ││ │ +│ ┌───────────────────────────┐ ││ ┌────────────┐ ││ │ +│ │IAM Role for CFN execution │ ││┌────│ S3 Asset │ ││ │ +│ │ IAM Role for lookup │ │││ └────────────┘ ││ │ +│ │ IAM Role for deployment │ │││ ││ │ +│ └───────────────────────────┘ │││ ││ ┌─────────────┐ │ +│ │││ ┌──────────┼┼─────│ S3 Asset │ │ +│ │││ │ ││ └─────────────┘ │ +│ ┌───────────────────────────────┐ │││ │ ││ │ +│ │ IAM Role for File Publishing │ │││ │ ││ │ +│ │ IAM Role for Image Publishing │ │││ │ ││ │ +│ └───────────────────────────────┘ │││ │ ││ │ +│ │││ │ ││ │ +│ ┌─────────────────────────────┐ │││ │ ││ │ +│ │S3 Bucket for Staging Assets │ │││ │ ││ │ +│ │ KMS Key encryption │◀─┼┼┴────────────┘ ││ ┌────────────┐ │ +│ └─────────────────────────────┘ ││ ┌──────────┼┼───── │ ECR Asset │ │ +│ ││ │ ││ └────────────┘ │ +│ ││ │ ││ │ +│┌─────────────────────────────────┐││ │ ││ │ +││ECR Repository for Staging Assets◀┼┼─────────────┘ ││ │ +│└─────────────────────────────────┘││ ││ │ +│ ││ ││ │ +│ ││ ││ │ +│ ││ ││ │ +│ ││ ││ │ +│ ││ ││ │ +│ ││ ││ │ +└───────────────────────────────────┘└────────────────────────┘└────────────────────────┘ ``` Your CDK Application utilizes these resources when deploying. For example, if you have a file asset, -it gets uploaded to the `StagingBucket` using the `FilePublishingRole` when you run `cdk deploy`. +it gets uploaded to the S3 Staging Bucket using the File Publishing Role when you run `cdk deploy`. This library introduces an alternate model to bootstrapping, by splitting out essential CloudFormation IAM roles and staging resources. There will still be a Bootstrap Stack, but this will only contain IAM roles necessary for @@ -49,26 +83,50 @@ The Staging Stack will contain, on a per-need basis, - An ECR Repository _per_ image (and its revisions). - IAM roles with access to the Bucket and Repositories. -```mermaid -graph TD - A(Bootstrap Stack) --- B(CloudFormationExecutionRole
DeploymentActionRole
LookupRole) - C(CDK App with File Asset) --- D(Staging Stack A) - C --- I(Stack A) - I --- J(File Asset) - F(CDK App with File/Image Asset) --- G(Staging Stack B) - F --- K(Stack B) - K --- L(File Asset
Image Asset) - G --- H(FilePublishingRole
ImagePublishingRole
StagingBucket
ContainerAssetsRepository
FileAssetsBucketEncryptionKey) - D --- E(FilePublishingRole
StagingBucket
FileAssetsBucketEncryptionKey) - M(CDK App with no Assets) --- N(Stack C) - N --- O(
) +```text +┌─────────────────────────────┐┌───────────────────────────────────────┐┌───────────────────────────────────────┐ +│ ││ ││ │ +│ ┌───────────────┐ ││ ┌──────────────┐ ││ ┌──────────────┐ │ +│ │Bootstrap Stack│ ││ │ CDK App 1 │ ││ │ CDK App 2 │ │ +│ └───────────────┘ ││ └──────────────┘ ││ └──────────────┘ │ +│ ││┌──────────────────┐ ││┌──────────────────┐ │ +│ │││ ┌──────────────┐ │ │││ ┌──────────────┐ │ │ +│ │││ │Staging Stack │ │ │││ │Staging Stack │ │ │ +│ │││ └──────────────┘ │ │││ └──────────────┘ │ │ +│ │││ │ │││ │ │ +│ │││ │ │││ │ │ +│ │││┌────────────────┐│ ┌────────────┐│││┌────────────────┐│ ┌────────────┐│ +│ ││││ IAM Role for ││ ┌───│ S3 Asset │││││ IAM Role for ││ ┌───│ S3 Asset ││ +│ ││││File Publishing ││ │ └────────────┘││││File Publishing ││ │ └────────────┘│ +│ │││└────────────────┘│ │ ││││ IAM Role for ││ │ │ +│ │││ │ │ ││││Image Publishing││ │ │ +│┌───────────────────────────┐│││ │ │ │││└────────────────┘│ │ │ +││IAM Role for CFN execution ││││ │ │ │││ │ │ │ +││ IAM Role for lookup ││││ │ │ │││ │ │ │ +││ IAM Role for deployment ││││┌────────────────┐│ │ │││┌────────────────┐│ │ │ +│└───────────────────────────┘││││ S3 Bucket for ││ │ ││││ S3 Bucket for ││ │ │ +│ ││││ Staging Assets │◀─┘ ││││ Staging Assets │◀─┘ │ +│ │││└────────────────┘│ │││└────────────────┘│ ┌───────────┐│ +│ │││ │ │││ │ ┌───│ ECR Asset ││ +│ │││ │ │││┌────────────────┐│ │ └───────────┘│ +│ │││ │ ││││ ECR Repository ││ │ │ +│ │││ │ ││││ for Staging │◀──┘ │ +│ │││ │ ││││ Assets ││ │ +│ │││ │ │││└────────────────┘│ │ +│ │││ │ │││ │ │ +│ │││ │ │││ │ │ +│ │││ │ │││ │ │ +│ │││ │ │││ │ │ +│ │││ │ │││ │ │ +│ ││└──────────────────┘ ││└──────────────────┘ │ +└─────────────────────────────┘└───────────────────────────────────────┘└───────────────────────────────────────┘ ``` This allows staging resources to be created when needed next to the CDK App. It has the following benefits: -- Bootstrapping will be faster since the heavy resource of a KMS key is no longer involved. -- Because roles are a global resource, every account now only needs to be bootstrapped once. +- Resources between separate CDK Apps are separated so they can be cleaned up and lifecycle +controlled individually. - Users have a familiar way to customize staging resources in the CDK Application. > As this library is `experimental`, the accompanying Bootstrap Stack is not yet implemented. To use this @@ -106,24 +164,50 @@ This has the benefit of providing a custom Staging Stack that can be created in is deployed to. ```ts -import { IStagingResources } from '@aws-cdk/app-staging-synthesizer'; +import { FileStagingLocation, ImageStagingLocation } from '@aws-cdk/app-staging-synthesizer'; + +interface CustomStagingStackProps extends StackProps {} class CustomStagingStack extends Stack implements IStagingResources { - // ... + public constructor(scope: Construct, id: string, props: CustomStagingStackProps) { + super(scope, id, props); + } + + public addFile(asset: FileAssetSource): FileStagingLocation { + return { + bucketName: 'myBucket', + assumeRoleArn: 'myArn', + dependencyStack: this, + }; + } + + public addDockerImage(asset: DockerImageAssetSource): ImageStagingLocation { + return { + repoName: 'myRepo', + assumeRoleArn: 'myArn', + dependencyStack: this, + }; + } +} +``` + +```ts fixture=with-custom-staging +import { IStagingResources } from '@aws-cdk/app-staging-synthesizer'; + +class CustomFactory implements IStagingResourcesFactory { + public obtainStagingResources(stack: Stack, context: ObtainStagingResourcesContext): IStagingResources { + const app = App.of(stack); + if (!App.isApp(app)) { + throw new Error(`Stack ${stack.stackName} must be part of an App`); + } + + return new CustomStagingStack(app, `CustomStagingStack-${appId}-${context.environmentString}`, { appId: 'my-app-id' }), + } } const app = new App({ defaultSynthesizer: AppStagingSynthesizer.customFactory({ - factory: { - obtainStagingResources(stack, context) { - const app = App.of(stack); - if (!App.isApp(app)) { - throw new Error(`Stack ${stack.stackName} must be part of an App`); - } - - return new CustomStagingStack(app, `CustomStagingStack-${appId}-${context.environmentString}`, { appId: 'my-app-id' }), - }, - }, + factory: new CustomFactory(), oncePerEnv: true, // by default }), }); @@ -137,10 +221,6 @@ Make sure that the custom stack you provide implements `IStagingResources`. ```ts import { IStagingResources } from '@aws-cdk/app-staging-synthesizer'; -class CustomStagingStack extends Stack implements IStagingResources { - // ... -} - const resourceApp = new App(); const resources = new CustomStagingStack(resourceApp, 'CustomStagingStack'); @@ -188,7 +268,7 @@ const app = new App({ appId: 'my-app-id', deploymentRoles: { cloudFormationExecutionRole: BoostrapRole.fromRoleArn('arn:aws:iam::123456789012:role/Execute'), - deploymentActionRole: BootstrapRole.fromRoleArn('arn:aws:iam::123456789012:role/Deploy'), + deploymentRole: BootstrapRole.fromRoleArn('arn:aws:iam::123456789012:role/Deploy'), lookupRole: BoostrapRole.fromRoleArn('arn:aws:iam::123456789012:role/Lookup'), }, }), @@ -210,7 +290,9 @@ const app = new App({ }); ``` -You can also specify an existing IAM role for the `fileAssetPublishingRole` or `imageAssetPublishingRole`: +The default staging stack will create roles to publish to the S3 bucket and ECR repositories, +assumable by the deployment role. You can also specify an existing IAM role for the +`fileAssetPublishingRole` or `imageAssetPublishingRole`: ```ts const app = new App({ @@ -245,7 +327,10 @@ new lambda.Function(stack, 'lambda', { This means that the asset will go into the S3 Bucket with the prefix `handoff/`. It will also be subject to the lifecycle rule set on ephemeral assets. By default, we store ephemeral assets for -30 days. +30 days, but you can change this number by specifying `handoffFileAssetLifetime`. The number you +specify here is how long you will be able to roll back to a previous version of an application +just by doing a CloudFormation deployment with the old template, without rebuilding and +republishing assets. ```ts const app = new App({ @@ -276,11 +361,9 @@ const app = new App({ ## Extending the Default Staging Stack -The exposed API of this synthesizer is purposefully opinionated. Should the feature set be -too restrictive, `AppStagingSynthesizer` is built to be subclassable. For example, say we want -to add a feature where we _do not_ apply lifecycle rules to any assets that have the prefix -"`permanent`". While that doesn't exist as a property of `AppStagingSynthesizer`, we can easily -extend it to include what we want: +If you want to customize some behavior that is not configurable via properties, +you can implement your own class that implements `IStagingResources`. To get a head start, +you can subclass `DefaultStagingStack`. ```ts interface CustomStagingStackOptions extends DefaultStagingStackOptions {} @@ -288,4 +371,4 @@ interface CustomStagingStackOptions extends DefaultStagingStackOptions {} class CustomStagingStack extends DefaultStagingStack { } -``` \ No newline at end of file +``` diff --git a/packages/@aws-cdk/app-staging-synthesizer/rosetta/custom-staging.ts-fixture b/packages/@aws-cdk/app-staging-synthesizer/rosetta/custom-staging.ts-fixture new file mode 100644 index 0000000000000..250cd7163a0fc --- /dev/null +++ b/packages/@aws-cdk/app-staging-synthesizer/rosetta/custom-staging.ts-fixture @@ -0,0 +1,35 @@ +// Fixture with packages imported, but nothing else +import { App, Stack, StackProps } from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import { AppStagingSynthesizer, BootstrapRole, FileStagingLocation, ImageStagingLocation } from '@aws-cdk/app-staging-synthesizer'; + +interface CustomStagingStackProps extends StackProps {} + +class CustomStagingStack extends Stack implements IStagingResources { + public constructor(scope: Construct, id: string, props: CustomStagingStackProps) { + super(scope, id, props); + } + + public addFile(asset: FileAssetSource): FileStagingLocation { + return { + bucketName: 'myBucket', + assumeRoleArn: 'myArn', + dependencyStack: this, + }; + } + + public addDockerImage(asset: DockerImageAssetSource): ImageStagingLocation { + return { + repoName: 'myRepo', + assumeRoleArn: 'myArn', + dependencyStack: this, + }; + } +} + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + /// here + } +} diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/default-staging-stack.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/default-staging-stack.test.ts index 641bda0c9e387..d711195d4ca25 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/default-staging-stack.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/default-staging-stack.test.ts @@ -1,5 +1,5 @@ -import { DefaultStagingStack } from '../lib'; import { App } from 'aws-cdk-lib'; +import { DefaultStagingStack } from '../lib'; describe('default staging stack', () => { describe('appId fails', () => { diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.custom-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer/test/integ.custom-staging-stack.ts deleted file mode 100644 index 1eb64d81a9726..0000000000000 --- a/packages/@aws-cdk/app-staging-synthesizer/test/integ.custom-staging-stack.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { DefaultStagingStack, DefaultStagingStackOptions, IStagingResourcesFactory } from '../lib'; - -interface PermanentAssetsStagingStackOptions extends DefaultStagingStackOptions {} - -class PermanentAssetsStagingStack extends DefaultStagingStack { - public static factory(options: PermanentAssetsStagingStackOptions): IStagingResourcesFactory { - const factory = DefaultStagingStack.factory(options); - return { - obtainStagingResources(stack, context) { - - }, - } - } -} \ No newline at end of file From 14379152a3dbd97bb9b21740782bcbd2266b257d Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Tue, 16 May 2023 13:28:16 -0400 Subject: [PATCH 110/120] pr feedback, rosetta passes, tests pass --- .../app-staging-synthesizer/README.md | 98 +++++++++++-------- .../lib/app-staging-synthesizer.ts | 10 +- .../lib/bootstrap-roles.ts | 27 +++++ .../rosetta/default.ts-fixture | 15 ++- ...fixture => with-custom-staging.ts-fixture} | 13 ++- .../test/bootstrap-roles.test.ts | 12 +-- 6 files changed, 119 insertions(+), 56 deletions(-) rename packages/@aws-cdk/app-staging-synthesizer/rosetta/{custom-staging.ts-fixture => with-custom-staging.ts-fixture} (70%) diff --git a/packages/@aws-cdk/app-staging-synthesizer/README.md b/packages/@aws-cdk/app-staging-synthesizer/README.md index 7862a8860a65b..87dd076f629af 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/README.md +++ b/packages/@aws-cdk/app-staging-synthesizer/README.md @@ -26,6 +26,26 @@ are as follows: - the `AppStagingSynthesizer`, a new CDK synthesizer that will synthesize CDK applications with the staging resources provided. +> Currently this module does not support CDK Pipelines. You must deploy CDK Apps using this +> synthesizer via `cdk deploy`. + +To get started, update your CDK App with a new `defaultStackSynthesizer`: + +```ts +const app = new App({ + defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ + appId: 'my-app-id', + }), +}); +``` + +This will introduce a `DefaultStagingStack` in your CDK App and staging assets of your App +will live in the resources from that stack rather than the CDK Bootstrap stack. + +If you are migrating from a different version of synthesis your updated CDK App will target +the resources in the `DefaultStagingStack` and no longer be tied to the bootstrapped resources +in your account. + ## Bootstrap Model Our current bootstrap model looks like this, when you run `cdk bootstrap aws:///` : @@ -147,7 +167,7 @@ its staging resources. To use this kind of synthesizer, use `AppStagingSynthesiz ```ts const app = new App({ - defaultSynthesizer: AppStagingSynthesizer.defaultResources({ + defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: 'my-app-id', }), }); @@ -160,12 +180,10 @@ with the app. ### Using a Custom Staging Stack per Environment Use `AppStagingSynthesizer.customFactory()` to supply a custom staging stack on a per-environment basis. -This has the benefit of providing a custom Staging Stack that can be created in every environment the CDK App -is deployed to. +This has the benefit of providing a custom Staging Stack that can be created in every environment the +CDK App is deployed to. ```ts -import { FileStagingLocation, ImageStagingLocation } from '@aws-cdk/app-staging-synthesizer'; - interface CustomStagingStackProps extends StackProps {} class CustomStagingStack extends Stack implements IStagingResources { @@ -192,21 +210,16 @@ class CustomStagingStack extends Stack implements IStagingResources { ``` ```ts fixture=with-custom-staging -import { IStagingResources } from '@aws-cdk/app-staging-synthesizer'; - class CustomFactory implements IStagingResourcesFactory { - public obtainStagingResources(stack: Stack, context: ObtainStagingResourcesContext): IStagingResources { - const app = App.of(stack); - if (!App.isApp(app)) { - throw new Error(`Stack ${stack.stackName} must be part of an App`); - } + public obtainStagingResources(stack: Stack, context: ObtainStagingResourcesContext) { + const myApp = App.of(stack); - return new CustomStagingStack(app, `CustomStagingStack-${appId}-${context.environmentString}`, { appId: 'my-app-id' }), + return new CustomStagingStack(myApp!, `CustomStagingStack-${context.environmentString}`, {}); } } const app = new App({ - defaultSynthesizer: AppStagingSynthesizer.customFactory({ + defaultStackSynthesizer: AppStagingSynthesizer.customFactory({ factory: new CustomFactory(), oncePerEnv: true, // by default }), @@ -218,14 +231,12 @@ const app = new App({ Use `AppStagingSynthesizer.customResources()` to supply an existing stack as the Staging Stack. Make sure that the custom stack you provide implements `IStagingResources`. -```ts -import { IStagingResources } from '@aws-cdk/app-staging-synthesizer'; - +```ts fixture=with-custom-staging const resourceApp = new App(); -const resources = new CustomStagingStack(resourceApp, 'CustomStagingStack'); +const resources = new CustomStagingStack(resourceApp, 'CustomStagingStack', {}); const app = new App({ - defaultSynthesizer: AppStagingSynthesizer.customResources({ + defaultStackSynthesizer: AppStagingSynthesizer.customResources({ resources, }), }); @@ -239,11 +250,8 @@ source code. As part of the `DefaultStagingStack`, an S3 bucket and IAM role wil used to upload the asset to S3. ```ts -import * as path from 'path'; -import * as lambda from 'aws-cdk-lib/aws-lambda'; - const app = new App({ - defaultSynthesizer: new AppStagingSynthesizer.defaultResources({ appId: 'my-app-id' }), + defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: 'my-app-id' }), }); const stack = new Stack(app, 'my-stack'); @@ -264,28 +272,29 @@ if all you need is to supply custom roles (and not change anything else in the ` ```ts const app = new App({ - defaultSynthesizer: new AppStagingSynthesizer.defaultResources({ + defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: 'my-app-id', - deploymentRoles: { - cloudFormationExecutionRole: BoostrapRole.fromRoleArn('arn:aws:iam::123456789012:role/Execute'), + deploymentRoles: DeploymentIdentities.specifyRoles({ + cloudFormationExecutionRole: BootstrapRole.fromRoleArn('arn:aws:iam::123456789012:role/Execute'), deploymentRole: BootstrapRole.fromRoleArn('arn:aws:iam::123456789012:role/Deploy'), - lookupRole: BoostrapRole.fromRoleArn('arn:aws:iam::123456789012:role/Lookup'), - }, + lookupRole: BootstrapRole.fromRoleArn('arn:aws:iam::123456789012:role/Lookup'), + }), }), }); ``` -Or, you can ask to use the CLI credentials that exist at deploy-time: +Or, you can ask to use the CLI credentials that exist at deploy-time. +These credentials must have the ability to perform CloudFormation calls, +lookup resources in your account, and perform CloudFormation deployment. +For a full list of what is necessary, see `LookupRole`, `DeploymentActionRole`, +and `CloudFormationExecutionRole` in the +[bootstrap template](https://github.com/aws/aws-cdk/blob/main/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml). ```ts const app = new App({ defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: 'my-app-id', - deploymentRoles: { - cloudFormationExecutionRole: BootstrapRole.cliCredentials(), - lookupRole: BootstrapRole.cliCredentials(), - deploymentRole: BootstrapRole.cliCredentials(), - }, + deploymentRoles: DeploymentIdentities.cliCredentials(), }), }); ``` @@ -312,19 +321,30 @@ you can tag them as such and they will be marked with an `handoff/` prefix when This allows configuration of a lifecycle rule specifically for ephemeral assets. A good example is a Lambda Function asset. The asset is only useful in the S3 Bucket at deploy -time, because the source code gets copied into Lambda itself. So we can mark Lambda assets -as ephemeral: +time, because the source code gets copied into Lambda itself. So Lambda assets are by default +marked as ephemeral: ```ts +declare const stack: Stack; new lambda.Function(stack, 'lambda', { - code: lambda.AssetCode.fromAsset(path.join(__dirname, 'assets'), { - ephemeral: true, - }), + code: lambda.AssetCode.fromAsset(path.join(__dirname, 'assets')), // lambda marks ephemeral = true handler: 'index.handler', runtime: lambda.Runtime.PYTHON_3_9, }); ``` +Or, if you want to create your own ephemeral asset: + +```ts +import { Asset } from 'aws-cdk-lib/aws-s3-assets'; + +declare const stack: Stack; +const asset = new Asset(stack, 'ephemeral-asset', { + ephemeral: true, + path: path.join(__dirname, './ephemeral-asset'), +}); +``` + This means that the asset will go into the S3 Bucket with the prefix `handoff/`. It will also be subject to the lifecycle rule set on ephemeral assets. By default, we store ephemeral assets for 30 days, but you can change this number by specifying `handoffFileAssetLifetime`. The number you diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts index b145e2d6ebeeb..7f03c37cb6e2a 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts @@ -13,7 +13,7 @@ import { Token, } from 'aws-cdk-lib'; import { StringSpecializer, translateCfnTokenToAssetToken } from 'aws-cdk-lib/core/lib/helpers-internal'; -import { BootstrapRole, BootstrapRoles } from './bootstrap-roles'; +import { BootstrapRole, BootstrapRoles, DeploymentIdentities } from './bootstrap-roles'; import { DefaultStagingStack, DefaultStagingStackOptions } from './default-staging-stack'; import { PerEnvironmentStagingFactory as PerEnvironmentStagingFactory } from './per-env-staging-factory'; import { AppScopedGlobal } from './private/app-global'; @@ -37,7 +37,7 @@ export interface AppStagingSynthesizerOptions { * * @default - The standard bootstrapped CDK roles */ - readonly deploymentRoles?: BootstrapRoles; + readonly deploymentRoles?: DeploymentIdentities; /** * Qualifier to disambiguate multiple bootstrapped environments in the same account @@ -172,11 +172,11 @@ export class AppStagingSynthesizer extends StackSynthesizer implements IReusable super(); this.roles = { - deploymentRole: props.deploymentRoles?.deploymentRole ?? + deploymentRole: props.deploymentRoles?.roles.deploymentRole ?? BootstrapRole.fromRoleArn(AppStagingSynthesizer.DEFAULT_DEPLOY_ROLE_ARN), - cloudFormationExecutionRole: props.deploymentRoles?.cloudFormationExecutionRole ?? + cloudFormationExecutionRole: props.deploymentRoles?.roles.cloudFormationExecutionRole ?? BootstrapRole.fromRoleArn(AppStagingSynthesizer.DEFAULT_CLOUDFORMATION_ROLE_ARN), - lookupRole: this.props.deploymentRoles?.lookupRole ?? + lookupRole: this.props.deploymentRoles?.roles.lookupRole ?? BootstrapRole.fromRoleArn(AppStagingSynthesizer.DEFAULT_LOOKUP_ROLE_ARN), }; } diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts b/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts index 53cf226f3b26b..5968cc938464d 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts @@ -53,6 +53,33 @@ export class BootstrapRole { } } +/** + * Deployment identities are the class of roles to be assumed by the CDK + * when deploying the App. + */ +export class DeploymentIdentities { + /** + * Use CLI credentials for all deployment identities. + */ + public static cliCredentials(): DeploymentIdentities { + return new DeploymentIdentities({ + cloudFormationExecutionRole: BootstrapRole.cliCredentials(), + deploymentRole: BootstrapRole.cliCredentials(), + lookupRole: BootstrapRole.cliCredentials(), + }); + } + + /** + * Specify your own roles for all deployment identities. These roles + * must already exist. + */ + public static specifyRoles(roles: BootstrapRoles): DeploymentIdentities { + return new DeploymentIdentities(roles); + } + + private constructor(public readonly roles: BootstrapRoles) {} +} + /** * Roles that are bootstrapped to your account. */ diff --git a/packages/@aws-cdk/app-staging-synthesizer/rosetta/default.ts-fixture b/packages/@aws-cdk/app-staging-synthesizer/rosetta/default.ts-fixture index e86db62607ab6..4ddbcf2798836 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/rosetta/default.ts-fixture +++ b/packages/@aws-cdk/app-staging-synthesizer/rosetta/default.ts-fixture @@ -1,7 +1,18 @@ // Fixture with packages imported, but nothing else -import { App, Stack } from 'aws-cdk-lib'; +import { App, Stack, StackProps, Duration, DockerImageAssetSource, FileAssetSource } from 'aws-cdk-lib'; import { Construct } from 'constructs'; -import { AppStagingSynthesizer, BootstrapRole } from '@aws-cdk/app-staging-synthesizer'; +import * as lambda from 'aws-cdk-lib/aws-lambda'; +import { + AppStagingSynthesizer, + BootstrapRole, + DefaultStagingStack, + DefaultStagingStackOptions, + IStagingResources, + FileStagingLocation, + ImageStagingLocation, + DeploymentIdentities, +} from '@aws-cdk/app-staging-synthesizer'; +import * as path from 'path'; class Fixture extends Stack { constructor(scope: Construct, id: string) { diff --git a/packages/@aws-cdk/app-staging-synthesizer/rosetta/custom-staging.ts-fixture b/packages/@aws-cdk/app-staging-synthesizer/rosetta/with-custom-staging.ts-fixture similarity index 70% rename from packages/@aws-cdk/app-staging-synthesizer/rosetta/custom-staging.ts-fixture rename to packages/@aws-cdk/app-staging-synthesizer/rosetta/with-custom-staging.ts-fixture index 250cd7163a0fc..b819563c2bf6f 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/rosetta/custom-staging.ts-fixture +++ b/packages/@aws-cdk/app-staging-synthesizer/rosetta/with-custom-staging.ts-fixture @@ -1,7 +1,16 @@ // Fixture with packages imported, but nothing else -import { App, Stack, StackProps } from 'aws-cdk-lib'; +import { App, Stack, StackProps, DockerImageAssetSource, FileAssetSource } from 'aws-cdk-lib'; import { Construct } from 'constructs'; -import { AppStagingSynthesizer, BootstrapRole, FileStagingLocation, ImageStagingLocation } from '@aws-cdk/app-staging-synthesizer'; +import { + AppStagingSynthesizer, + DefaultStagingStack, + BootstrapRole, + FileStagingLocation, + ImageStagingLocation, + ObtainStagingResourcesContext, + IStagingResourcesFactory, + IStagingResources, +} from '@aws-cdk/app-staging-synthesizer'; interface CustomStagingStackProps extends StackProps {} diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts b/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts index 2f18980205d9d..6396038692eac 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts @@ -2,7 +2,7 @@ import * as fs from 'fs'; import { App, Stack, CfnResource } from 'aws-cdk-lib'; import * as cxschema from 'aws-cdk-lib/cloud-assembly-schema'; import { APP_ID, isAssetManifest } from './util'; -import { AppStagingSynthesizer, BootstrapRole } from '../lib'; +import { AppStagingSynthesizer, BootstrapRole, DeploymentIdentities } from '../lib'; const CLOUDFORMATION_EXECUTION_ROLE = 'cloudformation-execution-role'; const DEPLOY_ACTION_ROLE = 'deploy-action-role'; @@ -42,11 +42,11 @@ describe('Boostrap Roles', () => { const app = new App({ defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: APP_ID, - deploymentRoles: { + deploymentRoles: DeploymentIdentities.specifyRoles({ cloudFormationExecutionRole: BootstrapRole.fromRoleArn(CLOUDFORMATION_EXECUTION_ROLE), lookupRole: BootstrapRole.fromRoleArn(LOOKUP_ROLE), deploymentRole: BootstrapRole.fromRoleArn(DEPLOY_ACTION_ROLE), - }, + }), }), }); const stack = new Stack(app, 'Stack', { @@ -138,11 +138,7 @@ describe('Boostrap Roles', () => { const app = new App({ defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: APP_ID, - deploymentRoles: { - cloudFormationExecutionRole: BootstrapRole.cliCredentials(), - lookupRole: BootstrapRole.cliCredentials(), - deploymentRole: BootstrapRole.cliCredentials(), - }, + deploymentRoles: DeploymentIdentities.cliCredentials(), }), }); const stack = new Stack(app, 'Stack', { From 2ea6d0a1d1354069ada75d4944d32804a1f38b19 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Tue, 16 May 2023 14:38:49 -0400 Subject: [PATCH 111/120] rename app-staging-synthesizer into app-staging-synthesizer-alpha --- .../.eslintrc.js | 0 .../.gitignore | 0 .../.npmignore | 0 .../LICENSE | 0 .../NOTICE | 0 .../README.md | 2 +- .../adr/resource-names.md | 0 .../jest.config.js | 0 .../lib/app-staging-synthesizer.ts | 0 .../lib/bootstrap-roles.ts | 5 ++++- .../lib/default-staging-stack.ts | 0 .../lib/index.ts | 0 .../lib/per-env-staging-factory.ts | 0 .../lib/private/app-global.ts | 0 .../lib/private/no-tokens.ts | 0 .../lib/staging-stack.ts | 0 .../package.json | 14 +++++++------- .../rosetta/default.ts-fixture | 2 +- .../rosetta/with-custom-staging.ts-fixture | 2 +- .../test/app-staging-synthesizer.test.ts | 0 .../test/assets/Dockerfile | 0 .../test/assets/index.py | 0 .../test/bootstrap-roles.test.ts | 0 .../test/default-staging-stack.test.ts | 0 .../test/evaluate-cfn.ts | 0 ...-default-resources-ACCOUNT-REGION.template.json | 0 .../Dockerfile | 0 .../index.py | 0 .../Dockerfile | 0 .../index.py | 0 .../cdk.out | 0 .../integ.json | 0 ...estsDefaultTestDeployAssert44C8D370.assets.json | 0 ...tsDefaultTestDeployAssert44C8D370.template.json | 0 .../manifest.json | 0 .../synthesize-default-resources.assets.json | 0 .../synthesize-default-resources.template.json | 0 .../tree.json | 0 .../test/integ.synth-default-resources.ts | 0 .../test/per-env-staging-factory.test.ts | 0 .../test/util.ts | 0 41 files changed, 14 insertions(+), 11 deletions(-) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/.eslintrc.js (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/.gitignore (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/.npmignore (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/LICENSE (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/NOTICE (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/README.md (99%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/adr/resource-names.md (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/jest.config.js (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/lib/app-staging-synthesizer.ts (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/lib/bootstrap-roles.ts (95%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/lib/default-staging-stack.ts (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/lib/index.ts (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/lib/per-env-staging-factory.ts (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/lib/private/app-global.ts (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/lib/private/no-tokens.ts (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/lib/staging-stack.ts (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/package.json (86%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/rosetta/default.ts-fixture (92%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/rosetta/with-custom-staging.ts-fixture (95%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/app-staging-synthesizer.test.ts (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/assets/Dockerfile (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/assets/index.py (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/bootstrap-roles.test.ts (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/default-staging-stack.test.ts (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/evaluate-cfn.ts (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/integ.synth-default-resources.js.snapshot/StagingStack-default-resources-ACCOUNT-REGION.template.json (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/integ.synth-default-resources.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/Dockerfile (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/integ.synth-default-resources.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/index.py (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/integ.synth-default-resources.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/Dockerfile (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/integ.synth-default-resources.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/index.py (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/integ.synth-default-resources.js.snapshot/cdk.out (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/integ.synth-default-resources.js.snapshot/integ.json (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/integ.synth-default-resources.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/integ.synth-default-resources.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.template.json (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/integ.synth-default-resources.js.snapshot/manifest.json (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/integ.synth-default-resources.js.snapshot/synthesize-default-resources.assets.json (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/integ.synth-default-resources.js.snapshot/synthesize-default-resources.template.json (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/integ.synth-default-resources.js.snapshot/tree.json (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/integ.synth-default-resources.ts (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/per-env-staging-factory.test.ts (100%) rename packages/@aws-cdk/{app-staging-synthesizer => app-staging-synthesizer-alpha}/test/util.ts (100%) diff --git a/packages/@aws-cdk/app-staging-synthesizer/.eslintrc.js b/packages/@aws-cdk/app-staging-synthesizer-alpha/.eslintrc.js similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/.eslintrc.js rename to packages/@aws-cdk/app-staging-synthesizer-alpha/.eslintrc.js diff --git a/packages/@aws-cdk/app-staging-synthesizer/.gitignore b/packages/@aws-cdk/app-staging-synthesizer-alpha/.gitignore similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/.gitignore rename to packages/@aws-cdk/app-staging-synthesizer-alpha/.gitignore diff --git a/packages/@aws-cdk/app-staging-synthesizer/.npmignore b/packages/@aws-cdk/app-staging-synthesizer-alpha/.npmignore similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/.npmignore rename to packages/@aws-cdk/app-staging-synthesizer-alpha/.npmignore diff --git a/packages/@aws-cdk/app-staging-synthesizer/LICENSE b/packages/@aws-cdk/app-staging-synthesizer-alpha/LICENSE similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/LICENSE rename to packages/@aws-cdk/app-staging-synthesizer-alpha/LICENSE diff --git a/packages/@aws-cdk/app-staging-synthesizer/NOTICE b/packages/@aws-cdk/app-staging-synthesizer-alpha/NOTICE similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/NOTICE rename to packages/@aws-cdk/app-staging-synthesizer-alpha/NOTICE diff --git a/packages/@aws-cdk/app-staging-synthesizer/README.md b/packages/@aws-cdk/app-staging-synthesizer-alpha/README.md similarity index 99% rename from packages/@aws-cdk/app-staging-synthesizer/README.md rename to packages/@aws-cdk/app-staging-synthesizer-alpha/README.md index 87dd076f629af..f695e01d17fd4 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/README.md +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/README.md @@ -1,4 +1,4 @@ -# App Scoped Staging Synthesizer +# App Staging Synthesizer --- diff --git a/packages/@aws-cdk/app-staging-synthesizer/adr/resource-names.md b/packages/@aws-cdk/app-staging-synthesizer-alpha/adr/resource-names.md similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/adr/resource-names.md rename to packages/@aws-cdk/app-staging-synthesizer-alpha/adr/resource-names.md diff --git a/packages/@aws-cdk/app-staging-synthesizer/jest.config.js b/packages/@aws-cdk/app-staging-synthesizer-alpha/jest.config.js similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/jest.config.js rename to packages/@aws-cdk/app-staging-synthesizer-alpha/jest.config.js diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/app-staging-synthesizer.ts similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/lib/app-staging-synthesizer.ts rename to packages/@aws-cdk/app-staging-synthesizer-alpha/lib/app-staging-synthesizer.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/bootstrap-roles.ts similarity index 95% rename from packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts rename to packages/@aws-cdk/app-staging-synthesizer-alpha/lib/bootstrap-roles.ts index 5968cc938464d..a5454ca51d021 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/lib/bootstrap-roles.ts +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/bootstrap-roles.ts @@ -77,7 +77,10 @@ export class DeploymentIdentities { return new DeploymentIdentities(roles); } - private constructor(public readonly roles: BootstrapRoles) {} + private constructor( + /** roles that are bootstrapped to your account. */ + public readonly roles: BootstrapRoles, + ) {} } /** diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/default-staging-stack.ts similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/lib/default-staging-stack.ts rename to packages/@aws-cdk/app-staging-synthesizer-alpha/lib/default-staging-stack.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/index.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/index.ts similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/lib/index.ts rename to packages/@aws-cdk/app-staging-synthesizer-alpha/lib/index.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/per-env-staging-factory.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/per-env-staging-factory.ts similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/lib/per-env-staging-factory.ts rename to packages/@aws-cdk/app-staging-synthesizer-alpha/lib/per-env-staging-factory.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/private/app-global.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/private/app-global.ts similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/lib/private/app-global.ts rename to packages/@aws-cdk/app-staging-synthesizer-alpha/lib/private/app-global.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/private/no-tokens.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/private/no-tokens.ts similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/lib/private/no-tokens.ts rename to packages/@aws-cdk/app-staging-synthesizer-alpha/lib/private/no-tokens.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/lib/staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/staging-stack.ts similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/lib/staging-stack.ts rename to packages/@aws-cdk/app-staging-synthesizer-alpha/lib/staging-stack.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/package.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/package.json similarity index 86% rename from packages/@aws-cdk/app-staging-synthesizer/package.json rename to packages/@aws-cdk/app-staging-synthesizer-alpha/package.json index a6262c9fd3186..7b5e5e9ef5dee 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/package.json +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/package.json @@ -1,5 +1,5 @@ { - "name": "@aws-cdk/app-staging-synthesizer", + "name": "@aws-cdk/app-staging-synthesizer-alpha", "private": true, "version": "0.0.0", "description": "Cdk synthesizer for with app-scoped staging stack", @@ -19,20 +19,20 @@ "java": { "maven": { "groupId": "software.amazon.awscdk", - "artifactId": "cdk-app-staging-synthesizer" + "artifactId": "cdk-app-staging-synthesizer-alpha" }, - "package": "software.amazon.awscdk.app.staging.synthesizer" + "package": "software.amazon.awscdk.app.staging.synthesizer.alpha" }, "python": { - "distName": "aws-cdk.app-staging-synthesizer", - "module": "aws_cdk.app_staging_synthesizer", + "distName": "aws-cdk.app-staging-synthesizer-alpha", + "module": "aws_cdk.app_staging_synthesizer_alpha", "classifiers": [ "Framework :: AWS CDK", "Framework :: AWS CDK :: 2" ] }, "dotnet": { - "namespace": "Amazon.CDK.App.Staging.Synthesizer", + "namespace": "Amazon.CDK.App.Staging.Synthesizer.Alpha", "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/main/logo/default-256-dark.png" } } @@ -40,7 +40,7 @@ "repository": { "type": "git", "url": "https://github.com/aws/aws-cdk.git", - "directory": "packages/@aws-cdk/app-staging-synthesizer" + "directory": "packages/@aws-cdk/app-staging-synthesizer-alpha" }, "scripts": { "build": "cdk-build", diff --git a/packages/@aws-cdk/app-staging-synthesizer/rosetta/default.ts-fixture b/packages/@aws-cdk/app-staging-synthesizer-alpha/rosetta/default.ts-fixture similarity index 92% rename from packages/@aws-cdk/app-staging-synthesizer/rosetta/default.ts-fixture rename to packages/@aws-cdk/app-staging-synthesizer-alpha/rosetta/default.ts-fixture index 4ddbcf2798836..150cd4c706021 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/rosetta/default.ts-fixture +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/rosetta/default.ts-fixture @@ -11,7 +11,7 @@ import { FileStagingLocation, ImageStagingLocation, DeploymentIdentities, -} from '@aws-cdk/app-staging-synthesizer'; +} from '@aws-cdk/app-staging-synthesizer-alpha'; import * as path from 'path'; class Fixture extends Stack { diff --git a/packages/@aws-cdk/app-staging-synthesizer/rosetta/with-custom-staging.ts-fixture b/packages/@aws-cdk/app-staging-synthesizer-alpha/rosetta/with-custom-staging.ts-fixture similarity index 95% rename from packages/@aws-cdk/app-staging-synthesizer/rosetta/with-custom-staging.ts-fixture rename to packages/@aws-cdk/app-staging-synthesizer-alpha/rosetta/with-custom-staging.ts-fixture index b819563c2bf6f..981f76ecbbac1 100644 --- a/packages/@aws-cdk/app-staging-synthesizer/rosetta/with-custom-staging.ts-fixture +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/rosetta/with-custom-staging.ts-fixture @@ -10,7 +10,7 @@ import { ObtainStagingResourcesContext, IStagingResourcesFactory, IStagingResources, -} from '@aws-cdk/app-staging-synthesizer'; +} from '@aws-cdk/app-staging-synthesizer-alpha'; interface CustomStagingStackProps extends StackProps {} diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/app-staging-synthesizer.test.ts similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/app-staging-synthesizer.test.ts rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/app-staging-synthesizer.test.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/assets/Dockerfile b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/assets/Dockerfile similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/assets/Dockerfile rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/assets/Dockerfile diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/assets/index.py b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/assets/index.py similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/assets/index.py rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/assets/index.py diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/bootstrap-roles.test.ts similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/bootstrap-roles.test.ts rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/bootstrap-roles.test.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/default-staging-stack.test.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/default-staging-stack.test.ts similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/default-staging-stack.test.ts rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/default-staging-stack.test.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/evaluate-cfn.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/evaluate-cfn.ts similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/evaluate-cfn.ts rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/evaluate-cfn.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/StagingStack-default-resources-ACCOUNT-REGION.template.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/StagingStack-default-resources-ACCOUNT-REGION.template.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/StagingStack-default-resources-ACCOUNT-REGION.template.json rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/StagingStack-default-resources-ACCOUNT-REGION.template.json diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/Dockerfile b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/Dockerfile similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/Dockerfile rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/Dockerfile diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/index.py b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/index.py similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/index.py rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/asset.16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622/index.py diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/Dockerfile b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/Dockerfile similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/Dockerfile rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/Dockerfile diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/index.py b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/index.py similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/index.py rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/asset.68539effc3f7ad46fff9765606c2a01b7f7965833643ab37e62799f19a37f650/index.py diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/cdk.out b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/cdk.out similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/cdk.out rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/cdk.out diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/integ.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/integ.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/integ.json rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/integ.json diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.template.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.template.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.template.json rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.template.json diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/manifest.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/manifest.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/manifest.json rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/manifest.json diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/synthesize-default-resources.assets.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/synthesize-default-resources.assets.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/synthesize-default-resources.assets.json rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/synthesize-default-resources.assets.json diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/synthesize-default-resources.template.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/synthesize-default-resources.template.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/synthesize-default-resources.template.json rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/synthesize-default-resources.template.json diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/tree.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/tree.json similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.js.snapshot/tree.json rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/tree.json diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/integ.synth-default-resources.ts rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/per-env-staging-factory.test.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/per-env-staging-factory.test.ts similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/per-env-staging-factory.test.ts rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/per-env-staging-factory.test.ts diff --git a/packages/@aws-cdk/app-staging-synthesizer/test/util.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/util.ts similarity index 100% rename from packages/@aws-cdk/app-staging-synthesizer/test/util.ts rename to packages/@aws-cdk/app-staging-synthesizer-alpha/test/util.ts From d1c873baa3ee5ef8a27a851837ba38eeffb5390d Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Tue, 16 May 2023 14:39:07 -0400 Subject: [PATCH 112/120] mark template assets as ephemeral --- .../aws-cdk-lib/core/lib/stack-synthesizers/stack-synthesizer.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/aws-cdk-lib/core/lib/stack-synthesizers/stack-synthesizer.ts b/packages/aws-cdk-lib/core/lib/stack-synthesizers/stack-synthesizer.ts index fff85006eb39e..97fb52189ce3a 100644 --- a/packages/aws-cdk-lib/core/lib/stack-synthesizers/stack-synthesizer.ts +++ b/packages/aws-cdk-lib/core/lib/stack-synthesizers/stack-synthesizer.ts @@ -292,6 +292,7 @@ function stackTemplateFileAsset(stack: Stack, session: ISynthesisSession): FileA fileName: stack.templateFile, packaging: FileAssetPackaging.FILE, sourceHash, + ephemeral: true, }; } From c91fc26f1e0c25290c63f8aab4e77098ca58d79d Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Tue, 16 May 2023 14:59:38 -0400 Subject: [PATCH 113/120] fix merge --- packages/@aws-cdk/app-staging-synthesizer-alpha/README.md | 2 +- .../core/lib/helpers-internal/string-specializer.ts | 4 ++-- packages/aws-cdk-lib/core/lib/stack-synthesizers/_shared.ts | 1 - .../core/lib/stack-synthesizers/asset-manifest-builder.ts | 2 +- .../lib/stack-synthesizers/cli-credentials-synthesizer.ts | 3 ++- .../core/lib/stack-synthesizers/default-synthesizer.ts | 4 ++-- .../core/lib/stack-synthesizers/stack-synthesizer.ts | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/README.md b/packages/@aws-cdk/app-staging-synthesizer-alpha/README.md index f695e01d17fd4..0df26d89cceca 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/README.md +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/README.md @@ -34,7 +34,7 @@ To get started, update your CDK App with a new `defaultStackSynthesizer`: ```ts const app = new App({ defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ - appId: 'my-app-id', + appId: 'my-app-id', // put a unique id here }), }); ``` diff --git a/packages/aws-cdk-lib/core/lib/helpers-internal/string-specializer.ts b/packages/aws-cdk-lib/core/lib/helpers-internal/string-specializer.ts index 4e0abd790d0a0..052cd2dafcd8a 100644 --- a/packages/aws-cdk-lib/core/lib/helpers-internal/string-specializer.ts +++ b/packages/aws-cdk-lib/core/lib/helpers-internal/string-specializer.ts @@ -1,7 +1,7 @@ -import { Stack } from '../stack'; import * as cxapi from '../../../cx-api'; -import { Token } from '../token'; import { Aws } from '../cfn-pseudo'; +import { Stack } from '../stack'; +import { Token } from '../token'; /** * A "replace-all" function that doesn't require us escaping a literal string to a regex diff --git a/packages/aws-cdk-lib/core/lib/stack-synthesizers/_shared.ts b/packages/aws-cdk-lib/core/lib/stack-synthesizers/_shared.ts index 3438e83a98abe..1017f172a850e 100644 --- a/packages/aws-cdk-lib/core/lib/stack-synthesizers/_shared.ts +++ b/packages/aws-cdk-lib/core/lib/stack-synthesizers/_shared.ts @@ -2,7 +2,6 @@ import * as crypto from 'crypto'; import { Node, IConstruct } from 'constructs'; import { ISynthesisSession } from './types'; import * as cxschema from '../../../cloud-assembly-schema'; -import * as cxapi from '../../../cx-api'; import { Stack } from '../stack'; /** diff --git a/packages/aws-cdk-lib/core/lib/stack-synthesizers/asset-manifest-builder.ts b/packages/aws-cdk-lib/core/lib/stack-synthesizers/asset-manifest-builder.ts index 564bad28d82db..4ad800e23ba88 100644 --- a/packages/aws-cdk-lib/core/lib/stack-synthesizers/asset-manifest-builder.ts +++ b/packages/aws-cdk-lib/core/lib/stack-synthesizers/asset-manifest-builder.ts @@ -1,9 +1,9 @@ import * as fs from 'fs'; import * as path from 'path'; -import { resolvedOr } from './_shared'; import { ISynthesisSession } from './types'; import * as cxschema from '../../../cloud-assembly-schema'; import { FileAssetSource, FileAssetPackaging, DockerImageAssetSource } from '../assets'; +import { resolvedOr } from '../helpers-internal/string-specializer'; import { Stack } from '../stack'; /** diff --git a/packages/aws-cdk-lib/core/lib/stack-synthesizers/cli-credentials-synthesizer.ts b/packages/aws-cdk-lib/core/lib/stack-synthesizers/cli-credentials-synthesizer.ts index d56604a35f21c..982530c851296 100644 --- a/packages/aws-cdk-lib/core/lib/stack-synthesizers/cli-credentials-synthesizer.ts +++ b/packages/aws-cdk-lib/core/lib/stack-synthesizers/cli-credentials-synthesizer.ts @@ -1,10 +1,11 @@ -import { assertBound, StringSpecializer } from './_shared'; +import { assertBound } from './_shared'; import { AssetManifestBuilder } from './asset-manifest-builder'; import { BOOTSTRAP_QUALIFIER_CONTEXT, DefaultStackSynthesizer } from './default-synthesizer'; import { StackSynthesizer } from './stack-synthesizer'; import { ISynthesisSession, IReusableStackSynthesizer, IBoundStackSynthesizer } from './types'; import * as cxapi from '../../../cx-api'; import { DockerImageAssetLocation, DockerImageAssetSource, FileAssetLocation, FileAssetSource } from '../assets'; +import { StringSpecializer } from '../helpers-internal/string-specializer'; import { Stack } from '../stack'; import { Token } from '../token'; diff --git a/packages/aws-cdk-lib/core/lib/stack-synthesizers/default-synthesizer.ts b/packages/aws-cdk-lib/core/lib/stack-synthesizers/default-synthesizer.ts index 0d6d12a499fb9..2bfc7f6989a21 100644 --- a/packages/aws-cdk-lib/core/lib/stack-synthesizers/default-synthesizer.ts +++ b/packages/aws-cdk-lib/core/lib/stack-synthesizers/default-synthesizer.ts @@ -1,10 +1,10 @@ -import { assertBound, StringSpecializer } from './_shared'; +import { assertBound } from './_shared'; import { AssetManifestBuilder } from './asset-manifest-builder'; import { StackSynthesizer } from './stack-synthesizer'; import { ISynthesisSession, IReusableStackSynthesizer, IBoundStackSynthesizer } from './types'; import * as cxapi from '../../../cx-api'; import { DockerImageAssetLocation, DockerImageAssetSource, FileAssetLocation, FileAssetSource } from '../assets'; -import { StringSpecializer } from '../helpers-internal'; +import { StringSpecializer } from '../helpers-internal/string-specializer'; import { Stack } from '../stack'; import { Token } from '../token'; diff --git a/packages/aws-cdk-lib/core/lib/stack-synthesizers/stack-synthesizer.ts b/packages/aws-cdk-lib/core/lib/stack-synthesizers/stack-synthesizer.ts index dc9b61158d825..97fb52189ce3a 100644 --- a/packages/aws-cdk-lib/core/lib/stack-synthesizers/stack-synthesizer.ts +++ b/packages/aws-cdk-lib/core/lib/stack-synthesizers/stack-synthesizer.ts @@ -1,6 +1,6 @@ import * as fs from 'fs'; import * as path from 'path'; -import { addStackArtifactToAssembly, contentHash, resolvedOr } from './_shared'; +import { addStackArtifactToAssembly, contentHash } from './_shared'; import { IStackSynthesizer, ISynthesisSession } from './types'; import * as cxschema from '../../../cloud-assembly-schema'; import * as cxapi from '../../../cx-api'; From 2c606239e387664abd50ae93f429691014656bb9 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Tue, 16 May 2023 18:28:42 -0400 Subject: [PATCH 114/120] fix tests --- .../test/app-staging-synthesizer.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/app-staging-synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/app-staging-synthesizer.test.ts index c6e4a286ff7d0..7f4150e4deab4 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/app-staging-synthesizer.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/app-staging-synthesizer.test.ts @@ -35,7 +35,7 @@ describe(AppStagingSynthesizer, () => { // THEN -- the S3 url is advertised on the stack artifact const stackArtifact = asm.getStackArtifact('Stack'); - const templateObjectKey = last(stackArtifact.stackTemplateAssetObjectUrl?.split('/')); + const templateObjectKey = `handoff/${last(stackArtifact.stackTemplateAssetObjectUrl?.split('/'))}`; expect(stackArtifact.stackTemplateAssetObjectUrl).toEqual(`s3://cdk-${APP_ID}-staging-000000000000-us-east-1/${templateObjectKey}`); // THEN - the template is in the asset manifest @@ -82,7 +82,7 @@ describe(AppStagingSynthesizer, () => { // THEN -- the S3 url is advertised on the stack artifact const stackArtifact = asm.getStackArtifact('Stack2'); - const templateObjectKey = last(stackArtifact.stackTemplateAssetObjectUrl?.split('/')); + const templateObjectKey = `handoff/${last(stackArtifact.stackTemplateAssetObjectUrl?.split('/'))}`; expect(stackArtifact.stackTemplateAssetObjectUrl).toEqual(`s3://cdk-${APP_ID}-staging-${accountToken}-${regionToken}/${templateObjectKey}`); // THEN - the template is in the asset manifest From 37074c47adc4da7c647aeddc8fcea7d4ca7652cd Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 17 May 2023 12:07:30 -0400 Subject: [PATCH 115/120] rename ephemeral into deployTime --- .../app-staging-synthesizer-alpha/README.md | 30 +++++++++---------- .../lib/default-staging-stack.ts | 12 ++++---- .../test/app-staging-synthesizer.test.ts | 30 +++++++++---------- .../aws-cdk-lib/aws-s3-assets/lib/asset.ts | 15 +++++----- packages/aws-cdk-lib/core/lib/assets.ts | 13 ++++---- .../stack-synthesizers/stack-synthesizer.ts | 2 +- 6 files changed, 52 insertions(+), 50 deletions(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/README.md b/packages/@aws-cdk/app-staging-synthesizer-alpha/README.md index 0df26d89cceca..d33aa08db9b0b 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/README.md +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/README.md @@ -313,41 +313,41 @@ const app = new App({ }); ``` -### Ephemeral S3 Assets +### Deploy Time S3 Assets -Some assets that get put into the staging S3 Bucket are ephemeral - they are only necessary -during CloudFormation deployment and not after. As long as you know what assets are ephemeral, -you can tag them as such and they will be marked with an `handoff/` prefix when they are staged. -This allows configuration of a lifecycle rule specifically for ephemeral assets. +Some assets that get put into the staging S3 Bucket are only necessary during CloudFormation +deployment and not after. As long as you know what assets are deploy time assets, you can tag +them as such and they will be marked with a `deploy-time/` prefix when they are staged. +This allows configuration of a lifecycle rule specifically for deploy time assets. A good example is a Lambda Function asset. The asset is only useful in the S3 Bucket at deploy time, because the source code gets copied into Lambda itself. So Lambda assets are by default -marked as ephemeral: +marked with `deployTime=true`: ```ts declare const stack: Stack; new lambda.Function(stack, 'lambda', { - code: lambda.AssetCode.fromAsset(path.join(__dirname, 'assets')), // lambda marks ephemeral = true + code: lambda.AssetCode.fromAsset(path.join(__dirname, 'assets')), // lambda marks deployTime = true handler: 'index.handler', runtime: lambda.Runtime.PYTHON_3_9, }); ``` -Or, if you want to create your own ephemeral asset: +Or, if you want to create your own deploy time asset: ```ts import { Asset } from 'aws-cdk-lib/aws-s3-assets'; declare const stack: Stack; -const asset = new Asset(stack, 'ephemeral-asset', { - ephemeral: true, - path: path.join(__dirname, './ephemeral-asset'), +const asset = new Asset(stack, 'deploy-time-asset', { + deployTime: true, + path: path.join(__dirname, './deploy-time-asset'), }); ``` -This means that the asset will go into the S3 Bucket with the prefix `handoff/`. It will also be -subject to the lifecycle rule set on ephemeral assets. By default, we store ephemeral assets for -30 days, but you can change this number by specifying `handoffFileAssetLifetime`. The number you +This means that the asset will go into the S3 Bucket with the prefix `deploy-time/`. It will also be +subject to the lifecycle rule set on deploy time assets. By default, we store deploy time assets for +30 days, but you can change this number by specifying `deployTimeFileAssetLifetime`. The number you specify here is how long you will be able to roll back to a previous version of an application just by doing a CloudFormation deployment with the old template, without rebuilding and republishing assets. @@ -356,7 +356,7 @@ republishing assets. const app = new App({ defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: 'my-app-id', - handoffFileAssetLifetime: Duration.days(100), + deployTimeFileAssetLifetime: Duration.days(100), }), }); ``` diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/default-staging-stack.ts index 869c9ca197578..d8f95ae7f75da 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/default-staging-stack.ts @@ -20,7 +20,7 @@ import { StringSpecializer } from 'aws-cdk-lib/core/lib/helpers-internal'; import { BootstrapRole } from './bootstrap-roles'; import { FileStagingLocation, IStagingResources, IStagingResourcesFactory, ImageStagingLocation } from './staging-stack'; -const EPHEMERAL_PREFIX = 'handoff/'; +export const DEPLOY_TIME_PREFIX = 'deploy-time/'; /** * User configurable options to the DefaultStagingStack. @@ -59,7 +59,7 @@ export interface DefaultStagingStackOptions { readonly imageAssetPublishingRole?: BootstrapRole; /** - * The lifetime for handoff file assets. + * The lifetime for deploy time file assets. * * Assets that are only necessary at deployment time (for instance, * CloudFormation templates and Lambda source code bundles) will be @@ -73,7 +73,7 @@ export interface DefaultStagingStackOptions { * * @default - Duration.days(30) */ - readonly handoffFileAssetLifetime?: Duration; + readonly deployTimeFileAssetLifetime?: Duration; /** * The maximum number of image versions to store in a repository. @@ -346,8 +346,8 @@ export class DefaultStagingStack extends Stack implements IStagingResources { }); bucket.addLifecycleRule({ - prefix: EPHEMERAL_PREFIX, - expiration: this.props.handoffFileAssetLifetime ?? Duration.days(30), + prefix: DEPLOY_TIME_PREFIX, + expiration: this.props.deployTimeFileAssetLifetime ?? Duration.days(30), }); return stagingBucketName; @@ -392,7 +392,7 @@ export class DefaultStagingStack extends Stack implements IStagingResources { return { bucketName, assumeRoleArn: this.fileRoleManifestArn, - prefix: asset.ephemeral ? EPHEMERAL_PREFIX : undefined, + prefix: asset.deployTime ? DEPLOY_TIME_PREFIX : undefined, dependencyStack: this, }; } diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/app-staging-synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/app-staging-synthesizer.test.ts index 7f4150e4deab4..f2b8c54a456f8 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/app-staging-synthesizer.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/app-staging-synthesizer.test.ts @@ -5,7 +5,7 @@ import * as cxschema from 'aws-cdk-lib/cloud-assembly-schema'; import { CloudAssembly } from 'aws-cdk-lib/cx-api'; import { evaluateCFN } from './evaluate-cfn'; import { APP_ID, CFN_CONTEXT, isAssetManifest, last } from './util'; -import { AppStagingSynthesizer } from '../lib'; +import { AppStagingSynthesizer, DEPLOY_TIME_PREFIX } from '../lib'; describe(AppStagingSynthesizer, () => { let app: App; @@ -35,7 +35,7 @@ describe(AppStagingSynthesizer, () => { // THEN -- the S3 url is advertised on the stack artifact const stackArtifact = asm.getStackArtifact('Stack'); - const templateObjectKey = `handoff/${last(stackArtifact.stackTemplateAssetObjectUrl?.split('/'))}`; + const templateObjectKey = `${DEPLOY_TIME_PREFIX}${last(stackArtifact.stackTemplateAssetObjectUrl?.split('/'))}`; expect(stackArtifact.stackTemplateAssetObjectUrl).toEqual(`s3://cdk-${APP_ID}-staging-000000000000-us-east-1/${templateObjectKey}`); // THEN - the template is in the asset manifest @@ -82,7 +82,7 @@ describe(AppStagingSynthesizer, () => { // THEN -- the S3 url is advertised on the stack artifact const stackArtifact = asm.getStackArtifact('Stack2'); - const templateObjectKey = `handoff/${last(stackArtifact.stackTemplateAssetObjectUrl?.split('/'))}`; + const templateObjectKey = `${DEPLOY_TIME_PREFIX}${last(stackArtifact.stackTemplateAssetObjectUrl?.split('/'))}`; expect(stackArtifact.stackTemplateAssetObjectUrl).toEqual(`s3://cdk-${APP_ID}-staging-${accountToken}-${regionToken}/${templateObjectKey}`); // THEN - the template is in the asset manifest @@ -152,21 +152,21 @@ describe(AppStagingSynthesizer, () => { expect(evalCFN(location1.bucketName)).toEqual(evalCFN(location2.bucketName)); }); - describe('ephemeral assets', () => { - test('ephemeral assets have the \'handoff/\' prefix', () => { + describe('deploy time assets', () => { + test('have the \'deploy-time/\' prefix', () => { // WHEN const location = stack.synthesizer.addFileAsset({ fileName: __filename, packaging: FileAssetPackaging.FILE, sourceHash: 'abcdef', - ephemeral: true, + deployTime: true, }); // THEN - asset has bucket prefix - expect(evalCFN(location.objectKey)).toEqual('handoff/abcdef.js'); + expect(evalCFN(location.objectKey)).toEqual(`${DEPLOY_TIME_PREFIX}abcdef.js`); }); - test('ephemeral assets do not get specified bucketPrefix', () => { + test('do not get specified bucketPrefix', () => { // GIVEN app = new App({ defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: APP_ID }), @@ -183,14 +183,14 @@ describe(AppStagingSynthesizer, () => { fileName: __filename, packaging: FileAssetPackaging.FILE, sourceHash: 'abcdef', - ephemeral: true, + deployTime: true, }); // THEN - asset has bucket prefix - expect(evalCFN(location.objectKey)).toEqual('handoff/abcdef.js'); + expect(evalCFN(location.objectKey)).toEqual(`${DEPLOY_TIME_PREFIX}abcdef.js`); }); - test('s3 bucket has lifecycle rule on ephemeral assets by default', () => { + test('have s3 bucket has lifecycle rule by default', () => { // GIVEN new CfnResource(stack, 'Resource', { type: 'Some::Resource', @@ -204,19 +204,19 @@ describe(AppStagingSynthesizer, () => { LifecycleConfiguration: { Rules: Match.arrayWith([{ ExpirationInDays: 30, - Prefix: 'handoff/', + Prefix: DEPLOY_TIME_PREFIX, Status: 'Enabled', }]), }, }); }); - test('lifecycle rule on ephemeral assets can be customized', () => { + test('can have customized lifecycle rules', () => { // GIVEN app = new App({ defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: APP_ID, - handoffFileAssetLifetime: Duration.days(1), + deployTimeFileAssetLifetime: Duration.days(1), }), }); stack = new Stack(app, 'Stack', { @@ -239,7 +239,7 @@ describe(AppStagingSynthesizer, () => { LifecycleConfiguration: { Rules: Match.arrayWith([{ ExpirationInDays: 1, - Prefix: 'handoff/', + Prefix: DEPLOY_TIME_PREFIX, Status: 'Enabled', }]), }, diff --git a/packages/aws-cdk-lib/aws-s3-assets/lib/asset.ts b/packages/aws-cdk-lib/aws-s3-assets/lib/asset.ts index ba4bc722580dd..d20059505d674 100644 --- a/packages/aws-cdk-lib/aws-s3-assets/lib/asset.ts +++ b/packages/aws-cdk-lib/aws-s3-assets/lib/asset.ts @@ -36,18 +36,19 @@ export interface AssetOptions extends CopyOptions, cdk.FileCopyOptions, cdk.Asse readonly sourceHash?: string; /** - * Whether or not the asset is ephemeral; i.e. only used during deployment - * and not needed afterwards. Setting this property to true has an impact - * on the lifecycle of the asset, because we will assume that it is safe to - * delete after the CloudFormation deployment succeeds. + * Whether or not the asset needs to exist beyond deployment time; i.e. + * are copied over to a different location and not needed afterwards. + * Setting this property to true has an impact on the lifecycle of the asset, + * because we will assume that it is safe to delete after the CloudFormation + * deployment succeeds. * * For example, Lambda Function assets are copied over to Lambda during * deployment. Therefore, it is not necessary to store the asset in S3, so - * we consider those assets ephemeral. + * we consider those deployTime assets. * * @default false */ - readonly ephemeral?: boolean; + readonly deployTime?: boolean; } export interface AssetProps extends AssetOptions { @@ -161,7 +162,7 @@ export class Asset extends Construct implements cdk.IAsset { packaging: staging.packaging, sourceHash: this.sourceHash, fileName: this.assetPath, - ephemeral: props.ephemeral, + deployTime: props.deployTime, }); this.s3BucketName = location.bucketName; diff --git a/packages/aws-cdk-lib/core/lib/assets.ts b/packages/aws-cdk-lib/core/lib/assets.ts index e02ba7a1a10ec..d4243b6f7a7b7 100644 --- a/packages/aws-cdk-lib/core/lib/assets.ts +++ b/packages/aws-cdk-lib/core/lib/assets.ts @@ -133,18 +133,19 @@ export interface FileAssetSource { readonly packaging?: FileAssetPackaging; /** - * Whether or not the asset is ephemeral; i.e. only used during deployment - * and not needed afterwards. Setting this property to true has an impact - * on the lifecycle of the asset, because we will assume that it is safe to - * delete after the CloudFormation deployment succeeds. + * Whether or not the asset needs to exist beyond deployment time; i.e. + * are copied over to a different location and not needed afterwards. + * Setting this property to true has an impact on the lifecycle of the asset, + * because we will assume that it is safe to delete after the CloudFormation + * deployment succeeds. * * For example, Lambda Function assets are copied over to Lambda during * deployment. Therefore, it is not necessary to store the asset in S3, so - * we consider those assets ephemeral. + * we consider those deployTime assets. * * @default false */ - readonly ephemeral?: boolean; + readonly deployTime?: boolean; } export interface DockerImageAssetSource { diff --git a/packages/aws-cdk-lib/core/lib/stack-synthesizers/stack-synthesizer.ts b/packages/aws-cdk-lib/core/lib/stack-synthesizers/stack-synthesizer.ts index 97fb52189ce3a..f8d5bb30ef344 100644 --- a/packages/aws-cdk-lib/core/lib/stack-synthesizers/stack-synthesizer.ts +++ b/packages/aws-cdk-lib/core/lib/stack-synthesizers/stack-synthesizer.ts @@ -292,7 +292,7 @@ function stackTemplateFileAsset(stack: Stack, session: ISynthesisSession): FileA fileName: stack.templateFile, packaging: FileAssetPackaging.FILE, sourceHash, - ephemeral: true, + deployTime: true, }; } From e0a9089c734ecdc330cdce98ec380a168f1d2b92 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 17 May 2023 12:12:03 -0400 Subject: [PATCH 116/120] update readme content --- .../app-staging-synthesizer-alpha/README.md | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/README.md b/packages/@aws-cdk/app-staging-synthesizer-alpha/README.md index d33aa08db9b0b..e420435383361 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/README.md +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/README.md @@ -315,14 +315,17 @@ const app = new App({ ### Deploy Time S3 Assets -Some assets that get put into the staging S3 Bucket are only necessary during CloudFormation -deployment and not after. As long as you know what assets are deploy time assets, you can tag -them as such and they will be marked with a `deploy-time/` prefix when they are staged. -This allows configuration of a lifecycle rule specifically for deploy time assets. +There are two types of assets: -A good example is a Lambda Function asset. The asset is only useful in the S3 Bucket at deploy -time, because the source code gets copied into Lambda itself. So Lambda assets are by default -marked with `deployTime=true`: +* Assets used only during deployment. These are used to hand off a large piece of data to another service, that will make a private copy of that data. After deployment, the asset is only necessary for a potential future rollback. +* Assets accessed throughout the running life time of the application. + +Examples of assets that are only used at deploy time are CloudFormation Templates and Lambda Code +bundles. Examples of assets accessed throughout the life time of the application are script files +downloaded to run in a CodeBuild Project, or on EC2 instance startup. ECR images are always application +life-time assets. S3 deploy time assets are stored with a `deploy-time/` prefix, and a lifecycle rule will collect them after a configurable number of days. + +Lambda assets are by default marked as deploy time assets: ```ts declare const stack: Stack; @@ -345,12 +348,10 @@ const asset = new Asset(stack, 'deploy-time-asset', { }); ``` -This means that the asset will go into the S3 Bucket with the prefix `deploy-time/`. It will also be -subject to the lifecycle rule set on deploy time assets. By default, we store deploy time assets for -30 days, but you can change this number by specifying `deployTimeFileAssetLifetime`. The number you -specify here is how long you will be able to roll back to a previous version of an application -just by doing a CloudFormation deployment with the old template, without rebuilding and -republishing assets. +By default, we store deploy time assets for 30 days, but you can change this number by specifying +`deployTimeFileAssetLifetime`. The number you specify here is how long you will be able to roll back +to a previous version of an application just by doing a CloudFormation deployment with the old +template, without rebuilding and republishing assets. ```ts const app = new App({ From af5856bff54067d0e39deb36cc78784bad280143 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 17 May 2023 12:32:05 -0400 Subject: [PATCH 117/120] final updates --- .../app-staging-synthesizer-alpha/README.md | 156 +++++++++--------- .../lib/app-staging-synthesizer.ts | 14 +- .../test/bootstrap-roles.test.ts | 4 +- 3 files changed, 84 insertions(+), 90 deletions(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/README.md b/packages/@aws-cdk/app-staging-synthesizer-alpha/README.md index e420435383361..9d9c9e372f7a0 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/README.md +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/README.md @@ -152,14 +152,7 @@ controlled individually. > As this library is `experimental`, the accompanying Bootstrap Stack is not yet implemented. To use this > library right now, you must reuse roles that have been traditionally bootstrapped. -## Synthesizer - -To use this library, supply the `AppStagingSynthesizer` in as the default synthesizer to the app. -This will ensure that a Staging Stack will be created next to the CDK App to hold the staging resources. - -`AppStagingSynthesizer` comes with static methods covering the use-cases for the synthesizer. - -### Using the Default Staging Stack per Environment +## Using the Default Staging Stack per Environment The most common use case will be to use the built-in default resources. In this scenario, the synthesizer will create a new Staging Stack in each environment the CDK App is deployed to store @@ -177,72 +170,7 @@ Every CDK App that uses the `DefaultStagingStack` must include an `appId`. This be an identifier unique to the app and is used to differentiate staging resources associated with the app. -### Using a Custom Staging Stack per Environment - -Use `AppStagingSynthesizer.customFactory()` to supply a custom staging stack on a per-environment basis. -This has the benefit of providing a custom Staging Stack that can be created in every environment the -CDK App is deployed to. - -```ts -interface CustomStagingStackProps extends StackProps {} - -class CustomStagingStack extends Stack implements IStagingResources { - public constructor(scope: Construct, id: string, props: CustomStagingStackProps) { - super(scope, id, props); - } - - public addFile(asset: FileAssetSource): FileStagingLocation { - return { - bucketName: 'myBucket', - assumeRoleArn: 'myArn', - dependencyStack: this, - }; - } - - public addDockerImage(asset: DockerImageAssetSource): ImageStagingLocation { - return { - repoName: 'myRepo', - assumeRoleArn: 'myArn', - dependencyStack: this, - }; - } -} -``` - -```ts fixture=with-custom-staging -class CustomFactory implements IStagingResourcesFactory { - public obtainStagingResources(stack: Stack, context: ObtainStagingResourcesContext) { - const myApp = App.of(stack); - - return new CustomStagingStack(myApp!, `CustomStagingStack-${context.environmentString}`, {}); - } -} - -const app = new App({ - defaultStackSynthesizer: AppStagingSynthesizer.customFactory({ - factory: new CustomFactory(), - oncePerEnv: true, // by default - }), -}); -``` - -### Using an Existing Staging Stack - -Use `AppStagingSynthesizer.customResources()` to supply an existing stack as the Staging Stack. -Make sure that the custom stack you provide implements `IStagingResources`. - -```ts fixture=with-custom-staging -const resourceApp = new App(); -const resources = new CustomStagingStack(resourceApp, 'CustomStagingStack', {}); - -const app = new App({ - defaultStackSynthesizer: AppStagingSynthesizer.customResources({ - resources, - }), -}); -``` - -## Default Staging Stack +### Default Staging Stack The Default Staging Stack includes all the staging resources necessary for CDK Assets. The below example is of a CDK App using the `AppStagingSynthesizer` and creating a file asset for the Lambda Function @@ -274,7 +202,7 @@ if all you need is to supply custom roles (and not change anything else in the ` const app = new App({ defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: 'my-app-id', - deploymentRoles: DeploymentIdentities.specifyRoles({ + deploymentIdentities: DeploymentIdentities.specifyRoles({ cloudFormationExecutionRole: BootstrapRole.fromRoleArn('arn:aws:iam::123456789012:role/Execute'), deploymentRole: BootstrapRole.fromRoleArn('arn:aws:iam::123456789012:role/Deploy'), lookupRole: BootstrapRole.fromRoleArn('arn:aws:iam::123456789012:role/Lookup'), @@ -294,7 +222,7 @@ and `CloudFormationExecutionRole` in the const app = new App({ defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: 'my-app-id', - deploymentRoles: DeploymentIdentities.cliCredentials(), + deploymentIdentities: DeploymentIdentities.cliCredentials(), }), }); ``` @@ -317,8 +245,10 @@ const app = new App({ There are two types of assets: -* Assets used only during deployment. These are used to hand off a large piece of data to another service, that will make a private copy of that data. After deployment, the asset is only necessary for a potential future rollback. -* Assets accessed throughout the running life time of the application. +- Assets used only during deployment. These are used to hand off a large piece of data to another +service, that will make a private copy of that data. After deployment, the asset is only necessary for +a potential future rollback. +- Assets accessed throughout the running life time of the application. Examples of assets that are only used at deploy time are CloudFormation Templates and Lambda Code bundles. Examples of assets accessed throughout the life time of the application are script files @@ -380,7 +310,7 @@ const app = new App({ }); ``` -## Extending the Default Staging Stack +## Using a Custom Staging Stack per Environment If you want to customize some behavior that is not configurable via properties, you can implement your own class that implements `IStagingResources`. To get a head start, @@ -389,7 +319,71 @@ you can subclass `DefaultStagingStack`. ```ts interface CustomStagingStackOptions extends DefaultStagingStackOptions {} -class CustomStagingStack extends DefaultStagingStack { - +class CustomStagingStack extends DefaultStagingStack { } ``` + +Or you can roll your own staging resources from scratch, as long as it implements `IStagingResources`. + +```ts +interface CustomStagingStackProps extends StackProps {} + +class CustomStagingStack extends Stack implements IStagingResources { + public constructor(scope: Construct, id: string, props: CustomStagingStackProps) { + super(scope, id, props); + } + + public addFile(asset: FileAssetSource): FileStagingLocation { + return { + bucketName: 'myBucket', + assumeRoleArn: 'myArn', + dependencyStack: this, + }; + } + + public addDockerImage(asset: DockerImageAssetSource): ImageStagingLocation { + return { + repoName: 'myRepo', + assumeRoleArn: 'myArn', + dependencyStack: this, + }; + } +} +``` + +Using your custom staging resources means implementing a `CustomFactory` class and calling the +`AppStagingSynthesizer.customFactory()` static method. This has the benefit of providing a +custom Staging Stack that can be created in every environment the CDK App is deployed to. + +```ts fixture=with-custom-staging +class CustomFactory implements IStagingResourcesFactory { + public obtainStagingResources(stack: Stack, context: ObtainStagingResourcesContext) { + const myApp = App.of(stack); + + return new CustomStagingStack(myApp!, `CustomStagingStack-${context.environmentString}`, {}); + } +} + +const app = new App({ + defaultStackSynthesizer: AppStagingSynthesizer.customFactory({ + factory: new CustomFactory(), + oncePerEnv: true, // by default + }), +}); +``` + +## Using an Existing Staging Stack + +Use `AppStagingSynthesizer.customResources()` to supply an existing stack as the Staging Stack. +Make sure that the custom stack you provide implements `IStagingResources`. + +```ts fixture=with-custom-staging +const resourceApp = new App(); +const resources = new CustomStagingStack(resourceApp, 'CustomStagingStack', {}); + +const app = new App({ + defaultStackSynthesizer: AppStagingSynthesizer.customResources({ + resources, + }), +}); +``` diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/app-staging-synthesizer.ts index 7f03c37cb6e2a..a7c84bf510fd7 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/app-staging-synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/app-staging-synthesizer.ts @@ -37,7 +37,7 @@ export interface AppStagingSynthesizerOptions { * * @default - The standard bootstrapped CDK roles */ - readonly deploymentRoles?: DeploymentIdentities; + readonly deploymentIdentities?: DeploymentIdentities; /** * Qualifier to disambiguate multiple bootstrapped environments in the same account @@ -126,7 +126,7 @@ export class AppStagingSynthesizer extends StackSynthesizer implements IReusable return AppStagingSynthesizer.customFactory({ factory: DefaultStagingStack.factory(options), - deploymentRoles: options.deploymentRoles, + deploymentIdentities: options.deploymentIdentities, bootstrapQualifier: options.bootstrapQualifier, oncePerEnv: true, }); @@ -137,7 +137,7 @@ export class AppStagingSynthesizer extends StackSynthesizer implements IReusable */ public static customResources(options: CustomResourcesOptions) { return AppStagingSynthesizer.customFactory({ - deploymentRoles: options.deploymentRoles, + deploymentIdentities: options.deploymentIdentities, bootstrapQualifier: options.bootstrapQualifier, oncePerEnv: false, factory: { @@ -162,7 +162,7 @@ export class AppStagingSynthesizer extends StackSynthesizer implements IReusable return new AppStagingSynthesizer({ factory, bootstrapQualifier: options.bootstrapQualifier, - deploymentRoles: options.deploymentRoles, + deploymentIdentities: options.deploymentIdentities, }); } @@ -172,11 +172,11 @@ export class AppStagingSynthesizer extends StackSynthesizer implements IReusable super(); this.roles = { - deploymentRole: props.deploymentRoles?.roles.deploymentRole ?? + deploymentRole: props.deploymentIdentities?.roles.deploymentRole ?? BootstrapRole.fromRoleArn(AppStagingSynthesizer.DEFAULT_DEPLOY_ROLE_ARN), - cloudFormationExecutionRole: props.deploymentRoles?.roles.cloudFormationExecutionRole ?? + cloudFormationExecutionRole: props.deploymentIdentities?.roles.cloudFormationExecutionRole ?? BootstrapRole.fromRoleArn(AppStagingSynthesizer.DEFAULT_CLOUDFORMATION_ROLE_ARN), - lookupRole: this.props.deploymentRoles?.roles.lookupRole ?? + lookupRole: this.props.deploymentIdentities?.roles.lookupRole ?? BootstrapRole.fromRoleArn(AppStagingSynthesizer.DEFAULT_LOOKUP_ROLE_ARN), }; } diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/bootstrap-roles.test.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/bootstrap-roles.test.ts index 6396038692eac..a46e1807f8c97 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/bootstrap-roles.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/bootstrap-roles.test.ts @@ -42,7 +42,7 @@ describe('Boostrap Roles', () => { const app = new App({ defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: APP_ID, - deploymentRoles: DeploymentIdentities.specifyRoles({ + deploymentIdentities: DeploymentIdentities.specifyRoles({ cloudFormationExecutionRole: BootstrapRole.fromRoleArn(CLOUDFORMATION_EXECUTION_ROLE), lookupRole: BootstrapRole.fromRoleArn(LOOKUP_ROLE), deploymentRole: BootstrapRole.fromRoleArn(DEPLOY_ACTION_ROLE), @@ -138,7 +138,7 @@ describe('Boostrap Roles', () => { const app = new App({ defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({ appId: APP_ID, - deploymentRoles: DeploymentIdentities.cliCredentials(), + deploymentIdentities: DeploymentIdentities.cliCredentials(), }), }); const stack = new Stack(app, 'Stack', { From b1210b34c8a463c71dd1703a86b051ed87e9a3b1 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 17 May 2023 17:11:42 -0400 Subject: [PATCH 118/120] minor renames --- .../lib/app-staging-synthesizer.ts | 11 ++++--- .../lib/default-staging-stack.ts | 2 +- .../test/app-staging-synthesizer.test.ts | 33 +++++++++++++++++++ ...ult-resources-ACCOUNT-REGION.template.json | 4 +-- .../manifest.json | 7 ++-- .../synthesize-default-resources.assets.json | 6 ++-- .../tree.json | 10 +++--- 7 files changed, 56 insertions(+), 17 deletions(-) diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/app-staging-synthesizer.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/app-staging-synthesizer.ts index a7c84bf510fd7..21f7290d19f4b 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/app-staging-synthesizer.ts +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/app-staging-synthesizer.ts @@ -298,6 +298,7 @@ class BoundAppStagingSynthesizer extends StackSynthesizer implements IBoundAppSt private readonly stagingStack: IStagingResources; private readonly assetManifest = new AssetManifestBuilder(); private readonly qualifier: string; + private readonly dependencyStacks: Set = new Set(); constructor(stack: Stack, private readonly props: BoundAppStagingSynthesizerProps) { super(); @@ -318,7 +319,8 @@ class BoundAppStagingSynthesizer extends StackSynthesizer implements IBoundAppSt const templateAssetSource = this.synthesizeTemplate(session, this.props.lookupRole?._arnForCloudAssembly()); const templateAsset = this.addFileAsset(templateAssetSource); - const assetManifestId = this.assetManifest.emitManifest(this.boundStack, session); + const dependencies = Array.from(this.dependencyStacks).flatMap((d) => d.artifactId); + const assetManifestId = this.assetManifest.emitManifest(this.boundStack, session, {}, dependencies); const lookupRoleArn = this.props.lookupRole?._arnForCloudAssembly(); @@ -339,11 +341,12 @@ class BoundAppStagingSynthesizer extends StackSynthesizer implements IBoundAppSt const location = this.assetManifest.defaultAddFileAsset(this.boundStack, asset, { bucketName: translateCfnTokenToAssetToken(bucketName), bucketPrefix: prefix, - role: assumeRoleArn ? { assumeRoleArn: translateCfnTokenToAssetToken(assumeRoleArn) } : undefined, // TODO: check if this is necessary + role: assumeRoleArn ? { assumeRoleArn: translateCfnTokenToAssetToken(assumeRoleArn) } : undefined, }); if (dependencyStack) { this.boundStack.addDependency(dependencyStack, 'stack depends on the staging stack for staging resources'); + this.dependencyStacks.add(dependencyStack); } return this.cloudFormationLocationFromFileAsset(location); @@ -356,12 +359,12 @@ class BoundAppStagingSynthesizer extends StackSynthesizer implements IBoundAppSt const { repoName, assumeRoleArn, dependencyStack } = this.stagingStack.addDockerImage(asset); const location = this.assetManifest.defaultAddDockerImageAsset(this.boundStack, asset, { repositoryName: translateCfnTokenToAssetToken(repoName), - role: assumeRoleArn ? { assumeRoleArn: translateCfnTokenToAssetToken(assumeRoleArn) } : undefined, // TODO: check if this is necessary - // TODO: more props + role: assumeRoleArn ? { assumeRoleArn: translateCfnTokenToAssetToken(assumeRoleArn) } : undefined, }); if (dependencyStack) { this.boundStack.addDependency(dependencyStack, 'stack depends on the staging stack for staging resources'); + this.dependencyStacks.add(dependencyStack); } return this.cloudFormationLocationFromDockerImageAsset(location); diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/default-staging-stack.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/default-staging-stack.ts index d8f95ae7f75da..468323e0ec2c4 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/default-staging-stack.ts +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/lib/default-staging-stack.ts @@ -160,7 +160,7 @@ export class DefaultStagingStack extends Stack implements IStagingResources { // This role name can be a maximum of 64 letters. The reason why // we slice the appId and not the entire name is because this.region // can be a token and we don't want to accidentally cut it off. - return `cdk-${this.appId}-asset-role-${this.region}`; + return `cdk-${this.appId}-image-role-${this.region}`; } /** diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/app-staging-synthesizer.test.ts b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/app-staging-synthesizer.test.ts index f2b8c54a456f8..d6a47ba76c65e 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/app-staging-synthesizer.test.ts +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/app-staging-synthesizer.test.ts @@ -135,6 +135,22 @@ describe(AppStagingSynthesizer, () => { expect(location.objectKey.indexOf('abcdef')).toBeGreaterThan(-1); }); + test('file asset depends on staging stack', () => { + // WHEN + stack.synthesizer.addFileAsset({ + fileName: __filename, + packaging: FileAssetPackaging.FILE, + sourceHash: 'abcdef', + }); + + const asm = app.synth(); + + // THEN - the template is in the asset manifest + const manifestArtifact = asm.artifacts.filter(isAssetManifest)[0]; + expect(manifestArtifact).toBeDefined(); + expect(manifestArtifact.manifest.dependencies).toEqual([`StagingStack-${APP_ID}-000000000000-us-east-1`]); + }); + test('adding multiple files only creates one bucket', () => { // WHEN const location1 = stack.synthesizer.addFileAsset({ @@ -299,6 +315,23 @@ describe(AppStagingSynthesizer, () => { })).toThrowError('Assets synthesized with AppScopedStagingSynthesizer must include an \'assetName\' in the asset source definition.'); }); + test('docker image asset depends on staging stack', () => { + // WHEN + const assetName = 'abcdef'; + stack.synthesizer.addDockerImageAsset({ + directoryName: '.', + sourceHash: 'abcdef', + assetName, + }); + + const asm = app.synth(); + + // THEN - the template is in the asset manifest + const manifestArtifact = asm.artifacts.filter(isAssetManifest)[0]; + expect(manifestArtifact).toBeDefined(); + expect(manifestArtifact.manifest.dependencies).toEqual([`StagingStack-${APP_ID}-000000000000-us-east-1`]); + }); + test('docker image assets with different assetName have separate repos', () => { // WHEN const location1 = stack.synthesizer.addDockerImageAsset({ diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/StagingStack-default-resources-ACCOUNT-REGION.template.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/StagingStack-default-resources-ACCOUNT-REGION.template.json index eb3be509a12f4..fae319dc47641 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/StagingStack-default-resources-ACCOUNT-REGION.template.json +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/StagingStack-default-resources-ACCOUNT-REGION.template.json @@ -240,7 +240,7 @@ }, { "ExpirationInDays": 30, - "Prefix": "handoff/", + "Prefix": "deploy-time/", "Status": "Enabled" } ] @@ -388,7 +388,7 @@ "Fn::Join": [ "", [ - "cdk-default-resources-asset-role-", + "cdk-default-resources-image-role-", { "Ref": "AWS::Region" } diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/manifest.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/manifest.json index ab3b50537bb2e..e9ac382233e75 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/manifest.json +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/manifest.json @@ -5,7 +5,10 @@ "type": "cdk:asset-manifest", "properties": { "file": "synthesize-default-resources.assets.json" - } + }, + "dependencies": [ + "StagingStack-default-resources-ACCOUNT-REGION" + ] }, "synthesize-default-resources": { "type": "aws:cloudformation:stack", @@ -17,7 +20,7 @@ "additionalDependencies": [ "synthesize-default-resources.assets" ], - "stackTemplateAssetObjectUrl": "s3://cdk-default-resources-staging-${AWS::AccountId}-${AWS::Region}/e21d11bec65be920861a56a86066cc88a0241d5cbe8324d0692ca982420e4cb0.json", + "stackTemplateAssetObjectUrl": "s3://cdk-default-resources-staging-${AWS::AccountId}-${AWS::Region}/deploy-time/e21d11bec65be920861a56a86066cc88a0241d5cbe8324d0692ca982420e4cb0.json", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", "lookupRole": { "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}" diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/synthesize-default-resources.assets.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/synthesize-default-resources.assets.json index ba7243f295609..c17a6ccdaa514 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/synthesize-default-resources.assets.json +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/synthesize-default-resources.assets.json @@ -22,7 +22,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-default-resources-staging-${AWS::AccountId}-${AWS::Region}", - "objectKey": "e21d11bec65be920861a56a86066cc88a0241d5cbe8324d0692ca982420e4cb0.json", + "objectKey": "deploy-time/e21d11bec65be920861a56a86066cc88a0241d5cbe8324d0692ca982420e4cb0.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-default-resources-file-role-${AWS::Region}" } } @@ -37,7 +37,7 @@ "current_account-current_region": { "repositoryName": "default-resources/ecr-asset", "imageTag": "16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622", - "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-default-resources-asset-role-${AWS::Region}" + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-default-resources-image-role-${AWS::Region}" } } }, @@ -49,7 +49,7 @@ "current_account-current_region": { "repositoryName": "default-resources/ecr-asset-2", "imageTag": "16624c2a162b07c5cc0e2c59c484f638bac238ca558ccbdc2aa0e0535df3e622", - "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-default-resources-asset-role-${AWS::Region}" + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-default-resources-image-role-${AWS::Region}" } } } diff --git a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/tree.json b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/tree.json index f25e00615302b..4a76ae37e2e0d 100644 --- a/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/tree.json +++ b/packages/@aws-cdk/app-staging-synthesizer-alpha/test/integ.synth-default-resources.js.snapshot/tree.json @@ -817,7 +817,7 @@ }, { "expirationInDays": 30, - "prefix": "handoff/", + "prefix": "deploy-time/", "status": "Enabled" } ] @@ -1007,7 +1007,7 @@ "Fn::Join": [ "", [ - "cdk-default-resources-asset-role-", + "cdk-default-resources-image-role-", { "Ref": "AWS::Region" } @@ -1150,7 +1150,7 @@ } }, "constructInfo": { - "fqn": "aws-cdk-lib.Stack", + "fqn": "@aws-cdk/app-staging-synthesizer-alpha.DefaultStagingStack", "version": "0.0.0" } }, @@ -1167,7 +1167,7 @@ "path": "integ-tests/DefaultTest/Default", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.9" + "version": "10.2.26" } }, "DeployAssert": { @@ -1213,7 +1213,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.9" + "version": "10.2.26" } } }, From 48270f4091a4356dbab3544583dc94c841b831bd Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 17 May 2023 17:13:32 -0400 Subject: [PATCH 119/120] catch errors in isPublished so removePublishedAsets cannot fail --- .../cdk-assets/lib/private/handlers/container-images.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/cdk-assets/lib/private/handlers/container-images.ts b/packages/cdk-assets/lib/private/handlers/container-images.ts index 0537c788970c9..a56f0bcc96081 100644 --- a/packages/cdk-assets/lib/private/handlers/container-images.ts +++ b/packages/cdk-assets/lib/private/handlers/container-images.ts @@ -46,8 +46,13 @@ export class ContainerImageAssetHandler implements IAssetHandler { } public async isPublished(): Promise { - const initOnce = await this.initOnce(); - return initOnce.destinationAlreadyExists; + try { + const initOnce = await this.initOnce(); + return initOnce.destinationAlreadyExists; + } catch (e: any) { + this.host.emitMessage(EventType.DEBUG, `${e.message}`); + } + return false; } public async publish(): Promise { From 93dad510830d819eee5ff06993c8166074cb12c7 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Fri, 19 May 2023 16:54:17 -0400 Subject: [PATCH 120/120] silence warning messages when calling is__Published --- packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts | 4 +++- packages/aws-cdk/lib/util/asset-publishing.ts | 3 ++- packages/cdk-assets/lib/aws.ts | 1 + .../cdk-assets/lib/private/handlers/container-images.ts | 9 ++++++--- packages/cdk-assets/lib/private/handlers/files.ts | 5 ++++- 5 files changed, 16 insertions(+), 6 deletions(-) diff --git a/packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts b/packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts index e77fefa61083b..9eeebb0347c8d 100644 --- a/packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts +++ b/packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts @@ -174,6 +174,7 @@ export class SdkProvider { environment: cxapi.Environment, mode: Mode, options?: CredentialsOptions, + quiet = false, ): Promise { const env = await this.resolveEnvironment(environment); @@ -213,7 +214,8 @@ export class SdkProvider { // but if we can't then let's just try with available credentials anyway. if (baseCreds.source === 'correctDefault' || baseCreds.source === 'plugin') { debug(e.message); - warning(`${fmtObtainedCredentials(baseCreds)} could not be used to assume '${options.assumeRoleArn}', but are for the right account. Proceeding anyway.`); + const logger = quiet ? debug : warning; + logger(`${fmtObtainedCredentials(baseCreds)} could not be used to assume '${options.assumeRoleArn}', but are for the right account. Proceeding anyway.`); return { sdk: new SDK(baseCreds.credentials, env.region, this.sdkOptions), didAssumeRole: false }; } diff --git a/packages/aws-cdk/lib/util/asset-publishing.ts b/packages/aws-cdk/lib/util/asset-publishing.ts index c94c9bab94a94..7b4c4f943d9ff 100644 --- a/packages/aws-cdk/lib/util/asset-publishing.ts +++ b/packages/aws-cdk/lib/util/asset-publishing.ts @@ -169,6 +169,7 @@ export class PublishingAws implements cdk_assets.IAws { env, // region, name, account assumeRuleArn: options.assumeRoleArn, assumeRoleExternalId: options.assumeRoleExternalId, + quiet: options.quiet, }); const maybeSdk = this.sdkCache.get(cacheKey); @@ -179,7 +180,7 @@ export class PublishingAws implements cdk_assets.IAws { const sdk = (await this.aws.forEnvironment(env, Mode.ForWriting, { assumeRoleArn: options.assumeRoleArn, assumeRoleExternalId: options.assumeRoleExternalId, - })).sdk; + }, options.quiet)).sdk; this.sdkCache.set(cacheKey, sdk); return sdk; diff --git a/packages/cdk-assets/lib/aws.ts b/packages/cdk-assets/lib/aws.ts index 02bb67d41916c..4d9e731692d4e 100644 --- a/packages/cdk-assets/lib/aws.ts +++ b/packages/cdk-assets/lib/aws.ts @@ -18,6 +18,7 @@ export interface ClientOptions { region?: string; assumeRoleArn?: string; assumeRoleExternalId?: string; + quiet?: boolean; } /** diff --git a/packages/cdk-assets/lib/private/handlers/container-images.ts b/packages/cdk-assets/lib/private/handlers/container-images.ts index a56f0bcc96081..670c813dd8b20 100644 --- a/packages/cdk-assets/lib/private/handlers/container-images.ts +++ b/packages/cdk-assets/lib/private/handlers/container-images.ts @@ -47,7 +47,7 @@ export class ContainerImageAssetHandler implements IAssetHandler { public async isPublished(): Promise { try { - const initOnce = await this.initOnce(); + const initOnce = await this.initOnce({ quiet: true }); return initOnce.destinationAlreadyExists; } catch (e: any) { this.host.emitMessage(EventType.DEBUG, `${e.message}`); @@ -73,13 +73,16 @@ export class ContainerImageAssetHandler implements IAssetHandler { await dockerForPushing.push(initOnce.imageUri); } - private async initOnce(): Promise { + private async initOnce(options: { quiet?: boolean } = {}): Promise { if (this.init) { return this.init; } const destination = await replaceAwsPlaceholders(this.asset.destination, this.host.aws); - const ecr = await this.host.aws.ecrClient(destination); + const ecr = await this.host.aws.ecrClient({ + ...destination, + quiet: options.quiet, + }); const account = async () => (await this.host.aws.discoverCurrentAccount())?.accountId; const repoUri = await repositoryUri(ecr, destination.repositoryName); diff --git a/packages/cdk-assets/lib/private/handlers/files.ts b/packages/cdk-assets/lib/private/handlers/files.ts index edc2addd61ada..fc538a82c95d0 100644 --- a/packages/cdk-assets/lib/private/handlers/files.ts +++ b/packages/cdk-assets/lib/private/handlers/files.ts @@ -33,7 +33,10 @@ export class FileAssetHandler implements IAssetHandler { const destination = await replaceAwsPlaceholders(this.asset.destination, this.host.aws); const s3Url = `s3://${destination.bucketName}/${destination.objectKey}`; try { - const s3 = await this.host.aws.s3Client(destination); + const s3 = await this.host.aws.s3Client({ + ...destination, + quiet: true, + }); this.host.emitMessage(EventType.CHECK, `Check ${s3Url}`); if (await objectExists(s3, destination.bucketName, destination.objectKey)) {