From b983212a6bf070ca2ca83537950e194d212c9cbe Mon Sep 17 00:00:00 2001 From: Francis Date: Mon, 4 Dec 2023 14:41:59 -0800 Subject: [PATCH 01/73] initial commit - framework, classes, constructors, modules Signed-off-by: Francis --- .../@aws-cdk/handler-framework/.eslintrc.js | 3 + .../@aws-cdk/handler-framework/.gitignore | 19 ++ .../@aws-cdk/handler-framework/.npmignore | 29 +++ packages/@aws-cdk/handler-framework/LICENSE | 202 ++++++++++++++++++ packages/@aws-cdk/handler-framework/NOTICE | 2 + .../@aws-cdk/handler-framework/jest.config.js | 18 ++ .../@aws-cdk/handler-framework/lib/classes.ts | 169 +++++++++++++++ .../handler-framework/lib/constructors.ts | 94 ++++++++ .../handler-framework/lib/framework.ts | 66 ++++++ .../@aws-cdk/handler-framework/lib/modules.ts | 42 ++++ .../@aws-cdk/handler-framework/package.json | 61 ++++++ .../handler-framework/tsconfig.dev.json | 23 ++ 12 files changed, 728 insertions(+) create mode 100644 packages/@aws-cdk/handler-framework/.eslintrc.js create mode 100644 packages/@aws-cdk/handler-framework/.gitignore create mode 100644 packages/@aws-cdk/handler-framework/.npmignore create mode 100644 packages/@aws-cdk/handler-framework/LICENSE create mode 100644 packages/@aws-cdk/handler-framework/NOTICE create mode 100644 packages/@aws-cdk/handler-framework/jest.config.js create mode 100644 packages/@aws-cdk/handler-framework/lib/classes.ts create mode 100644 packages/@aws-cdk/handler-framework/lib/constructors.ts create mode 100644 packages/@aws-cdk/handler-framework/lib/framework.ts create mode 100644 packages/@aws-cdk/handler-framework/lib/modules.ts create mode 100644 packages/@aws-cdk/handler-framework/package.json create mode 100644 packages/@aws-cdk/handler-framework/tsconfig.dev.json diff --git a/packages/@aws-cdk/handler-framework/.eslintrc.js b/packages/@aws-cdk/handler-framework/.eslintrc.js new file mode 100644 index 0000000000000..c045813e19b83 --- /dev/null +++ b/packages/@aws-cdk/handler-framework/.eslintrc.js @@ -0,0 +1,3 @@ +const baseConfig = require('@aws-cdk/cdk-build-tools/config/eslintrc'); +baseConfig.parserOptions.project = __dirname + '/tsconfig.json'; +module.exports = baseConfig; \ No newline at end of file diff --git a/packages/@aws-cdk/handler-framework/.gitignore b/packages/@aws-cdk/handler-framework/.gitignore new file mode 100644 index 0000000000000..d8a8561d50885 --- /dev/null +++ b/packages/@aws-cdk/handler-framework/.gitignore @@ -0,0 +1,19 @@ +*.js +*.js.map +*.d.ts +tsconfig.json +node_modules +*.generated.ts +dist +.jsii + +.LAST_BUILD +.nyc_output +coverage +nyc.config.js +.LAST_PACKAGE +*.snk +!.eslintrc.js +!jest.config.js + +junit.xml \ No newline at end of file diff --git a/packages/@aws-cdk/handler-framework/.npmignore b/packages/@aws-cdk/handler-framework/.npmignore new file mode 100644 index 0000000000000..5ddbf25e19336 --- /dev/null +++ b/packages/@aws-cdk/handler-framework/.npmignore @@ -0,0 +1,29 @@ +# Don't include original .ts files when doing `npm pack` +*.ts +!*.d.ts +coverage +.nyc_output +*.tgz + +dist +.LAST_PACKAGE +.LAST_BUILD +!*.js + +# Include .jsii +!.jsii + +*.snk + +*.tsbuildinfo + +tsconfig.json +.eslintrc.js +jest.config.js + +# exclude cdk artifacts +**/cdk.out +junit.xml +test/ +!*.lit.ts +**/*.snapshot \ No newline at end of file diff --git a/packages/@aws-cdk/handler-framework/LICENSE b/packages/@aws-cdk/handler-framework/LICENSE new file mode 100644 index 0000000000000..4e0a4651eaa41 --- /dev/null +++ b/packages/@aws-cdk/handler-framework/LICENSE @@ -0,0 +1,202 @@ + 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. + \ No newline at end of file diff --git a/packages/@aws-cdk/handler-framework/NOTICE b/packages/@aws-cdk/handler-framework/NOTICE new file mode 100644 index 0000000000000..a27b7dd317649 --- /dev/null +++ b/packages/@aws-cdk/handler-framework/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/handler-framework/jest.config.js b/packages/@aws-cdk/handler-framework/jest.config.js new file mode 100644 index 0000000000000..a5d279d7bf4d0 --- /dev/null +++ b/packages/@aws-cdk/handler-framework/jest.config.js @@ -0,0 +1,18 @@ +const baseConfig = require('@aws-cdk/cdk-build-tools/config/jest.config'); + +/** @type {import('ts-jest').JestConfigWithTsJest} */ +module.exports = { + ...baseConfig, + + // Different than usual + testMatch: [ + '/**/test/**/?(*.)+(test).ts', + ], + + coverageThreshold: { + global: { + branches: 35, + statements: 55, + }, + }, +}; diff --git a/packages/@aws-cdk/handler-framework/lib/classes.ts b/packages/@aws-cdk/handler-framework/lib/classes.ts new file mode 100644 index 0000000000000..98da63c974671 --- /dev/null +++ b/packages/@aws-cdk/handler-framework/lib/classes.ts @@ -0,0 +1,169 @@ +import { ClassType, IScope, stmt, expr, Type, StructType } from '@cdklabs/typewriter'; +import { CdkHandlerFrameworkConstructor } from './constructors'; +import { CONSTRUCTS_MODULE, CORE_MODULE, LAMBDA_MODULE } from './modules'; + +interface CdkHandlerClassOptions { + readonly entrypoint?: string; +} + +export interface CdkHandlerClassProps extends CdkHandlerClassOptions { + readonly className: string; + readonly codeDirectory: string; +} + +export abstract class CdkHandlerFrameworkClass extends ClassType { + /** + * Builds a CdkFunction class. + */ + public static buildCdkFunction(scope: IScope, props: CdkHandlerClassProps): CdkHandlerFrameworkClass { + return new (class CdkFunction extends CdkHandlerFrameworkClass { + public readonly codeDirectory: string; + public readonly entrypoint: string; + public readonly propsType: Type; + + public constructor() { + super(scope, { + name: props.className, + extends: LAMBDA_MODULE.Function, + }); + this.codeDirectory = props.codeDirectory; + this.entrypoint = props.entrypoint ?? 'index.handler'; + + const _props = new StructType(this, { + name: 'CdkFunctionProps', + extends: [LAMBDA_MODULE.FunctionOptions], + export: true, + }); + this.propsType = _props.type; + + CdkHandlerFrameworkConstructor.forCdkFunction(this); + } + })(); + } + + /** + * Builds a CdkSingletonFunction class. + */ + public static buildCdkSingletonFunction(scope: IScope, props: CdkHandlerClassProps): ClassType { + return new (class CdkSingletonFunction extends CdkHandlerFrameworkClass { + public readonly codeDirectory: string; + public readonly entrypoint: string; + public readonly propsType: Type; + + public constructor() { + super(scope, { + name: props.className, + extends: LAMBDA_MODULE.SingletonFunction, + }); + this.codeDirectory = props.codeDirectory; + this.entrypoint = props.entrypoint ?? 'index.handler'; + + const _props = new StructType(this, { + name: 'CdkSingletonFunctionProps', + extends: [LAMBDA_MODULE.FunctionOptions], + export: true, + }); + this.propsType = _props.type; + _props.addProperty({ + name: 'uuid', + type: Type.STRING, + }); + _props.addProperty({ + name: 'lambdaPurpose', + type: Type.STRING, + optional: true, + }); + + CdkHandlerFrameworkConstructor.forCdkSingletonFunction(this); + } + })(); + } + + /** + * Builds a CdkCustomResourceProvider class. + */ + public static buildCdkCustomResourceProvider(scope: IScope, props: CdkHandlerClassProps): ClassType { + return new (class CdkCustomResourceProvider extends CdkHandlerFrameworkClass { + public readonly codeDirectory: string; + public readonly entrypoint: string; + public readonly propsType: Type; + + public constructor() { + super(scope, { + name: props.className, + extends: CORE_MODULE.CustomResourceProviderBase, + }); + this.codeDirectory = props.codeDirectory; + this.entrypoint = props.entrypoint ?? 'index.handler'; + + const _props = new StructType(this, { + name: 'CdkCustomResourceProviderProps', + extends: [CORE_MODULE.CustomResourceProviderOptions], + export: true, + }); + this.propsType = _props.type; + + const getOrCreateMethod = this.addMethod({ + name: 'getOrCreate', + static: true, + returnType: Type.STRING, + }); + getOrCreateMethod.addParameter({ + name: 'scope', + type: CONSTRUCTS_MODULE.Construct, + }); + getOrCreateMethod.addParameter({ + name: 'uniqueid', + type: Type.STRING, + }); + getOrCreateMethod.addParameter({ + name: 'props', + type: this.propsType, + }); + getOrCreateMethod.addBody( + stmt.ret(expr.directCode('this.getOrCreateProvider(scope, uniqueid, props).serviceToken')), + ); + + const getOrCreateProviderMethod = this.addMethod({ + name: 'getOrCreateProvider', + static: true, + returnType: this.type, + }); + getOrCreateProviderMethod.addParameter({ + name: 'scope', + type: CONSTRUCTS_MODULE.Construct, + }); + getOrCreateProviderMethod.addParameter({ + name: 'uniqueid', + type: Type.STRING, + }); + getOrCreateProviderMethod.addParameter({ + name: 'props', + type: this.propsType, + }); + getOrCreateProviderMethod.addBody( + stmt.constVar(expr.ident('id'), expr.directCode('`${uniqueid}CustomResourceProvider`')), + stmt.constVar(expr.ident('stack'), expr.directCode('Stack.of(scope)')), + stmt.ret(expr.ident('provider')), + ); + + CdkHandlerFrameworkConstructor.forCdkCustomResourceProvider(this); + } + })(); + } + + /** + * + */ + public abstract readonly codeDirectory: string; + + /** + * + */ + public abstract readonly entrypoint: string; + + /** + * + */ + public abstract readonly propsType: Type; +} diff --git a/packages/@aws-cdk/handler-framework/lib/constructors.ts b/packages/@aws-cdk/handler-framework/lib/constructors.ts new file mode 100644 index 0000000000000..c258f2ded9557 --- /dev/null +++ b/packages/@aws-cdk/handler-framework/lib/constructors.ts @@ -0,0 +1,94 @@ +import { Expression, ParameterSpec, Type, stmt, expr, SuperInitializer, ObjectLiteral, Splat } from '@cdklabs/typewriter'; +import { CdkHandlerFrameworkClass } from './classes'; +import { CONSTRUCTS_MODULE } from './modules'; + +interface ConstructorOptions { + readonly constructorPropsParam?: ParameterSpec, + readonly superProps?: Expression, +} + +export class CdkHandlerFrameworkConstructor { + /** + * Builds a constructor for a CdkFunction class. + */ + public static forCdkFunction(_class: CdkHandlerFrameworkClass) { + const constructorPropsParam: ParameterSpec = { + name: 'props', + type: _class.propsType, + }; + const superProps = new ObjectLiteral([ + new Splat(expr.directCode('props')), + ['code', expr.directCode('cdkHandler.code')], + ['handler', expr.directCode('cdkHandler.entrypoint')], + ['runtime', expr.directCode('cdkHandler.runtime')], + ]); + CdkHandlerFrameworkConstructor.forCdkHandlerFrameworkClass(_class, { constructorPropsParam, superProps }); + } + + /** + * Builds a constructor for a CdkSingletonFunction class. + */ + public static forCdkSingletonFunction(_class: CdkHandlerFrameworkClass) { + CdkHandlerFrameworkConstructor.forCdkFunction(_class); + } + + /** + * Builds a constructor for a CdkCustomResourceProvider class. + */ + public static forCdkCustomResourceProvider(_class: CdkHandlerFrameworkClass) { + const constructorPropsParam: ParameterSpec = { + name: 'props', + type: _class.propsType, + }; + const superProps = new ObjectLiteral([ + new Splat(expr.directCode('props')), + ['codeDirectory', expr.directCode('cdkHandler.codeDirectory')], + ['runtimeName', expr.directCode('cdkHandler.runtime.name')], + ]); + CdkHandlerFrameworkConstructor.forCdkHandlerFrameworkClass(_class, { constructorPropsParam, superProps }); + } + + private static forCdkHandlerFrameworkClass(_class: CdkHandlerFrameworkClass, constructorOptions: ConstructorOptions = {}) { + // constructor + const init = _class.addInitializer({}); + const scope = init.addParameter({ + name: 'scope', + type: CONSTRUCTS_MODULE.Construct, + }); + const id = init.addParameter({ + name: 'id', + type: Type.STRING, + }); + if (constructorOptions.constructorPropsParam) { + init.addParameter(constructorOptions.constructorPropsParam); + } + + // build CdkHandler instance + init.addBody( + stmt.constVar( + expr.ident('cdkHandlerProps'), + expr.object({ + codeDirectory: expr.lit(`${_class.codeDirectory}`), + entrypoint: _class.entrypoint + ? expr.lit(`${_class.entrypoint}`) + : expr.lit('index.handler'), + }), + ), + ); + init.addBody( + stmt.constVar( + expr.ident('cdkHandler'), + expr.directCode("new CdkHandler(scope, 'Handler', cdkHandlerProps)"), + ), + ); + + // build super call + const superInitializerArgs: Expression[] = [scope, id]; + if (constructorOptions.superProps) { + superInitializerArgs.push(constructorOptions.superProps); + } + init.addBody(new SuperInitializer(...superInitializerArgs)); + } + + private constructor() {} +} diff --git a/packages/@aws-cdk/handler-framework/lib/framework.ts b/packages/@aws-cdk/handler-framework/lib/framework.ts new file mode 100644 index 0000000000000..0c1327015c4d0 --- /dev/null +++ b/packages/@aws-cdk/handler-framework/lib/framework.ts @@ -0,0 +1,66 @@ +import { Module, TypeScriptRenderer } from '@cdklabs/typewriter'; +import { CdkHandlerFrameworkClass, CdkHandlerClassProps } from './classes'; +import { CDK_HANDLER_MODULE, CONSTRUCTS_MODULE, LAMBDA_MODULE, CORE_MODULE } from './modules'; + +export interface CdkHandlerFrameworkProps extends CdkHandlerClassProps {} + +export abstract class CdkHandlerFramework { + /** + * Generates a renderable CdkFunction class. + */ + public static cdkFunction(props: CdkHandlerFrameworkProps): CdkHandlerFramework { + return new (class extends CdkHandlerFramework { + public constructor() { + const module = new Module('cdk-function'); + CONSTRUCTS_MODULE.importSelective(module, ['Construct']); + CDK_HANDLER_MODULE.importSelective(module, ['CdkHandler']); + LAMBDA_MODULE.importSelective(module, ['Function', 'FunctionOptions']); + CdkHandlerFrameworkClass.buildCdkFunction(module, props); + super(module); + } + })(); + } + + /** + * Generates a renderable CdkSingletonFunction class. + */ + public static cdkSingletonFunction(props: CdkHandlerFrameworkProps): CdkHandlerFramework { + return new (class extends CdkHandlerFramework { + public constructor() { + const module = new Module('cdk-singleton-function'); + CONSTRUCTS_MODULE.importSelective(module, ['Construct']); + CDK_HANDLER_MODULE.importSelective(module, ['CdkHandler']); + LAMBDA_MODULE.importSelective(module, ['SingletonFunction', 'FunctionOptions']); + CdkHandlerFrameworkClass.buildCdkSingletonFunction(module, props); + super(module); + } + })(); + } + + /** + * Generates a renderable CdkCustomResourceProvider class. + */ + public static cdkCustomResourceProvider(props: CdkHandlerFrameworkProps): CdkHandlerFramework { + return new (class extends CdkHandlerFramework { + public constructor() { + const module = new Module('cdk-custom-resource-provider'); + CONSTRUCTS_MODULE.importSelective(module, ['Construct']); + CDK_HANDLER_MODULE.importSelective(module, ['CdkHandler']); + CORE_MODULE.importSelective(module, ['CustomResourceProviderBase']); + CdkHandlerFrameworkClass.buildCdkCustomResourceProvider(module, props); + super(module); + } + })(); + } + + private readonly renderer = new TypeScriptRenderer(); + + protected constructor(private readonly module: Module) {} + + /** + * Render code for the framework. + */ + public render() { + return this.renderer.render(this.module); + }; +} diff --git a/packages/@aws-cdk/handler-framework/lib/modules.ts b/packages/@aws-cdk/handler-framework/lib/modules.ts new file mode 100644 index 0000000000000..476d9958f4539 --- /dev/null +++ b/packages/@aws-cdk/handler-framework/lib/modules.ts @@ -0,0 +1,42 @@ +import { ExternalModule, Type } from '@cdklabs/typewriter'; + +class ConstructsModule extends ExternalModule { + public readonly Construct = Type.fromName(this, 'Construct'); + public readonly IConstruct = Type.fromName(this, 'IConstruct'); + + public constructor() { + super('constructs'); + } +} + +class CdkHandlerModule extends ExternalModule { + public readonly CdkHandler = Type.fromName(this, 'CdkHandler'); + + public constructor() { + super('../../handler-framework/lib/cdk-handler'); + } +} + +class CoreModule extends ExternalModule { + public readonly CustomResourceProviderBase = Type.fromName(this, 'CustomResourceProviderBase'); + public readonly CustomResourceProviderOptions = Type.fromName(this, 'CustomResourceProviderOptions'); + + public constructor() { + super('../../core'); + } +} + +class LambdaModule extends ExternalModule { + public readonly Function = Type.fromName(this, 'Function'); + public readonly SingletonFunction = Type.fromName(this, 'SingletonFunction'); + public readonly FunctionOptions = Type.fromName(this, 'FunctionOptions'); + + public constructor() { + super('../../aws-lambda'); + } +} + +export const CONSTRUCTS_MODULE = new ConstructsModule(); +export const CDK_HANDLER_MODULE = new CdkHandlerModule(); +export const CORE_MODULE = new CoreModule(); +export const LAMBDA_MODULE = new LambdaModule(); diff --git a/packages/@aws-cdk/handler-framework/package.json b/packages/@aws-cdk/handler-framework/package.json new file mode 100644 index 0000000000000..bc0f96b550e28 --- /dev/null +++ b/packages/@aws-cdk/handler-framework/package.json @@ -0,0 +1,61 @@ +{ + "name": "@aws-cdk/cdk-handler-framework", + "version": "0.0.0", + "private": true, + "description": "CDK Vended Handler Framework", + "scripts": { + "build": "cdk-build", + "integ": "integ-runner", + "lint": "cdk-lint", + "package": "cdk-package", + "awslint": "cdk-awslint", + "pkglint": "pkglint -f", + "test": "cdk-test", + "watch": "cdk-watch", + "build+test": "yarn build && yarn test", + "build+test+package": "yarn build+test && yarn package", + "compat": "cdk-compat", + "build+extract": "yarn build", + "build+test+extract": "yarn build+test" + }, + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com", + "organization": true + }, + "license": "Apache-2.0", + "devDependencies": { + "@aws-cdk/cdk-build-tools": "0.0.0", + "@aws-cdk/pkglint": "0.0.0", + "jest": "^29.7.0" + }, + "dependencies": { + "@cdklabs/typewriter": "^0.0.3", + "fs-extra": "^11.2.0" + }, + "peerDependencies": { + "aws-cdk-lib": "^0.0.0", + "constructs": "^10.0.0" + }, + "repository": { + "url": "https://github.com/aws/aws-cdk.git", + "type": "git", + "directory": "packages/@aws-cdk/cdk-handler-framework" + }, + "keywords": [ + "aws", + "cdk" + ], + "homepage": "https://github.com/aws/aws-cdk", + "engines": { + "node": ">= 14.15.0" + }, + "cdk-package": { + "shrinkWrap": true + }, + "stability": "experimental", + "maturity": "experimental", + "publishConfig": { + "tag": "latest" + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/handler-framework/tsconfig.dev.json b/packages/@aws-cdk/handler-framework/tsconfig.dev.json new file mode 100644 index 0000000000000..604b91d0c16f2 --- /dev/null +++ b/packages/@aws-cdk/handler-framework/tsconfig.dev.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": ["es2020", "dom"], + "strict": true, + "alwaysStrict": true, + "declaration": true, + "inlineSourceMap": true, + "inlineSources": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "resolveJsonModule": true, + "composite": true, + "incremental": true, + }, + "include": [ + "**/*.ts", + "**/*.d.ts", + ], +} From 7145c7aebcdbdb6c2bca7fcac1c71a520e2a337a Mon Sep 17 00:00:00 2001 From: Francis Date: Mon, 4 Dec 2023 15:48:25 -0800 Subject: [PATCH 02/73] refactor Signed-off-by: Francis --- .../@aws-cdk/handler-framework/lib/classes.ts | 37 ++++++---- .../handler-framework/lib/constructors.ts | 39 +++-------- .../handler-framework/lib/framework.ts | 70 ++++++------------- 3 files changed, 55 insertions(+), 91 deletions(-) diff --git a/packages/@aws-cdk/handler-framework/lib/classes.ts b/packages/@aws-cdk/handler-framework/lib/classes.ts index 98da63c974671..c0fc7be248668 100644 --- a/packages/@aws-cdk/handler-framework/lib/classes.ts +++ b/packages/@aws-cdk/handler-framework/lib/classes.ts @@ -1,34 +1,35 @@ -import { ClassType, IScope, stmt, expr, Type, StructType } from '@cdklabs/typewriter'; +import { Module, ClassType, stmt, expr, Type, StructType } from '@cdklabs/typewriter'; import { CdkHandlerFrameworkConstructor } from './constructors'; -import { CONSTRUCTS_MODULE, CORE_MODULE, LAMBDA_MODULE } from './modules'; +import { CDK_HANDLER_MODULE, CONSTRUCTS_MODULE, LAMBDA_MODULE, CORE_MODULE } from './modules'; -interface CdkHandlerClassOptions { - readonly entrypoint?: string; -} - -export interface CdkHandlerClassProps extends CdkHandlerClassOptions { +export interface CdkHandlerClassProps { readonly className: string; readonly codeDirectory: string; + readonly entrypoint?: string; } export abstract class CdkHandlerFrameworkClass extends ClassType { /** * Builds a CdkFunction class. */ - public static buildCdkFunction(scope: IScope, props: CdkHandlerClassProps): CdkHandlerFrameworkClass { + public static buildCdkFunction(module: Module, props: CdkHandlerClassProps): CdkHandlerFrameworkClass { return new (class CdkFunction extends CdkHandlerFrameworkClass { public readonly codeDirectory: string; public readonly entrypoint: string; public readonly propsType: Type; public constructor() { - super(scope, { + super(module, { name: props.className, extends: LAMBDA_MODULE.Function, }); this.codeDirectory = props.codeDirectory; this.entrypoint = props.entrypoint ?? 'index.handler'; + CONSTRUCTS_MODULE.importSelective(module, ['Constructs']); + CDK_HANDLER_MODULE.importSelective(module, ['CdkHandler']); + LAMBDA_MODULE.importSelective(module, ['Function']); + const _props = new StructType(this, { name: 'CdkFunctionProps', extends: [LAMBDA_MODULE.FunctionOptions], @@ -44,20 +45,24 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { /** * Builds a CdkSingletonFunction class. */ - public static buildCdkSingletonFunction(scope: IScope, props: CdkHandlerClassProps): ClassType { + public static buildCdkSingletonFunction(module: Module, props: CdkHandlerClassProps): ClassType { return new (class CdkSingletonFunction extends CdkHandlerFrameworkClass { public readonly codeDirectory: string; public readonly entrypoint: string; public readonly propsType: Type; public constructor() { - super(scope, { + super(module, { name: props.className, extends: LAMBDA_MODULE.SingletonFunction, }); this.codeDirectory = props.codeDirectory; this.entrypoint = props.entrypoint ?? 'index.handler'; + CONSTRUCTS_MODULE.importSelective(module, ['Constructs']); + CDK_HANDLER_MODULE.importSelective(module, ['CdkHandler']); + LAMBDA_MODULE.importSelective(module, ['SingletonFunction']); + const _props = new StructType(this, { name: 'CdkSingletonFunctionProps', extends: [LAMBDA_MODULE.FunctionOptions], @@ -74,7 +79,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { optional: true, }); - CdkHandlerFrameworkConstructor.forCdkSingletonFunction(this); + CdkHandlerFrameworkConstructor.forCdkFunction(this); } })(); } @@ -82,20 +87,24 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { /** * Builds a CdkCustomResourceProvider class. */ - public static buildCdkCustomResourceProvider(scope: IScope, props: CdkHandlerClassProps): ClassType { + public static buildCdkCustomResourceProvider(module: Module, props: CdkHandlerClassProps): ClassType { return new (class CdkCustomResourceProvider extends CdkHandlerFrameworkClass { public readonly codeDirectory: string; public readonly entrypoint: string; public readonly propsType: Type; public constructor() { - super(scope, { + super(module, { name: props.className, extends: CORE_MODULE.CustomResourceProviderBase, }); this.codeDirectory = props.codeDirectory; this.entrypoint = props.entrypoint ?? 'index.handler'; + CONSTRUCTS_MODULE.importSelective(module, ['Constructs']); + CDK_HANDLER_MODULE.importSelective(module, ['CdkHandler']); + CORE_MODULE.importSelective(module, ['CustomResourceProviderBase']); + const _props = new StructType(this, { name: 'CdkCustomResourceProviderProps', extends: [CORE_MODULE.CustomResourceProviderOptions], diff --git a/packages/@aws-cdk/handler-framework/lib/constructors.ts b/packages/@aws-cdk/handler-framework/lib/constructors.ts index c258f2ded9557..116479cda342c 100644 --- a/packages/@aws-cdk/handler-framework/lib/constructors.ts +++ b/packages/@aws-cdk/handler-framework/lib/constructors.ts @@ -1,54 +1,34 @@ -import { Expression, ParameterSpec, Type, stmt, expr, SuperInitializer, ObjectLiteral, Splat } from '@cdklabs/typewriter'; +import { Expression, Type, stmt, expr, SuperInitializer, ObjectLiteral, Splat } from '@cdklabs/typewriter'; import { CdkHandlerFrameworkClass } from './classes'; import { CONSTRUCTS_MODULE } from './modules'; -interface ConstructorOptions { - readonly constructorPropsParam?: ParameterSpec, - readonly superProps?: Expression, -} - export class CdkHandlerFrameworkConstructor { /** * Builds a constructor for a CdkFunction class. */ public static forCdkFunction(_class: CdkHandlerFrameworkClass) { - const constructorPropsParam: ParameterSpec = { - name: 'props', - type: _class.propsType, - }; const superProps = new ObjectLiteral([ new Splat(expr.directCode('props')), ['code', expr.directCode('cdkHandler.code')], ['handler', expr.directCode('cdkHandler.entrypoint')], ['runtime', expr.directCode('cdkHandler.runtime')], ]); - CdkHandlerFrameworkConstructor.forCdkHandlerFrameworkClass(_class, { constructorPropsParam, superProps }); - } - - /** - * Builds a constructor for a CdkSingletonFunction class. - */ - public static forCdkSingletonFunction(_class: CdkHandlerFrameworkClass) { - CdkHandlerFrameworkConstructor.forCdkFunction(_class); + CdkHandlerFrameworkConstructor.forClass(_class, superProps); } /** * Builds a constructor for a CdkCustomResourceProvider class. */ public static forCdkCustomResourceProvider(_class: CdkHandlerFrameworkClass) { - const constructorPropsParam: ParameterSpec = { - name: 'props', - type: _class.propsType, - }; const superProps = new ObjectLiteral([ new Splat(expr.directCode('props')), ['codeDirectory', expr.directCode('cdkHandler.codeDirectory')], ['runtimeName', expr.directCode('cdkHandler.runtime.name')], ]); - CdkHandlerFrameworkConstructor.forCdkHandlerFrameworkClass(_class, { constructorPropsParam, superProps }); + CdkHandlerFrameworkConstructor.forClass(_class, superProps); } - private static forCdkHandlerFrameworkClass(_class: CdkHandlerFrameworkClass, constructorOptions: ConstructorOptions = {}) { + private static forClass(_class: CdkHandlerFrameworkClass, superProps?: Expression) { // constructor const init = _class.addInitializer({}); const scope = init.addParameter({ @@ -59,9 +39,10 @@ export class CdkHandlerFrameworkConstructor { name: 'id', type: Type.STRING, }); - if (constructorOptions.constructorPropsParam) { - init.addParameter(constructorOptions.constructorPropsParam); - } + init.addParameter({ + name: 'props', + type: _class.propsType, + }); // build CdkHandler instance init.addBody( @@ -84,8 +65,8 @@ export class CdkHandlerFrameworkConstructor { // build super call const superInitializerArgs: Expression[] = [scope, id]; - if (constructorOptions.superProps) { - superInitializerArgs.push(constructorOptions.superProps); + if (superProps) { + superInitializerArgs.push(superProps); } init.addBody(new SuperInitializer(...superInitializerArgs)); } diff --git a/packages/@aws-cdk/handler-framework/lib/framework.ts b/packages/@aws-cdk/handler-framework/lib/framework.ts index 0c1327015c4d0..b29fdce0b7af4 100644 --- a/packages/@aws-cdk/handler-framework/lib/framework.ts +++ b/packages/@aws-cdk/handler-framework/lib/framework.ts @@ -1,66 +1,40 @@ import { Module, TypeScriptRenderer } from '@cdklabs/typewriter'; +import * as fs from 'fs-extra'; import { CdkHandlerFrameworkClass, CdkHandlerClassProps } from './classes'; -import { CDK_HANDLER_MODULE, CONSTRUCTS_MODULE, LAMBDA_MODULE, CORE_MODULE } from './modules'; -export interface CdkHandlerFrameworkProps extends CdkHandlerClassProps {} +export interface CdkHandlerFrameworkProps extends CdkHandlerClassProps { + readonly outfileLocation: string; +} -export abstract class CdkHandlerFramework { +export class CdkHandlerFramework { /** - * Generates a renderable CdkFunction class. + * Generates a CdkFunction class. */ - public static cdkFunction(props: CdkHandlerFrameworkProps): CdkHandlerFramework { - return new (class extends CdkHandlerFramework { - public constructor() { - const module = new Module('cdk-function'); - CONSTRUCTS_MODULE.importSelective(module, ['Construct']); - CDK_HANDLER_MODULE.importSelective(module, ['CdkHandler']); - LAMBDA_MODULE.importSelective(module, ['Function', 'FunctionOptions']); - CdkHandlerFrameworkClass.buildCdkFunction(module, props); - super(module); - } - })(); + public static generateCdkFunction(props: CdkHandlerFrameworkProps) { + const module = new Module('cdk-function'); + CdkHandlerFrameworkClass.buildCdkFunction(module, props); + fs.outputFileSync(`${props.outfileLocation}/index.ts`, CdkHandlerFramework.renderer.render(module)); } /** - * Generates a renderable CdkSingletonFunction class. + * Generates a CdkSingletonFunction class. */ - public static cdkSingletonFunction(props: CdkHandlerFrameworkProps): CdkHandlerFramework { - return new (class extends CdkHandlerFramework { - public constructor() { - const module = new Module('cdk-singleton-function'); - CONSTRUCTS_MODULE.importSelective(module, ['Construct']); - CDK_HANDLER_MODULE.importSelective(module, ['CdkHandler']); - LAMBDA_MODULE.importSelective(module, ['SingletonFunction', 'FunctionOptions']); - CdkHandlerFrameworkClass.buildCdkSingletonFunction(module, props); - super(module); - } - })(); + public static generateCdkSingletonFunction(props: CdkHandlerFrameworkProps) { + const module = new Module('cdk-singleton-function'); + CdkHandlerFrameworkClass.buildCdkSingletonFunction(module, props); + fs.outputFileSync(`${props.outfileLocation}/index.ts`, CdkHandlerFramework.renderer.render(module)); } /** - * Generates a renderable CdkCustomResourceProvider class. + * Generates a CdkCustomResourceProvider class. */ - public static cdkCustomResourceProvider(props: CdkHandlerFrameworkProps): CdkHandlerFramework { - return new (class extends CdkHandlerFramework { - public constructor() { - const module = new Module('cdk-custom-resource-provider'); - CONSTRUCTS_MODULE.importSelective(module, ['Construct']); - CDK_HANDLER_MODULE.importSelective(module, ['CdkHandler']); - CORE_MODULE.importSelective(module, ['CustomResourceProviderBase']); - CdkHandlerFrameworkClass.buildCdkCustomResourceProvider(module, props); - super(module); - } - })(); + public static generateCdkCustomResourceProvider(props: CdkHandlerFrameworkProps) { + const module = new Module('cdk-custom-resource-provider'); + CdkHandlerFrameworkClass.buildCdkCustomResourceProvider(module, props); + fs.outputFileSync(`${props.outfileLocation}/index.ts`, CdkHandlerFramework.renderer.render(module)); } - private readonly renderer = new TypeScriptRenderer(); - - protected constructor(private readonly module: Module) {} + private static readonly renderer = new TypeScriptRenderer(); - /** - * Render code for the framework. - */ - public render() { - return this.renderer.render(this.module); - }; + private constructor() {} } From dd2ff429fd36045997449c7048441cfe0094d131 Mon Sep 17 00:00:00 2001 From: Francis Date: Mon, 4 Dec 2023 18:10:35 -0800 Subject: [PATCH 03/73] refactor and bug fixes Signed-off-by: Francis --- .../@aws-cdk/handler-framework/.eslintrc.js | 15 ++- packages/@aws-cdk/handler-framework/LICENSE | 1 - packages/@aws-cdk/handler-framework/NOTICE | 2 +- .../@aws-cdk/handler-framework/jest.config.js | 18 +-- .../@aws-cdk/handler-framework/lib/classes.ts | 106 +++++++----------- .../handler-framework/lib/constructors.ts | 17 +-- .../handler-framework/lib/framework.ts | 20 ++-- .../@aws-cdk/handler-framework/lib/modules.ts | 1 + .../@aws-cdk/handler-framework/package.json | 4 +- 9 files changed, 80 insertions(+), 104 deletions(-) diff --git a/packages/@aws-cdk/handler-framework/.eslintrc.js b/packages/@aws-cdk/handler-framework/.eslintrc.js index c045813e19b83..0adc65405c74e 100644 --- a/packages/@aws-cdk/handler-framework/.eslintrc.js +++ b/packages/@aws-cdk/handler-framework/.eslintrc.js @@ -1,3 +1,16 @@ const baseConfig = require('@aws-cdk/cdk-build-tools/config/eslintrc'); -baseConfig.parserOptions.project = __dirname + '/tsconfig.json'; +baseConfig.parserOptions.project = __dirname + '/tsconfig.dev.json'; +baseConfig.rules['import/no-extraneous-dependencies'] = [ + 'error', + { + devDependencies: [ + '**/build-tools/**', + '**/scripts/**', + '**/test/**', + ], + optionalDependencies: false, + peerDependencies: true, + } +]; + module.exports = baseConfig; \ No newline at end of file diff --git a/packages/@aws-cdk/handler-framework/LICENSE b/packages/@aws-cdk/handler-framework/LICENSE index 4e0a4651eaa41..9b722c65c5481 100644 --- a/packages/@aws-cdk/handler-framework/LICENSE +++ b/packages/@aws-cdk/handler-framework/LICENSE @@ -199,4 +199,3 @@ 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. - \ No newline at end of file diff --git a/packages/@aws-cdk/handler-framework/NOTICE b/packages/@aws-cdk/handler-framework/NOTICE index a27b7dd317649..5efb94182cd88 100644 --- a/packages/@aws-cdk/handler-framework/NOTICE +++ b/packages/@aws-cdk/handler-framework/NOTICE @@ -1,2 +1,2 @@ AWS Cloud Development Kit (AWS CDK) -Copyright 2018-2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +Copyright 2018-2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. \ No newline at end of file diff --git a/packages/@aws-cdk/handler-framework/jest.config.js b/packages/@aws-cdk/handler-framework/jest.config.js index a5d279d7bf4d0..3a2fd93a1228a 100644 --- a/packages/@aws-cdk/handler-framework/jest.config.js +++ b/packages/@aws-cdk/handler-framework/jest.config.js @@ -1,18 +1,2 @@ const baseConfig = require('@aws-cdk/cdk-build-tools/config/jest.config'); - -/** @type {import('ts-jest').JestConfigWithTsJest} */ -module.exports = { - ...baseConfig, - - // Different than usual - testMatch: [ - '/**/test/**/?(*.)+(test).ts', - ], - - coverageThreshold: { - global: { - branches: 35, - statements: 55, - }, - }, -}; +module.exports = baseConfig; diff --git a/packages/@aws-cdk/handler-framework/lib/classes.ts b/packages/@aws-cdk/handler-framework/lib/classes.ts index c0fc7be248668..4d76683b15a6d 100644 --- a/packages/@aws-cdk/handler-framework/lib/classes.ts +++ b/packages/@aws-cdk/handler-framework/lib/classes.ts @@ -1,10 +1,27 @@ -import { Module, ClassType, stmt, expr, Type, StructType } from '@cdklabs/typewriter'; +import { Module, ClassType, stmt, expr, Type } from '@cdklabs/typewriter'; import { CdkHandlerFrameworkConstructor } from './constructors'; import { CDK_HANDLER_MODULE, CONSTRUCTS_MODULE, LAMBDA_MODULE, CORE_MODULE } from './modules'; +/** + * Initialization properties used to build a `CdkHandlerFrameworkClass`. + */ export interface CdkHandlerClassProps { + /** + * The name of the class. + */ readonly className: string; + + /** + * A local file system directory with the provider's code. The code will be + * bundled into a zip asset and wired to the provider's AWS Lambda function. + */ readonly codeDirectory: string; + + /** + * The name of the method within your code that Lambda calls to execute your function. + * + * @default 'index.handler' + */ readonly entrypoint?: string; } @@ -12,30 +29,22 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { /** * Builds a CdkFunction class. */ - public static buildCdkFunction(module: Module, props: CdkHandlerClassProps): CdkHandlerFrameworkClass { - return new (class CdkFunction extends CdkHandlerFrameworkClass { + public static buildCdkFunction(scope: Module, props: CdkHandlerClassProps) { + new (class CdkFunction extends CdkHandlerFrameworkClass { public readonly codeDirectory: string; public readonly entrypoint: string; - public readonly propsType: Type; public constructor() { - super(module, { + super(scope, { name: props.className, extends: LAMBDA_MODULE.Function, }); this.codeDirectory = props.codeDirectory; this.entrypoint = props.entrypoint ?? 'index.handler'; - CONSTRUCTS_MODULE.importSelective(module, ['Constructs']); - CDK_HANDLER_MODULE.importSelective(module, ['CdkHandler']); - LAMBDA_MODULE.importSelective(module, ['Function']); - - const _props = new StructType(this, { - name: 'CdkFunctionProps', - extends: [LAMBDA_MODULE.FunctionOptions], - export: true, - }); - this.propsType = _props.type; + CONSTRUCTS_MODULE.importSelective(scope, ['Construct']); + CDK_HANDLER_MODULE.importSelective(scope, ['CdkHandlerProps', 'CdkHandler']); + LAMBDA_MODULE.importSelective(scope, ['Function']); CdkHandlerFrameworkConstructor.forCdkFunction(this); } @@ -45,39 +54,22 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { /** * Builds a CdkSingletonFunction class. */ - public static buildCdkSingletonFunction(module: Module, props: CdkHandlerClassProps): ClassType { - return new (class CdkSingletonFunction extends CdkHandlerFrameworkClass { + public static buildCdkSingletonFunction(scope: Module, props: CdkHandlerClassProps) { + new (class CdkSingletonFunction extends CdkHandlerFrameworkClass { public readonly codeDirectory: string; public readonly entrypoint: string; - public readonly propsType: Type; public constructor() { - super(module, { + super(scope, { name: props.className, extends: LAMBDA_MODULE.SingletonFunction, }); this.codeDirectory = props.codeDirectory; this.entrypoint = props.entrypoint ?? 'index.handler'; - CONSTRUCTS_MODULE.importSelective(module, ['Constructs']); - CDK_HANDLER_MODULE.importSelective(module, ['CdkHandler']); - LAMBDA_MODULE.importSelective(module, ['SingletonFunction']); - - const _props = new StructType(this, { - name: 'CdkSingletonFunctionProps', - extends: [LAMBDA_MODULE.FunctionOptions], - export: true, - }); - this.propsType = _props.type; - _props.addProperty({ - name: 'uuid', - type: Type.STRING, - }); - _props.addProperty({ - name: 'lambdaPurpose', - type: Type.STRING, - optional: true, - }); + CONSTRUCTS_MODULE.importSelective(scope, ['Construct']); + CDK_HANDLER_MODULE.importSelective(scope, ['CdkHandlerProps', 'CdkHandler']); + LAMBDA_MODULE.importSelective(scope, ['SingletonFunction']); CdkHandlerFrameworkConstructor.forCdkFunction(this); } @@ -87,30 +79,22 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { /** * Builds a CdkCustomResourceProvider class. */ - public static buildCdkCustomResourceProvider(module: Module, props: CdkHandlerClassProps): ClassType { - return new (class CdkCustomResourceProvider extends CdkHandlerFrameworkClass { + public static buildCdkCustomResourceProvider(scope: Module, props: CdkHandlerClassProps) { + new (class CdkCustomResourceProvider extends CdkHandlerFrameworkClass { public readonly codeDirectory: string; public readonly entrypoint: string; - public readonly propsType: Type; public constructor() { - super(module, { + super(scope, { name: props.className, extends: CORE_MODULE.CustomResourceProviderBase, }); this.codeDirectory = props.codeDirectory; this.entrypoint = props.entrypoint ?? 'index.handler'; - CONSTRUCTS_MODULE.importSelective(module, ['Constructs']); - CDK_HANDLER_MODULE.importSelective(module, ['CdkHandler']); - CORE_MODULE.importSelective(module, ['CustomResourceProviderBase']); - - const _props = new StructType(this, { - name: 'CdkCustomResourceProviderProps', - extends: [CORE_MODULE.CustomResourceProviderOptions], - export: true, - }); - this.propsType = _props.type; + CONSTRUCTS_MODULE.importSelective(scope, ['Construct']); + CDK_HANDLER_MODULE.importSelective(scope, ['CdkHandlerProps', 'CdkHandler']); + CORE_MODULE.importSelective(scope, ['CustomResourceProviderBase']); const getOrCreateMethod = this.addMethod({ name: 'getOrCreate', @@ -125,12 +109,8 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { name: 'uniqueid', type: Type.STRING, }); - getOrCreateMethod.addParameter({ - name: 'props', - type: this.propsType, - }); getOrCreateMethod.addBody( - stmt.ret(expr.directCode('this.getOrCreateProvider(scope, uniqueid, props).serviceToken')), + stmt.ret(expr.directCode('this.getOrCreateProvider(scope, uniqueid).serviceToken')), ); const getOrCreateProviderMethod = this.addMethod({ @@ -146,14 +126,11 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { name: 'uniqueid', type: Type.STRING, }); - getOrCreateProviderMethod.addParameter({ - name: 'props', - type: this.propsType, - }); getOrCreateProviderMethod.addBody( stmt.constVar(expr.ident('id'), expr.directCode('`${uniqueid}CustomResourceProvider`')), stmt.constVar(expr.ident('stack'), expr.directCode('Stack.of(scope)')), - stmt.ret(expr.ident('provider')), + stmt.constVar(expr.ident('existing'), expr.directCode(`stack.node.tryFindChild(id) as ${this.type}`)), + stmt.ret(expr.directCode(`existing ?? new ${this.name}(scope, id)`)), ); CdkHandlerFrameworkConstructor.forCdkCustomResourceProvider(this); @@ -170,9 +147,4 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { * */ public abstract readonly entrypoint: string; - - /** - * - */ - public abstract readonly propsType: Type; } diff --git a/packages/@aws-cdk/handler-framework/lib/constructors.ts b/packages/@aws-cdk/handler-framework/lib/constructors.ts index 116479cda342c..e555ac634fd80 100644 --- a/packages/@aws-cdk/handler-framework/lib/constructors.ts +++ b/packages/@aws-cdk/handler-framework/lib/constructors.ts @@ -1,4 +1,4 @@ -import { Expression, Type, stmt, expr, SuperInitializer, ObjectLiteral, Splat } from '@cdklabs/typewriter'; +import { Expression, Type, stmt, expr, SuperInitializer, ObjectLiteral } from '@cdklabs/typewriter'; import { CdkHandlerFrameworkClass } from './classes'; import { CONSTRUCTS_MODULE } from './modules'; @@ -8,7 +8,6 @@ export class CdkHandlerFrameworkConstructor { */ public static forCdkFunction(_class: CdkHandlerFrameworkClass) { const superProps = new ObjectLiteral([ - new Splat(expr.directCode('props')), ['code', expr.directCode('cdkHandler.code')], ['handler', expr.directCode('cdkHandler.entrypoint')], ['runtime', expr.directCode('cdkHandler.runtime')], @@ -16,12 +15,18 @@ export class CdkHandlerFrameworkConstructor { CdkHandlerFrameworkConstructor.forClass(_class, superProps); } + /** + * Builds a constructor for a CdkSingletonFunction class. + */ + public static forCdkSingletonFunction(_class: CdkHandlerFrameworkClass) { + CdkHandlerFrameworkConstructor.forCdkFunction(_class); + } + /** * Builds a constructor for a CdkCustomResourceProvider class. */ public static forCdkCustomResourceProvider(_class: CdkHandlerFrameworkClass) { const superProps = new ObjectLiteral([ - new Splat(expr.directCode('props')), ['codeDirectory', expr.directCode('cdkHandler.codeDirectory')], ['runtimeName', expr.directCode('cdkHandler.runtime.name')], ]); @@ -39,15 +44,11 @@ export class CdkHandlerFrameworkConstructor { name: 'id', type: Type.STRING, }); - init.addParameter({ - name: 'props', - type: _class.propsType, - }); // build CdkHandler instance init.addBody( stmt.constVar( - expr.ident('cdkHandlerProps'), + expr.directCode('cdkHandlerProps: CdkHandlerProps'), expr.object({ codeDirectory: expr.lit(`${_class.codeDirectory}`), entrypoint: _class.entrypoint diff --git a/packages/@aws-cdk/handler-framework/lib/framework.ts b/packages/@aws-cdk/handler-framework/lib/framework.ts index b29fdce0b7af4..c0f46ad826b75 100644 --- a/packages/@aws-cdk/handler-framework/lib/framework.ts +++ b/packages/@aws-cdk/handler-framework/lib/framework.ts @@ -2,36 +2,42 @@ import { Module, TypeScriptRenderer } from '@cdklabs/typewriter'; import * as fs from 'fs-extra'; import { CdkHandlerFrameworkClass, CdkHandlerClassProps } from './classes'; +/** + * Initialization properties for generating `CdkHandlerFramework` components. + */ export interface CdkHandlerFrameworkProps extends CdkHandlerClassProps { - readonly outfileLocation: string; + /** + * The location to render the output file to. + */ + readonly outputFileLocation: string; } export class CdkHandlerFramework { /** - * Generates a CdkFunction class. + * Generates a CdkFunction class and renders it to a specified output file location. */ public static generateCdkFunction(props: CdkHandlerFrameworkProps) { const module = new Module('cdk-function'); CdkHandlerFrameworkClass.buildCdkFunction(module, props); - fs.outputFileSync(`${props.outfileLocation}/index.ts`, CdkHandlerFramework.renderer.render(module)); + fs.outputFileSync(`${props.outputFileLocation}/index.ts`, CdkHandlerFramework.renderer.render(module)); } /** - * Generates a CdkSingletonFunction class. + * Generates a CdkSingletonFunction class and renders it to a specified output file location. */ public static generateCdkSingletonFunction(props: CdkHandlerFrameworkProps) { const module = new Module('cdk-singleton-function'); CdkHandlerFrameworkClass.buildCdkSingletonFunction(module, props); - fs.outputFileSync(`${props.outfileLocation}/index.ts`, CdkHandlerFramework.renderer.render(module)); + fs.outputFileSync(`${props.outputFileLocation}/index.ts`, CdkHandlerFramework.renderer.render(module)); } /** - * Generates a CdkCustomResourceProvider class. + * Generates a CdkCustomResourceProvider class and renders it to a specified output file location. */ public static generateCdkCustomResourceProvider(props: CdkHandlerFrameworkProps) { const module = new Module('cdk-custom-resource-provider'); CdkHandlerFrameworkClass.buildCdkCustomResourceProvider(module, props); - fs.outputFileSync(`${props.outfileLocation}/index.ts`, CdkHandlerFramework.renderer.render(module)); + fs.outputFileSync(`${props.outputFileLocation}/index.ts`, CdkHandlerFramework.renderer.render(module)); } private static readonly renderer = new TypeScriptRenderer(); diff --git a/packages/@aws-cdk/handler-framework/lib/modules.ts b/packages/@aws-cdk/handler-framework/lib/modules.ts index 476d9958f4539..d9aa43fff94c5 100644 --- a/packages/@aws-cdk/handler-framework/lib/modules.ts +++ b/packages/@aws-cdk/handler-framework/lib/modules.ts @@ -11,6 +11,7 @@ class ConstructsModule extends ExternalModule { class CdkHandlerModule extends ExternalModule { public readonly CdkHandler = Type.fromName(this, 'CdkHandler'); + public readonly CdkHandlerProps = Type.fromName(this, 'CdkHandlerProps'); public constructor() { super('../../handler-framework/lib/cdk-handler'); diff --git a/packages/@aws-cdk/handler-framework/package.json b/packages/@aws-cdk/handler-framework/package.json index bc0f96b550e28..a3562ce8b8694 100644 --- a/packages/@aws-cdk/handler-framework/package.json +++ b/packages/@aws-cdk/handler-framework/package.json @@ -1,5 +1,5 @@ { - "name": "@aws-cdk/cdk-handler-framework", + "name": "@aws-cdk/handler-framework", "version": "0.0.0", "private": true, "description": "CDK Vended Handler Framework", @@ -40,7 +40,7 @@ "repository": { "url": "https://github.com/aws/aws-cdk.git", "type": "git", - "directory": "packages/@aws-cdk/cdk-handler-framework" + "directory": "packages/@aws-cdk/handler-framework" }, "keywords": [ "aws", From f7b459392f1362d5e5969b5a956467266b7db101 Mon Sep 17 00:00:00 2001 From: Francis Date: Mon, 4 Dec 2023 19:06:06 -0800 Subject: [PATCH 04/73] initial setup for config file Signed-off-by: Francis --- .../@aws-cdk/handler-framework/lib/classes.ts | 19 ++-- .../@aws-cdk/handler-framework/lib/config.ts | 94 +++++++++++++++++++ 2 files changed, 107 insertions(+), 6 deletions(-) create mode 100644 packages/@aws-cdk/handler-framework/lib/config.ts diff --git a/packages/@aws-cdk/handler-framework/lib/classes.ts b/packages/@aws-cdk/handler-framework/lib/classes.ts index 4d76683b15a6d..f664c71383466 100644 --- a/packages/@aws-cdk/handler-framework/lib/classes.ts +++ b/packages/@aws-cdk/handler-framework/lib/classes.ts @@ -3,7 +3,7 @@ import { CdkHandlerFrameworkConstructor } from './constructors'; import { CDK_HANDLER_MODULE, CONSTRUCTS_MODULE, LAMBDA_MODULE, CORE_MODULE } from './modules'; /** - * Initialization properties used to build a `CdkHandlerFrameworkClass`. + * Initialization properties used to build a `CdkHandlerFrameworkClass` instance. */ export interface CdkHandlerClassProps { /** @@ -27,7 +27,7 @@ export interface CdkHandlerClassProps { export abstract class CdkHandlerFrameworkClass extends ClassType { /** - * Builds a CdkFunction class. + * Builds a `CdkFunction` class. */ public static buildCdkFunction(scope: Module, props: CdkHandlerClassProps) { new (class CdkFunction extends CdkHandlerFrameworkClass { @@ -52,7 +52,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { } /** - * Builds a CdkSingletonFunction class. + * Builds a `CdkSingletonFunction` class. */ public static buildCdkSingletonFunction(scope: Module, props: CdkHandlerClassProps) { new (class CdkSingletonFunction extends CdkHandlerFrameworkClass { @@ -77,7 +77,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { } /** - * Builds a CdkCustomResourceProvider class. + * Builds a `CdkCustomResourceProvider` class. */ public static buildCdkCustomResourceProvider(scope: Module, props: CdkHandlerClassProps) { new (class CdkCustomResourceProvider extends CdkHandlerFrameworkClass { @@ -100,6 +100,9 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { name: 'getOrCreate', static: true, returnType: Type.STRING, + docs: { + summary: 'Returns a stack-level singleton ARN (service token) for the custom resource provider.', + }, }); getOrCreateMethod.addParameter({ name: 'scope', @@ -117,6 +120,9 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { name: 'getOrCreateProvider', static: true, returnType: this.type, + docs: { + summary: 'Returns a stack-level singleton for the custom resource provider.', + }, }); getOrCreateProviderMethod.addParameter({ name: 'scope', @@ -139,12 +145,13 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { } /** - * + * A local file system directory with the provider's code. The code will be + * bundled into a zip asset and wired to the provider's AWS Lambda function. */ public abstract readonly codeDirectory: string; /** - * + * The name of the method within your code that Lambda calls to execute your function. */ public abstract readonly entrypoint: string; } diff --git a/packages/@aws-cdk/handler-framework/lib/config.ts b/packages/@aws-cdk/handler-framework/lib/config.ts new file mode 100644 index 0000000000000..866238b5066b1 --- /dev/null +++ b/packages/@aws-cdk/handler-framework/lib/config.ts @@ -0,0 +1,94 @@ +import * as path from 'path'; +import { Runtime } from 'aws-cdk-lib/aws-lambda'; + +/** + * Handler framework component types. + */ +enum ComponentType { + /** + * `CdkFunction` + */ + CDK_FUNCTION = 'CdkFunction', + + /** + * `CdkSingletonFunction` + */ + CDK_SINGLETON_FUNCTION = 'CdkSingletonFunction', + + /** + * `CdkCustomResourceProvider` + */ + CDK_CUSTOM_RESOURCE_PROVIDER = 'CdkCustomResourceProvider', +} + +/** + * Properties used to generate a specific handler framework component + */ +interface ComponentDefinition { + /** + * The component type to generate. + */ + readonly type: ComponentType; + + /** + * The name to generate the component with. + * + * Note: This will be the name of the class, i.e., `MyCdkFunction`, etc. + */ + readonly name: string; + + /** + * The local file system directory with the source code. + */ + readonly codeDirectory: string; + + /** + * Runtimes that are compatible with the source code. + */ + readonly compatibleRuntimes: Runtime[]; + + /** + * The name of the method within your code that Lambda calls to execute your function. + * + * @default 'index.handler' + */ + readonly entrypoint?: string; + + /** + * Configurable options for the underlying Lambda function. + */ + readonly providerOptions?: any; +} + +type HandlerFrameworkConfig = { [module: string]: { [identifier: string]: ComponentDefinition[] } }; + +export const config: HandlerFrameworkConfig = { + s3: { + replicaProvider: [ + { + type: ComponentType.CDK_FUNCTION, + name: 'ReplicaOnEventProvider', + codeDirectory: path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-dynamodb', 'replica-handler'), + compatibleRuntimes: [Runtime.NODEJS_LATEST], + entrypoint: 'index.onEventHandler', + }, + { + type: ComponentType.CDK_FUNCTION, + name: 'ReplicaOnCompleteProvider', + codeDirectory: path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-dynamodb', 'replica-handler'), + compatibleRuntimes: [Runtime.NODEJS_LATEST], + entrypoint: 'index.onCompleteHandler', + }, + ], + }, + ses: { + dropSpamProvider: [ + { + type: ComponentType.CDK_SINGLETON_FUNCTION, + name: 'DropSpamProvider', + codeDirectory: path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-ses', 'drop-spam-handler'), + compatibleRuntimes: [Runtime.NODEJS_LATEST], + }, + ], + }, +}; From 2038bcfe3b37a7f29f327d9a67d2c6d27972d4d7 Mon Sep 17 00:00:00 2001 From: Francis Date: Mon, 4 Dec 2023 19:14:59 -0800 Subject: [PATCH 05/73] updated outfile location and config format Signed-off-by: Francis --- packages/@aws-cdk/handler-framework/lib/config.ts | 9 +++++---- packages/@aws-cdk/handler-framework/lib/framework.ts | 6 +++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/@aws-cdk/handler-framework/lib/config.ts b/packages/@aws-cdk/handler-framework/lib/config.ts index 866238b5066b1..8251e31c42144 100644 --- a/packages/@aws-cdk/handler-framework/lib/config.ts +++ b/packages/@aws-cdk/handler-framework/lib/config.ts @@ -62,9 +62,10 @@ interface ComponentDefinition { type HandlerFrameworkConfig = { [module: string]: { [identifier: string]: ComponentDefinition[] } }; +// output will be packages/aws-cdk-lib/handler-framework///index.generated.ts export const config: HandlerFrameworkConfig = { - s3: { - replicaProvider: [ + 'aws-s3': { + 'replica-provider': [ { type: ComponentType.CDK_FUNCTION, name: 'ReplicaOnEventProvider', @@ -81,8 +82,8 @@ export const config: HandlerFrameworkConfig = { }, ], }, - ses: { - dropSpamProvider: [ + 'aws-ses': { + 'drop-spam-provider': [ { type: ComponentType.CDK_SINGLETON_FUNCTION, name: 'DropSpamProvider', diff --git a/packages/@aws-cdk/handler-framework/lib/framework.ts b/packages/@aws-cdk/handler-framework/lib/framework.ts index c0f46ad826b75..2bba28aa5309d 100644 --- a/packages/@aws-cdk/handler-framework/lib/framework.ts +++ b/packages/@aws-cdk/handler-framework/lib/framework.ts @@ -19,7 +19,7 @@ export class CdkHandlerFramework { public static generateCdkFunction(props: CdkHandlerFrameworkProps) { const module = new Module('cdk-function'); CdkHandlerFrameworkClass.buildCdkFunction(module, props); - fs.outputFileSync(`${props.outputFileLocation}/index.ts`, CdkHandlerFramework.renderer.render(module)); + fs.outputFileSync(`${props.outputFileLocation}/index.generated.ts`, CdkHandlerFramework.renderer.render(module)); } /** @@ -28,7 +28,7 @@ export class CdkHandlerFramework { public static generateCdkSingletonFunction(props: CdkHandlerFrameworkProps) { const module = new Module('cdk-singleton-function'); CdkHandlerFrameworkClass.buildCdkSingletonFunction(module, props); - fs.outputFileSync(`${props.outputFileLocation}/index.ts`, CdkHandlerFramework.renderer.render(module)); + fs.outputFileSync(`${props.outputFileLocation}/index.generated.ts`, CdkHandlerFramework.renderer.render(module)); } /** @@ -37,7 +37,7 @@ export class CdkHandlerFramework { public static generateCdkCustomResourceProvider(props: CdkHandlerFrameworkProps) { const module = new Module('cdk-custom-resource-provider'); CdkHandlerFrameworkClass.buildCdkCustomResourceProvider(module, props); - fs.outputFileSync(`${props.outputFileLocation}/index.ts`, CdkHandlerFramework.renderer.render(module)); + fs.outputFileSync(`${props.outputFileLocation}/index.generated.ts`, CdkHandlerFramework.renderer.render(module)); } private static readonly renderer = new TypeScriptRenderer(); From c87613b8ed6db80c284a9294ea84b13dffe34315 Mon Sep 17 00:00:00 2001 From: Francis Date: Tue, 5 Dec 2023 00:46:33 -0800 Subject: [PATCH 06/73] initial work on build script Signed-off-by: Francis --- .../@aws-cdk/handler-framework/.gitignore | 4 +- .../@aws-cdk/handler-framework/lib/config.ts | 6 +-- .../@aws-cdk/handler-framework/package.json | 2 +- .../handler-framework/scripts/generate.ts | 48 +++++++++++++++++++ .../@aws-cdk/handler-framework/tsconfig.json | 23 +++++++++ 5 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 packages/@aws-cdk/handler-framework/scripts/generate.ts create mode 100644 packages/@aws-cdk/handler-framework/tsconfig.json diff --git a/packages/@aws-cdk/handler-framework/.gitignore b/packages/@aws-cdk/handler-framework/.gitignore index d8a8561d50885..b0b1fb617af66 100644 --- a/packages/@aws-cdk/handler-framework/.gitignore +++ b/packages/@aws-cdk/handler-framework/.gitignore @@ -16,4 +16,6 @@ nyc.config.js !.eslintrc.js !jest.config.js -junit.xml \ No newline at end of file +junit.xml + +!tsconfig.json \ No newline at end of file diff --git a/packages/@aws-cdk/handler-framework/lib/config.ts b/packages/@aws-cdk/handler-framework/lib/config.ts index 8251e31c42144..268eb42e14511 100644 --- a/packages/@aws-cdk/handler-framework/lib/config.ts +++ b/packages/@aws-cdk/handler-framework/lib/config.ts @@ -4,7 +4,7 @@ import { Runtime } from 'aws-cdk-lib/aws-lambda'; /** * Handler framework component types. */ -enum ComponentType { +export enum ComponentType { /** * `CdkFunction` */ @@ -24,7 +24,7 @@ enum ComponentType { /** * Properties used to generate a specific handler framework component */ -interface ComponentDefinition { +export interface ComponentDefinition { /** * The component type to generate. */ @@ -60,7 +60,7 @@ interface ComponentDefinition { readonly providerOptions?: any; } -type HandlerFrameworkConfig = { [module: string]: { [identifier: string]: ComponentDefinition[] } }; +export type HandlerFrameworkConfig = { [module: string]: { [identifier: string]: ComponentDefinition[] } }; // output will be packages/aws-cdk-lib/handler-framework///index.generated.ts export const config: HandlerFrameworkConfig = { diff --git a/packages/@aws-cdk/handler-framework/package.json b/packages/@aws-cdk/handler-framework/package.json index a3562ce8b8694..51409e8578bbf 100644 --- a/packages/@aws-cdk/handler-framework/package.json +++ b/packages/@aws-cdk/handler-framework/package.json @@ -4,7 +4,7 @@ "private": true, "description": "CDK Vended Handler Framework", "scripts": { - "build": "cdk-build", + "build": "tsc -b && node scripts/generate.js", "integ": "integ-runner", "lint": "cdk-lint", "package": "cdk-package", diff --git a/packages/@aws-cdk/handler-framework/scripts/generate.ts b/packages/@aws-cdk/handler-framework/scripts/generate.ts new file mode 100644 index 0000000000000..a199486aa0763 --- /dev/null +++ b/packages/@aws-cdk/handler-framework/scripts/generate.ts @@ -0,0 +1,48 @@ +import * as path from 'path'; +import { ComponentDefinition, ComponentType, config } from '../lib/config'; +import { CdkHandlerFramework } from '../lib/framework'; + +/* eslint-disable no-console */ + +const componentDefinitions: ComponentDefinition[] = []; + +function main() { + recurse(config); + for (const component of componentDefinitions) { + generateFrameworkComponent(component); + } + + function recurse(_config: any) { + if (_config instanceof Array) { + componentDefinitions.push(..._config); + return; + } + for (const key in _config) { + if (_config.hasOwnProperty(key) && typeof _config[key] === 'object') { + recurse(_config[key]); + } + } + } +} + +function generateFrameworkComponent(component: ComponentDefinition) { + switch (component.type) { + case ComponentType.CDK_FUNCTION: { + CdkHandlerFramework.generateCdkFunction({ + outputFileLocation: path.join(__dirname), + className: component.name, + codeDirectory: component.codeDirectory, + entrypoint: component.entrypoint, + }); + return; + } + case ComponentType.CDK_SINGLETON_FUNCTION: { + return; + } + case ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER: { + return; + } + } +} + +main(); diff --git a/packages/@aws-cdk/handler-framework/tsconfig.json b/packages/@aws-cdk/handler-framework/tsconfig.json new file mode 100644 index 0000000000000..604b91d0c16f2 --- /dev/null +++ b/packages/@aws-cdk/handler-framework/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": ["es2020", "dom"], + "strict": true, + "alwaysStrict": true, + "declaration": true, + "inlineSourceMap": true, + "inlineSources": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "resolveJsonModule": true, + "composite": true, + "incremental": true, + }, + "include": [ + "**/*.ts", + "**/*.d.ts", + ], +} From 6de2be292e0ce3f258e043f736662969715f0cf0 Mon Sep 17 00:00:00 2001 From: Francis Date: Tue, 5 Dec 2023 12:17:09 -0800 Subject: [PATCH 07/73] refactor Signed-off-by: Francis --- .../@aws-cdk/handler-framework/lib/classes.ts | 18 +-- .../@aws-cdk/handler-framework/lib/config.ts | 60 +--------- .../handler-framework/lib/framework.ts | 105 ++++++++++++++---- .../handler-framework/scripts/generate.ts | 44 ++------ 4 files changed, 104 insertions(+), 123 deletions(-) diff --git a/packages/@aws-cdk/handler-framework/lib/classes.ts b/packages/@aws-cdk/handler-framework/lib/classes.ts index f664c71383466..c277d18718451 100644 --- a/packages/@aws-cdk/handler-framework/lib/classes.ts +++ b/packages/@aws-cdk/handler-framework/lib/classes.ts @@ -29,8 +29,8 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { /** * Builds a `CdkFunction` class. */ - public static buildCdkFunction(scope: Module, props: CdkHandlerClassProps) { - new (class CdkFunction extends CdkHandlerFrameworkClass { + public static buildCdkFunction(scope: Module, props: CdkHandlerClassProps): CdkHandlerFrameworkClass { + return new (class CdkFunction extends CdkHandlerFrameworkClass { public readonly codeDirectory: string; public readonly entrypoint: string; @@ -42,9 +42,9 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { this.codeDirectory = props.codeDirectory; this.entrypoint = props.entrypoint ?? 'index.handler'; - CONSTRUCTS_MODULE.importSelective(scope, ['Construct']); - CDK_HANDLER_MODULE.importSelective(scope, ['CdkHandlerProps', 'CdkHandler']); - LAMBDA_MODULE.importSelective(scope, ['Function']); + CONSTRUCTS_MODULE.importSelective(scope, ['Construct']), + CDK_HANDLER_MODULE.importSelective(scope, ['CdkHandlerProps', 'CdkHandler']), + LAMBDA_MODULE.importSelective(scope, ['Function']), CdkHandlerFrameworkConstructor.forCdkFunction(this); } @@ -54,8 +54,8 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { /** * Builds a `CdkSingletonFunction` class. */ - public static buildCdkSingletonFunction(scope: Module, props: CdkHandlerClassProps) { - new (class CdkSingletonFunction extends CdkHandlerFrameworkClass { + public static buildCdkSingletonFunction(scope: Module, props: CdkHandlerClassProps): CdkHandlerFrameworkClass { + return new (class CdkSingletonFunction extends CdkHandlerFrameworkClass { public readonly codeDirectory: string; public readonly entrypoint: string; @@ -79,8 +79,8 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { /** * Builds a `CdkCustomResourceProvider` class. */ - public static buildCdkCustomResourceProvider(scope: Module, props: CdkHandlerClassProps) { - new (class CdkCustomResourceProvider extends CdkHandlerFrameworkClass { + public static buildCdkCustomResourceProvider(scope: Module, props: CdkHandlerClassProps): CdkHandlerFrameworkClass { + return new (class CdkCustomResourceProvider extends CdkHandlerFrameworkClass { public readonly codeDirectory: string; public readonly entrypoint: string; diff --git a/packages/@aws-cdk/handler-framework/lib/config.ts b/packages/@aws-cdk/handler-framework/lib/config.ts index 268eb42e14511..aab96a6158e19 100644 --- a/packages/@aws-cdk/handler-framework/lib/config.ts +++ b/packages/@aws-cdk/handler-framework/lib/config.ts @@ -1,64 +1,6 @@ import * as path from 'path'; import { Runtime } from 'aws-cdk-lib/aws-lambda'; - -/** - * Handler framework component types. - */ -export enum ComponentType { - /** - * `CdkFunction` - */ - CDK_FUNCTION = 'CdkFunction', - - /** - * `CdkSingletonFunction` - */ - CDK_SINGLETON_FUNCTION = 'CdkSingletonFunction', - - /** - * `CdkCustomResourceProvider` - */ - CDK_CUSTOM_RESOURCE_PROVIDER = 'CdkCustomResourceProvider', -} - -/** - * Properties used to generate a specific handler framework component - */ -export interface ComponentDefinition { - /** - * The component type to generate. - */ - readonly type: ComponentType; - - /** - * The name to generate the component with. - * - * Note: This will be the name of the class, i.e., `MyCdkFunction`, etc. - */ - readonly name: string; - - /** - * The local file system directory with the source code. - */ - readonly codeDirectory: string; - - /** - * Runtimes that are compatible with the source code. - */ - readonly compatibleRuntimes: Runtime[]; - - /** - * The name of the method within your code that Lambda calls to execute your function. - * - * @default 'index.handler' - */ - readonly entrypoint?: string; - - /** - * Configurable options for the underlying Lambda function. - */ - readonly providerOptions?: any; -} +import { ComponentType, ComponentDefinition } from './framework'; export type HandlerFrameworkConfig = { [module: string]: { [identifier: string]: ComponentDefinition[] } }; diff --git a/packages/@aws-cdk/handler-framework/lib/framework.ts b/packages/@aws-cdk/handler-framework/lib/framework.ts index 2bba28aa5309d..3d683dd15fa84 100644 --- a/packages/@aws-cdk/handler-framework/lib/framework.ts +++ b/packages/@aws-cdk/handler-framework/lib/framework.ts @@ -1,43 +1,102 @@ +import * as path from 'path'; import { Module, TypeScriptRenderer } from '@cdklabs/typewriter'; +import { Runtime } from 'aws-cdk-lib/aws-lambda'; import * as fs from 'fs-extra'; -import { CdkHandlerFrameworkClass, CdkHandlerClassProps } from './classes'; +import { CdkHandlerClassProps, CdkHandlerFrameworkClass } from './classes'; /** - * Initialization properties for generating `CdkHandlerFramework` components. + * Handler framework component types. */ -export interface CdkHandlerFrameworkProps extends CdkHandlerClassProps { +export enum ComponentType { /** - * The location to render the output file to. + * `CdkFunction` */ - readonly outputFileLocation: string; + CDK_FUNCTION = 'CdkFunction', + + /** + * `CdkSingletonFunction` + */ + CDK_SINGLETON_FUNCTION = 'CdkSingletonFunction', + + /** + * `CdkCustomResourceProvider` + */ + CDK_CUSTOM_RESOURCE_PROVIDER = 'CdkCustomResourceProvider', } -export class CdkHandlerFramework { +/** + * Properties used to generate a specific handler framework component + */ +export interface ComponentDefinition { /** - * Generates a CdkFunction class and renders it to a specified output file location. + * The component type to generate. */ - public static generateCdkFunction(props: CdkHandlerFrameworkProps) { - const module = new Module('cdk-function'); - CdkHandlerFrameworkClass.buildCdkFunction(module, props); - fs.outputFileSync(`${props.outputFileLocation}/index.generated.ts`, CdkHandlerFramework.renderer.render(module)); - } + readonly type: ComponentType; /** - * Generates a CdkSingletonFunction class and renders it to a specified output file location. + * The name to generate the component with. + * + * Note: This will be the name of the class, i.e., `MyCdkFunction`, etc. */ - public static generateCdkSingletonFunction(props: CdkHandlerFrameworkProps) { - const module = new Module('cdk-singleton-function'); - CdkHandlerFrameworkClass.buildCdkSingletonFunction(module, props); - fs.outputFileSync(`${props.outputFileLocation}/index.generated.ts`, CdkHandlerFramework.renderer.render(module)); - } + readonly name: string; + + /** + * The local file system directory with the source code. + */ + readonly codeDirectory: string; /** - * Generates a CdkCustomResourceProvider class and renders it to a specified output file location. + * Runtimes that are compatible with the source code. */ - public static generateCdkCustomResourceProvider(props: CdkHandlerFrameworkProps) { - const module = new Module('cdk-custom-resource-provider'); - CdkHandlerFrameworkClass.buildCdkCustomResourceProvider(module, props); - fs.outputFileSync(`${props.outputFileLocation}/index.generated.ts`, CdkHandlerFramework.renderer.render(module)); + readonly compatibleRuntimes: Runtime[]; + + /** + * The name of the method within your code that Lambda calls to execute your function. + * + * @default 'index.handler' + */ + readonly entrypoint?: string; + + /** + * Configurable options for the underlying Lambda function. + */ + readonly providerOptions?: any; +} + +export class CdkHandlerFramework { + /** + * Generate framework using component definitions. + */ + public static generate(outputFileLocation: string, components: ComponentDefinition[]) { + const module = new Module('cdk-handler-framework'); + + for (let component of components) { + const props: CdkHandlerClassProps = { + codeDirectory: component.codeDirectory, + className: component.name, + entrypoint: component.entrypoint, + }; + + switch (component.type) { + case ComponentType.CDK_FUNCTION: { + CdkHandlerFrameworkClass.buildCdkFunction(module, props); + break; + } + case ComponentType.CDK_SINGLETON_FUNCTION: { + CdkHandlerFrameworkClass.buildCdkSingletonFunction(module, props); + break; + } + case ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER: { + CdkHandlerFrameworkClass.buildCdkCustomResourceProvider(module, props); + break; + } + } + } + + fs.outputFileSync( + `${path.join(__dirname, outputFileLocation)}/index.generated.ts`, + CdkHandlerFramework.renderer.render(module), + ); } private static readonly renderer = new TypeScriptRenderer(); diff --git a/packages/@aws-cdk/handler-framework/scripts/generate.ts b/packages/@aws-cdk/handler-framework/scripts/generate.ts index a199486aa0763..05b39dd954871 100644 --- a/packages/@aws-cdk/handler-framework/scripts/generate.ts +++ b/packages/@aws-cdk/handler-framework/scripts/generate.ts @@ -1,48 +1,28 @@ -import * as path from 'path'; -import { ComponentDefinition, ComponentType, config } from '../lib/config'; -import { CdkHandlerFramework } from '../lib/framework'; +import { config } from '../lib/config'; +import { CdkHandlerFramework, ComponentDefinition } from '../lib/framework'; -/* eslint-disable no-console */ - -const componentDefinitions: ComponentDefinition[] = []; +const generate: { [outputFileLocation: string]: ComponentDefinition[] } = {}; function main() { - recurse(config); - for (const component of componentDefinitions) { - generateFrameworkComponent(component); + recurse(config, []); + for (const [outputFileLocation, components] of Object.entries(generate)) { + CdkHandlerFramework.generate(outputFileLocation, components); } - function recurse(_config: any) { + function recurse(_config: any, path: string[]) { if (_config instanceof Array) { - componentDefinitions.push(..._config); + const outputFileLocation = path.join('/'); + generate[outputFileLocation] = _config; return; } for (const key in _config) { if (_config.hasOwnProperty(key) && typeof _config[key] === 'object') { - recurse(_config[key]); + path.push(key); + recurse(_config[key], path); + path.pop(); // backtrack } } } } -function generateFrameworkComponent(component: ComponentDefinition) { - switch (component.type) { - case ComponentType.CDK_FUNCTION: { - CdkHandlerFramework.generateCdkFunction({ - outputFileLocation: path.join(__dirname), - className: component.name, - codeDirectory: component.codeDirectory, - entrypoint: component.entrypoint, - }); - return; - } - case ComponentType.CDK_SINGLETON_FUNCTION: { - return; - } - case ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER: { - return; - } - } -} - main(); From 5873fe426ebb78a363ef9b1ecc530d9ce0b0e8e1 Mon Sep 17 00:00:00 2001 From: Francis Date: Tue, 5 Dec 2023 14:34:12 -0800 Subject: [PATCH 08/73] migrated handler framework into custom resource handlers and updated minify and bundle script for framework generation Signed-off-by: Francis --- .../lib/handler-framework}/classes.ts | 47 +++- .../lib/handler-framework}/config.ts | 6 +- .../lib/handler-framework}/constructors.ts | 4 +- .../lib/handler-framework}/framework.ts | 51 +++-- .../lib/handler-framework}/modules.ts | 0 .../custom-resource-handlers/package.json | 2 + .../scripts/minify-and-bundle-sources.ts | 25 +++ .../@aws-cdk/handler-framework/.eslintrc.js | 16 -- .../@aws-cdk/handler-framework/.gitignore | 21 -- .../@aws-cdk/handler-framework/.npmignore | 29 --- packages/@aws-cdk/handler-framework/LICENSE | 201 ------------------ packages/@aws-cdk/handler-framework/NOTICE | 2 - .../@aws-cdk/handler-framework/jest.config.js | 2 - .../@aws-cdk/handler-framework/package.json | 61 ------ .../handler-framework/scripts/generate.ts | 28 --- .../handler-framework/tsconfig.dev.json | 23 -- .../@aws-cdk/handler-framework/tsconfig.json | 23 -- 17 files changed, 95 insertions(+), 446 deletions(-) rename packages/@aws-cdk/{handler-framework/lib => custom-resource-handlers/lib/handler-framework}/classes.ts (75%) rename packages/@aws-cdk/{handler-framework/lib => custom-resource-handlers/lib/handler-framework}/config.ts (78%) rename packages/@aws-cdk/{handler-framework/lib => custom-resource-handlers/lib/handler-framework}/constructors.ts (93%) rename packages/@aws-cdk/{handler-framework/lib => custom-resource-handlers/lib/handler-framework}/framework.ts (59%) rename packages/@aws-cdk/{handler-framework/lib => custom-resource-handlers/lib/handler-framework}/modules.ts (100%) delete mode 100644 packages/@aws-cdk/handler-framework/.eslintrc.js delete mode 100644 packages/@aws-cdk/handler-framework/.gitignore delete mode 100644 packages/@aws-cdk/handler-framework/.npmignore delete mode 100644 packages/@aws-cdk/handler-framework/LICENSE delete mode 100644 packages/@aws-cdk/handler-framework/NOTICE delete mode 100644 packages/@aws-cdk/handler-framework/jest.config.js delete mode 100644 packages/@aws-cdk/handler-framework/package.json delete mode 100644 packages/@aws-cdk/handler-framework/scripts/generate.ts delete mode 100644 packages/@aws-cdk/handler-framework/tsconfig.dev.json delete mode 100644 packages/@aws-cdk/handler-framework/tsconfig.json diff --git a/packages/@aws-cdk/handler-framework/lib/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts similarity index 75% rename from packages/@aws-cdk/handler-framework/lib/classes.ts rename to packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts index c277d18718451..622e9c9bff7a5 100644 --- a/packages/@aws-cdk/handler-framework/lib/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts @@ -1,4 +1,4 @@ -import { Module, ClassType, stmt, expr, Type } from '@cdklabs/typewriter'; +import { Module, ClassType, stmt, expr, Type, ExternalModule } from '@cdklabs/typewriter'; import { CdkHandlerFrameworkConstructor } from './constructors'; import { CDK_HANDLER_MODULE, CONSTRUCTS_MODULE, LAMBDA_MODULE, CORE_MODULE } from './modules'; @@ -33,18 +33,25 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { return new (class CdkFunction extends CdkHandlerFrameworkClass { public readonly codeDirectory: string; public readonly entrypoint: string; + public readonly constructs: { [construct: string]: ExternalModule } = { + Construct: CONSTRUCTS_MODULE, + CdkHandlerProps: CDK_HANDLER_MODULE, + CdkHandler: CDK_HANDLER_MODULE, + Function: LAMBDA_MODULE, + }; public constructor() { super(scope, { name: props.className, extends: LAMBDA_MODULE.Function, + export: true, }); this.codeDirectory = props.codeDirectory; this.entrypoint = props.entrypoint ?? 'index.handler'; - CONSTRUCTS_MODULE.importSelective(scope, ['Construct']), - CDK_HANDLER_MODULE.importSelective(scope, ['CdkHandlerProps', 'CdkHandler']), - LAMBDA_MODULE.importSelective(scope, ['Function']), + CONSTRUCTS_MODULE.import(scope, 'constructs'); + CDK_HANDLER_MODULE.import(scope, 'handler'); + LAMBDA_MODULE.import(scope, 'lambda'); CdkHandlerFrameworkConstructor.forCdkFunction(this); } @@ -58,18 +65,25 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { return new (class CdkSingletonFunction extends CdkHandlerFrameworkClass { public readonly codeDirectory: string; public readonly entrypoint: string; + public readonly constructs: { [construct: string]: ExternalModule } = { + Construct: CONSTRUCTS_MODULE, + CdkHandlerProps: CDK_HANDLER_MODULE, + CdkHandler: CDK_HANDLER_MODULE, + SingletonFunction: LAMBDA_MODULE, + }; public constructor() { super(scope, { name: props.className, extends: LAMBDA_MODULE.SingletonFunction, + export: true, }); this.codeDirectory = props.codeDirectory; this.entrypoint = props.entrypoint ?? 'index.handler'; - CONSTRUCTS_MODULE.importSelective(scope, ['Construct']); - CDK_HANDLER_MODULE.importSelective(scope, ['CdkHandlerProps', 'CdkHandler']); - LAMBDA_MODULE.importSelective(scope, ['SingletonFunction']); + CONSTRUCTS_MODULE.import(scope, 'constructs'); + CDK_HANDLER_MODULE.import(scope, 'handler'); + LAMBDA_MODULE.import(scope, 'lambda'); CdkHandlerFrameworkConstructor.forCdkFunction(this); } @@ -83,18 +97,25 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { return new (class CdkCustomResourceProvider extends CdkHandlerFrameworkClass { public readonly codeDirectory: string; public readonly entrypoint: string; + public readonly constructs: { [construct: string]: ExternalModule } = { + Construct: CONSTRUCTS_MODULE, + CdkHandlerProps: CDK_HANDLER_MODULE, + CdkHandler: CDK_HANDLER_MODULE, + CustomResourceProviderBase: CORE_MODULE, + }; public constructor() { super(scope, { name: props.className, extends: CORE_MODULE.CustomResourceProviderBase, + export: true, }); this.codeDirectory = props.codeDirectory; this.entrypoint = props.entrypoint ?? 'index.handler'; - CONSTRUCTS_MODULE.importSelective(scope, ['Construct']); - CDK_HANDLER_MODULE.importSelective(scope, ['CdkHandlerProps', 'CdkHandler']); - CORE_MODULE.importSelective(scope, ['CustomResourceProviderBase']); + CONSTRUCTS_MODULE.import(scope, 'constructs'); + CDK_HANDLER_MODULE.import(scope, 'handler'); + CORE_MODULE.import(scope, 'core'); const getOrCreateMethod = this.addMethod({ name: 'getOrCreate', @@ -154,4 +175,10 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { * The name of the method within your code that Lambda calls to execute your function. */ public abstract readonly entrypoint: string; + + /** + * A map representing the constructs this class depends on and the external module that the + * consruct can be imported from. + */ + public abstract readonly constructs: { [construct: string]: ExternalModule }; } diff --git a/packages/@aws-cdk/handler-framework/lib/config.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts similarity index 78% rename from packages/@aws-cdk/handler-framework/lib/config.ts rename to packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts index aab96a6158e19..3efb8a811482c 100644 --- a/packages/@aws-cdk/handler-framework/lib/config.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts @@ -1,8 +1,7 @@ import * as path from 'path'; -import { Runtime } from 'aws-cdk-lib/aws-lambda'; import { ComponentType, ComponentDefinition } from './framework'; -export type HandlerFrameworkConfig = { [module: string]: { [identifier: string]: ComponentDefinition[] } }; +type HandlerFrameworkConfig = { [module: string]: { [identifier: string]: ComponentDefinition[] } }; // output will be packages/aws-cdk-lib/handler-framework///index.generated.ts export const config: HandlerFrameworkConfig = { @@ -12,14 +11,12 @@ export const config: HandlerFrameworkConfig = { type: ComponentType.CDK_FUNCTION, name: 'ReplicaOnEventProvider', codeDirectory: path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-dynamodb', 'replica-handler'), - compatibleRuntimes: [Runtime.NODEJS_LATEST], entrypoint: 'index.onEventHandler', }, { type: ComponentType.CDK_FUNCTION, name: 'ReplicaOnCompleteProvider', codeDirectory: path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-dynamodb', 'replica-handler'), - compatibleRuntimes: [Runtime.NODEJS_LATEST], entrypoint: 'index.onCompleteHandler', }, ], @@ -30,7 +27,6 @@ export const config: HandlerFrameworkConfig = { type: ComponentType.CDK_SINGLETON_FUNCTION, name: 'DropSpamProvider', codeDirectory: path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-ses', 'drop-spam-handler'), - compatibleRuntimes: [Runtime.NODEJS_LATEST], }, ], }, diff --git a/packages/@aws-cdk/handler-framework/lib/constructors.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts similarity index 93% rename from packages/@aws-cdk/handler-framework/lib/constructors.ts rename to packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts index e555ac634fd80..f04e4e495fc8c 100644 --- a/packages/@aws-cdk/handler-framework/lib/constructors.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts @@ -48,7 +48,7 @@ export class CdkHandlerFrameworkConstructor { // build CdkHandler instance init.addBody( stmt.constVar( - expr.directCode('cdkHandlerProps: CdkHandlerProps'), + expr.directCode('cdkHandlerProps: handler.CdkHandlerProps'), expr.object({ codeDirectory: expr.lit(`${_class.codeDirectory}`), entrypoint: _class.entrypoint @@ -60,7 +60,7 @@ export class CdkHandlerFrameworkConstructor { init.addBody( stmt.constVar( expr.ident('cdkHandler'), - expr.directCode("new CdkHandler(scope, 'Handler', cdkHandlerProps)"), + expr.directCode("new handler.CdkHandler(scope, 'Handler', cdkHandlerProps)"), ), ); diff --git a/packages/@aws-cdk/handler-framework/lib/framework.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts similarity index 59% rename from packages/@aws-cdk/handler-framework/lib/framework.ts rename to packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts index 3d683dd15fa84..703caa8f39924 100644 --- a/packages/@aws-cdk/handler-framework/lib/framework.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts @@ -1,6 +1,4 @@ -import * as path from 'path'; -import { Module, TypeScriptRenderer } from '@cdklabs/typewriter'; -import { Runtime } from 'aws-cdk-lib/aws-lambda'; +import { ExternalModule, Module, TypeScriptRenderer } from '@cdklabs/typewriter'; import * as fs from 'fs-extra'; import { CdkHandlerClassProps, CdkHandlerFrameworkClass } from './classes'; @@ -45,11 +43,6 @@ export interface ComponentDefinition { */ readonly codeDirectory: string; - /** - * Runtimes that are compatible with the source code. - */ - readonly compatibleRuntimes: Runtime[]; - /** * The name of the method within your code that Lambda calls to execute your function. * @@ -63,12 +56,19 @@ export interface ComponentDefinition { readonly providerOptions?: any; } -export class CdkHandlerFramework { +export class CdkHandlerFramework extends Module { /** - * Generate framework using component definitions. + * Build a framework module with specified components. */ - public static generate(outputFileLocation: string, components: ComponentDefinition[]) { - const module = new Module('cdk-handler-framework'); + public static build(components: ComponentDefinition[]) { + return new CdkHandlerFramework(components); + } + + private readonly renderer = new TypeScriptRenderer(); + private readonly constructs = new Map(); + + private constructor(components: ComponentDefinition[]) { + super('cdk-handler-framework'); for (let component of components) { const props: CdkHandlerClassProps = { @@ -77,29 +77,34 @@ export class CdkHandlerFramework { entrypoint: component.entrypoint, }; + let _class: CdkHandlerFrameworkClass; switch (component.type) { case ComponentType.CDK_FUNCTION: { - CdkHandlerFrameworkClass.buildCdkFunction(module, props); + _class = CdkHandlerFrameworkClass.buildCdkFunction(this, props); break; } case ComponentType.CDK_SINGLETON_FUNCTION: { - CdkHandlerFrameworkClass.buildCdkSingletonFunction(module, props); + _class = CdkHandlerFrameworkClass.buildCdkSingletonFunction(this, props); break; } case ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER: { - CdkHandlerFrameworkClass.buildCdkCustomResourceProvider(module, props); + _class = CdkHandlerFrameworkClass.buildCdkCustomResourceProvider(this, props); break; } } - } - fs.outputFileSync( - `${path.join(__dirname, outputFileLocation)}/index.generated.ts`, - CdkHandlerFramework.renderer.render(module), - ); + for (const [construct, module] of Object.entries(_class.constructs)) { + if (!this.constructs.has(construct)) { + this.constructs.set(construct, module); + } + } + } } - private static readonly renderer = new TypeScriptRenderer(); - - private constructor() {} + /** + * Render built framework into an output file. + */ + public render(outputFileLocation: string) { + fs.outputFileSync(`${outputFileLocation}.generated.ts`, this.renderer.render(this)); + } } diff --git a/packages/@aws-cdk/handler-framework/lib/modules.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts similarity index 100% rename from packages/@aws-cdk/handler-framework/lib/modules.ts rename to packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts diff --git a/packages/@aws-cdk/custom-resource-handlers/package.json b/packages/@aws-cdk/custom-resource-handlers/package.json index 8858b02269b6d..c8d8dc4b0405d 100644 --- a/packages/@aws-cdk/custom-resource-handlers/package.json +++ b/packages/@aws-cdk/custom-resource-handlers/package.json @@ -45,6 +45,7 @@ "@aws-sdk/client-eks": "3.421.0", "@aws-sdk/client-sts": "3.421.0", "@aws-sdk/node-http-handler": "^3.370.0", + "@cdklabs/typewriter": "^0.0.3", "@smithy/util-stream": "^2.0.20", "@types/jest": "^29.5.8", "aws-sdk-client-mock": "^3.0.0", @@ -62,6 +63,7 @@ "@aws-sdk/client-synthetics": "3.421.0", "@aws-sdk/client-ecr": "3.421.0", "@aws-sdk/client-s3": "3.421.0", + "@cdklabs/typewriter": "^0.0.3", "aws-sdk": "^2.1498.0" }, "repository": { diff --git a/packages/@aws-cdk/custom-resource-handlers/scripts/minify-and-bundle-sources.ts b/packages/@aws-cdk/custom-resource-handlers/scripts/minify-and-bundle-sources.ts index ed618b4446ae1..73a2c82549b12 100644 --- a/packages/@aws-cdk/custom-resource-handlers/scripts/minify-and-bundle-sources.ts +++ b/packages/@aws-cdk/custom-resource-handlers/scripts/minify-and-bundle-sources.ts @@ -1,8 +1,12 @@ import * as fs from 'fs'; import * as path from 'path'; import * as esbuild from 'esbuild'; +import { config } from '../lib/handler-framework/config'; +import { CdkHandlerFramework, ComponentDefinition } from '../lib/handler-framework/framework'; +const framework: { [outputFileLocation: string]: ComponentDefinition[] } = {}; const entryPoints: string[] = []; + function recFolderStructure(fileOrDir: string) { if (fs.statSync(fileOrDir).isDirectory()) { const items = fs.readdirSync(fileOrDir); @@ -73,6 +77,12 @@ async function main() { } } + recurse(config, []); + for (const [outputFileLocation, components] of Object.entries(framework)) { + const module = CdkHandlerFramework.build(components); + module.render(outputFileLocation); + } + function calculateOutfile(file: string) { // turn ts extension into js extension if (file.includes('index.ts')) { @@ -85,6 +95,21 @@ async function main() { return fileContents.join(path.sep); } + + function recurse(_config: any, _path: string[]) { + if (_config instanceof Array) { + const outputFileLocation = path.join('/'); + framework[outputFileLocation] = _config; + return; + } + for (const key in _config) { + if (_config.hasOwnProperty(key) && typeof _config[key] === 'object') { + _path.push(key); + recurse(_config[key], _path); + _path.pop(); // backtrack + } + } + } } function ignoreWarnings(result: esbuild.BuildResult) { diff --git a/packages/@aws-cdk/handler-framework/.eslintrc.js b/packages/@aws-cdk/handler-framework/.eslintrc.js deleted file mode 100644 index 0adc65405c74e..0000000000000 --- a/packages/@aws-cdk/handler-framework/.eslintrc.js +++ /dev/null @@ -1,16 +0,0 @@ -const baseConfig = require('@aws-cdk/cdk-build-tools/config/eslintrc'); -baseConfig.parserOptions.project = __dirname + '/tsconfig.dev.json'; -baseConfig.rules['import/no-extraneous-dependencies'] = [ - 'error', - { - devDependencies: [ - '**/build-tools/**', - '**/scripts/**', - '**/test/**', - ], - optionalDependencies: false, - peerDependencies: true, - } -]; - -module.exports = baseConfig; \ No newline at end of file diff --git a/packages/@aws-cdk/handler-framework/.gitignore b/packages/@aws-cdk/handler-framework/.gitignore deleted file mode 100644 index b0b1fb617af66..0000000000000 --- a/packages/@aws-cdk/handler-framework/.gitignore +++ /dev/null @@ -1,21 +0,0 @@ -*.js -*.js.map -*.d.ts -tsconfig.json -node_modules -*.generated.ts -dist -.jsii - -.LAST_BUILD -.nyc_output -coverage -nyc.config.js -.LAST_PACKAGE -*.snk -!.eslintrc.js -!jest.config.js - -junit.xml - -!tsconfig.json \ No newline at end of file diff --git a/packages/@aws-cdk/handler-framework/.npmignore b/packages/@aws-cdk/handler-framework/.npmignore deleted file mode 100644 index 5ddbf25e19336..0000000000000 --- a/packages/@aws-cdk/handler-framework/.npmignore +++ /dev/null @@ -1,29 +0,0 @@ -# Don't include original .ts files when doing `npm pack` -*.ts -!*.d.ts -coverage -.nyc_output -*.tgz - -dist -.LAST_PACKAGE -.LAST_BUILD -!*.js - -# Include .jsii -!.jsii - -*.snk - -*.tsbuildinfo - -tsconfig.json -.eslintrc.js -jest.config.js - -# exclude cdk artifacts -**/cdk.out -junit.xml -test/ -!*.lit.ts -**/*.snapshot \ No newline at end of file diff --git a/packages/@aws-cdk/handler-framework/LICENSE b/packages/@aws-cdk/handler-framework/LICENSE deleted file mode 100644 index 9b722c65c5481..0000000000000 --- a/packages/@aws-cdk/handler-framework/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/handler-framework/NOTICE b/packages/@aws-cdk/handler-framework/NOTICE deleted file mode 100644 index 5efb94182cd88..0000000000000 --- a/packages/@aws-cdk/handler-framework/NOTICE +++ /dev/null @@ -1,2 +0,0 @@ -AWS Cloud Development Kit (AWS CDK) -Copyright 2018-2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. \ No newline at end of file diff --git a/packages/@aws-cdk/handler-framework/jest.config.js b/packages/@aws-cdk/handler-framework/jest.config.js deleted file mode 100644 index 3a2fd93a1228a..0000000000000 --- a/packages/@aws-cdk/handler-framework/jest.config.js +++ /dev/null @@ -1,2 +0,0 @@ -const baseConfig = require('@aws-cdk/cdk-build-tools/config/jest.config'); -module.exports = baseConfig; diff --git a/packages/@aws-cdk/handler-framework/package.json b/packages/@aws-cdk/handler-framework/package.json deleted file mode 100644 index 51409e8578bbf..0000000000000 --- a/packages/@aws-cdk/handler-framework/package.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "name": "@aws-cdk/handler-framework", - "version": "0.0.0", - "private": true, - "description": "CDK Vended Handler Framework", - "scripts": { - "build": "tsc -b && node scripts/generate.js", - "integ": "integ-runner", - "lint": "cdk-lint", - "package": "cdk-package", - "awslint": "cdk-awslint", - "pkglint": "pkglint -f", - "test": "cdk-test", - "watch": "cdk-watch", - "build+test": "yarn build && yarn test", - "build+test+package": "yarn build+test && yarn package", - "compat": "cdk-compat", - "build+extract": "yarn build", - "build+test+extract": "yarn build+test" - }, - "author": { - "name": "Amazon Web Services", - "url": "https://aws.amazon.com", - "organization": true - }, - "license": "Apache-2.0", - "devDependencies": { - "@aws-cdk/cdk-build-tools": "0.0.0", - "@aws-cdk/pkglint": "0.0.0", - "jest": "^29.7.0" - }, - "dependencies": { - "@cdklabs/typewriter": "^0.0.3", - "fs-extra": "^11.2.0" - }, - "peerDependencies": { - "aws-cdk-lib": "^0.0.0", - "constructs": "^10.0.0" - }, - "repository": { - "url": "https://github.com/aws/aws-cdk.git", - "type": "git", - "directory": "packages/@aws-cdk/handler-framework" - }, - "keywords": [ - "aws", - "cdk" - ], - "homepage": "https://github.com/aws/aws-cdk", - "engines": { - "node": ">= 14.15.0" - }, - "cdk-package": { - "shrinkWrap": true - }, - "stability": "experimental", - "maturity": "experimental", - "publishConfig": { - "tag": "latest" - } -} \ No newline at end of file diff --git a/packages/@aws-cdk/handler-framework/scripts/generate.ts b/packages/@aws-cdk/handler-framework/scripts/generate.ts deleted file mode 100644 index 05b39dd954871..0000000000000 --- a/packages/@aws-cdk/handler-framework/scripts/generate.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { config } from '../lib/config'; -import { CdkHandlerFramework, ComponentDefinition } from '../lib/framework'; - -const generate: { [outputFileLocation: string]: ComponentDefinition[] } = {}; - -function main() { - recurse(config, []); - for (const [outputFileLocation, components] of Object.entries(generate)) { - CdkHandlerFramework.generate(outputFileLocation, components); - } - - function recurse(_config: any, path: string[]) { - if (_config instanceof Array) { - const outputFileLocation = path.join('/'); - generate[outputFileLocation] = _config; - return; - } - for (const key in _config) { - if (_config.hasOwnProperty(key) && typeof _config[key] === 'object') { - path.push(key); - recurse(_config[key], path); - path.pop(); // backtrack - } - } - } -} - -main(); diff --git a/packages/@aws-cdk/handler-framework/tsconfig.dev.json b/packages/@aws-cdk/handler-framework/tsconfig.dev.json deleted file mode 100644 index 604b91d0c16f2..0000000000000 --- a/packages/@aws-cdk/handler-framework/tsconfig.dev.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2020", - "module": "commonjs", - "lib": ["es2020", "dom"], - "strict": true, - "alwaysStrict": true, - "declaration": true, - "inlineSourceMap": true, - "inlineSources": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "resolveJsonModule": true, - "composite": true, - "incremental": true, - }, - "include": [ - "**/*.ts", - "**/*.d.ts", - ], -} diff --git a/packages/@aws-cdk/handler-framework/tsconfig.json b/packages/@aws-cdk/handler-framework/tsconfig.json deleted file mode 100644 index 604b91d0c16f2..0000000000000 --- a/packages/@aws-cdk/handler-framework/tsconfig.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2020", - "module": "commonjs", - "lib": ["es2020", "dom"], - "strict": true, - "alwaysStrict": true, - "declaration": true, - "inlineSourceMap": true, - "inlineSources": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "resolveJsonModule": true, - "composite": true, - "incremental": true, - }, - "include": [ - "**/*.ts", - "**/*.d.ts", - ], -} From dccc9852b5963034d95fe858cec00685359ebb60 Mon Sep 17 00:00:00 2001 From: Francis Date: Tue, 5 Dec 2023 14:53:38 -0800 Subject: [PATCH 09/73] migrated handler-framework into custom-resource-handlers Signed-off-by: Francis --- .../custom-resource-handlers/lib/handler-framework/classes.ts | 1 + .../custom-resource-handlers/lib/handler-framework/config.ts | 2 +- .../lib/handler-framework/constructors.ts | 1 + .../lib/handler-framework/framework.ts | 3 ++- .../custom-resource-handlers/lib/handler-framework/modules.ts | 1 + packages/@aws-cdk/custom-resource-handlers/package.json | 3 +-- .../scripts/minify-and-bundle-sources.ts | 2 +- 7 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts index 622e9c9bff7a5..c554ab4ef3c91 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts @@ -1,3 +1,4 @@ +/* eslint-disable import/no-extraneous-dependencies */ import { Module, ClassType, stmt, expr, Type, ExternalModule } from '@cdklabs/typewriter'; import { CdkHandlerFrameworkConstructor } from './constructors'; import { CDK_HANDLER_MODULE, CONSTRUCTS_MODULE, LAMBDA_MODULE, CORE_MODULE } from './modules'; diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts index 3efb8a811482c..ffa753d4c2bcc 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts @@ -5,7 +5,7 @@ type HandlerFrameworkConfig = { [module: string]: { [identifier: string]: Compon // output will be packages/aws-cdk-lib/handler-framework///index.generated.ts export const config: HandlerFrameworkConfig = { - 'aws-s3': { + 'aws-dynamodb': { 'replica-provider': [ { type: ComponentType.CDK_FUNCTION, diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts index f04e4e495fc8c..bfe8547d63375 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts @@ -1,3 +1,4 @@ +/* eslint-disable import/no-extraneous-dependencies */ import { Expression, Type, stmt, expr, SuperInitializer, ObjectLiteral } from '@cdklabs/typewriter'; import { CdkHandlerFrameworkClass } from './classes'; import { CONSTRUCTS_MODULE } from './modules'; diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts index 703caa8f39924..a2831b3d6c1fd 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts @@ -1,3 +1,4 @@ +/* eslint-disable import/no-extraneous-dependencies */ import { ExternalModule, Module, TypeScriptRenderer } from '@cdklabs/typewriter'; import * as fs from 'fs-extra'; import { CdkHandlerClassProps, CdkHandlerFrameworkClass } from './classes'; @@ -105,6 +106,6 @@ export class CdkHandlerFramework extends Module { * Render built framework into an output file. */ public render(outputFileLocation: string) { - fs.outputFileSync(`${outputFileLocation}.generated.ts`, this.renderer.render(this)); + fs.outputFileSync(`dist/${outputFileLocation}.generated.ts`, this.renderer.render(this)); } } diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts index d9aa43fff94c5..4ea7bec3b7f0c 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts @@ -1,3 +1,4 @@ +/* eslint-disable import/no-extraneous-dependencies */ import { ExternalModule, Type } from '@cdklabs/typewriter'; class ConstructsModule extends ExternalModule { diff --git a/packages/@aws-cdk/custom-resource-handlers/package.json b/packages/@aws-cdk/custom-resource-handlers/package.json index c8d8dc4b0405d..d177996b09edd 100644 --- a/packages/@aws-cdk/custom-resource-handlers/package.json +++ b/packages/@aws-cdk/custom-resource-handlers/package.json @@ -45,12 +45,12 @@ "@aws-sdk/client-eks": "3.421.0", "@aws-sdk/client-sts": "3.421.0", "@aws-sdk/node-http-handler": "^3.370.0", - "@cdklabs/typewriter": "^0.0.3", "@smithy/util-stream": "^2.0.20", "@types/jest": "^29.5.8", "aws-sdk-client-mock": "^3.0.0", "aws-sdk-client-mock-jest": "^3.0.0", "aws-sdk-mock": "5.8.0", + "@cdklabs/typewriter": "^0.0.3", "jest": "^29.7.0", "sinon": "^9.2.4", "nock": "^13.3.8", @@ -63,7 +63,6 @@ "@aws-sdk/client-synthetics": "3.421.0", "@aws-sdk/client-ecr": "3.421.0", "@aws-sdk/client-s3": "3.421.0", - "@cdklabs/typewriter": "^0.0.3", "aws-sdk": "^2.1498.0" }, "repository": { diff --git a/packages/@aws-cdk/custom-resource-handlers/scripts/minify-and-bundle-sources.ts b/packages/@aws-cdk/custom-resource-handlers/scripts/minify-and-bundle-sources.ts index 73a2c82549b12..f326082025972 100644 --- a/packages/@aws-cdk/custom-resource-handlers/scripts/minify-and-bundle-sources.ts +++ b/packages/@aws-cdk/custom-resource-handlers/scripts/minify-and-bundle-sources.ts @@ -98,7 +98,7 @@ async function main() { function recurse(_config: any, _path: string[]) { if (_config instanceof Array) { - const outputFileLocation = path.join('/'); + const outputFileLocation = _path.join('/'); framework[outputFileLocation] = _config; return; } From 688a69dcfbc7f1d2d1e95184cca91458de6a3457 Mon Sep 17 00:00:00 2001 From: Francis Date: Tue, 5 Dec 2023 20:54:03 -0800 Subject: [PATCH 10/73] imports for external modules Signed-off-by: Francis --- .../lib/handler-framework/classes.ts | 50 ++++++----------- .../lib/handler-framework/config.ts | 1 - .../lib/handler-framework/framework.ts | 54 ++++++++++++++----- .../scripts/minify-and-bundle-sources.ts | 7 ++- 4 files changed, 62 insertions(+), 50 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts index c554ab4ef3c91..73dfb7b763bd3 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts @@ -1,7 +1,8 @@ /* eslint-disable import/no-extraneous-dependencies */ -import { Module, ClassType, stmt, expr, Type, ExternalModule } from '@cdklabs/typewriter'; +import { ClassType, stmt, expr, Type, ExternalModule } from '@cdklabs/typewriter'; import { CdkHandlerFrameworkConstructor } from './constructors'; import { CDK_HANDLER_MODULE, CONSTRUCTS_MODULE, LAMBDA_MODULE, CORE_MODULE } from './modules'; +import { CdkHandlerFrameworkModule } from './framework'; /** * Initialization properties used to build a `CdkHandlerFrameworkClass` instance. @@ -30,16 +31,12 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { /** * Builds a `CdkFunction` class. */ - public static buildCdkFunction(scope: Module, props: CdkHandlerClassProps): CdkHandlerFrameworkClass { + public static buildCdkFunction(scope: CdkHandlerFrameworkModule, props: CdkHandlerClassProps): CdkHandlerFrameworkClass { return new (class CdkFunction extends CdkHandlerFrameworkClass { public readonly codeDirectory: string; public readonly entrypoint: string; - public readonly constructs: { [construct: string]: ExternalModule } = { - Construct: CONSTRUCTS_MODULE, - CdkHandlerProps: CDK_HANDLER_MODULE, - CdkHandler: CDK_HANDLER_MODULE, - Function: LAMBDA_MODULE, - }; + + protected readonly externalModules = [CONSTRUCTS_MODULE, LAMBDA_MODULE, CDK_HANDLER_MODULE]; public constructor() { super(scope, { @@ -50,9 +47,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { this.codeDirectory = props.codeDirectory; this.entrypoint = props.entrypoint ?? 'index.handler'; - CONSTRUCTS_MODULE.import(scope, 'constructs'); - CDK_HANDLER_MODULE.import(scope, 'handler'); - LAMBDA_MODULE.import(scope, 'lambda'); + this.externalModules.forEach(module => scope.addExternalModule(module)); CdkHandlerFrameworkConstructor.forCdkFunction(this); } @@ -62,16 +57,12 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { /** * Builds a `CdkSingletonFunction` class. */ - public static buildCdkSingletonFunction(scope: Module, props: CdkHandlerClassProps): CdkHandlerFrameworkClass { + public static buildCdkSingletonFunction(scope: CdkHandlerFrameworkModule, props: CdkHandlerClassProps): CdkHandlerFrameworkClass { return new (class CdkSingletonFunction extends CdkHandlerFrameworkClass { public readonly codeDirectory: string; public readonly entrypoint: string; - public readonly constructs: { [construct: string]: ExternalModule } = { - Construct: CONSTRUCTS_MODULE, - CdkHandlerProps: CDK_HANDLER_MODULE, - CdkHandler: CDK_HANDLER_MODULE, - SingletonFunction: LAMBDA_MODULE, - }; + + protected readonly externalModules = [CONSTRUCTS_MODULE, LAMBDA_MODULE, CDK_HANDLER_MODULE]; public constructor() { super(scope, { @@ -82,9 +73,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { this.codeDirectory = props.codeDirectory; this.entrypoint = props.entrypoint ?? 'index.handler'; - CONSTRUCTS_MODULE.import(scope, 'constructs'); - CDK_HANDLER_MODULE.import(scope, 'handler'); - LAMBDA_MODULE.import(scope, 'lambda'); + this.externalModules.forEach(module => scope.addExternalModule(module)); CdkHandlerFrameworkConstructor.forCdkFunction(this); } @@ -94,16 +83,12 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { /** * Builds a `CdkCustomResourceProvider` class. */ - public static buildCdkCustomResourceProvider(scope: Module, props: CdkHandlerClassProps): CdkHandlerFrameworkClass { + public static buildCdkCustomResourceProvider(scope: CdkHandlerFrameworkModule, props: CdkHandlerClassProps): CdkHandlerFrameworkClass { return new (class CdkCustomResourceProvider extends CdkHandlerFrameworkClass { public readonly codeDirectory: string; public readonly entrypoint: string; - public readonly constructs: { [construct: string]: ExternalModule } = { - Construct: CONSTRUCTS_MODULE, - CdkHandlerProps: CDK_HANDLER_MODULE, - CdkHandler: CDK_HANDLER_MODULE, - CustomResourceProviderBase: CORE_MODULE, - }; + + protected readonly externalModules = [CONSTRUCTS_MODULE, CORE_MODULE, CDK_HANDLER_MODULE]; public constructor() { super(scope, { @@ -114,9 +99,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { this.codeDirectory = props.codeDirectory; this.entrypoint = props.entrypoint ?? 'index.handler'; - CONSTRUCTS_MODULE.import(scope, 'constructs'); - CDK_HANDLER_MODULE.import(scope, 'handler'); - CORE_MODULE.import(scope, 'core'); + this.externalModules.forEach(module => scope.addExternalModule(module)); const getOrCreateMethod = this.addMethod({ name: 'getOrCreate', @@ -178,8 +161,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { public abstract readonly entrypoint: string; /** - * A map representing the constructs this class depends on and the external module that the - * consruct can be imported from. + * External modules that this class depends on. */ - public abstract readonly constructs: { [construct: string]: ExternalModule }; + protected abstract readonly externalModules: ExternalModule[]; } diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts index ffa753d4c2bcc..a9f3e81327658 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts @@ -3,7 +3,6 @@ import { ComponentType, ComponentDefinition } from './framework'; type HandlerFrameworkConfig = { [module: string]: { [identifier: string]: ComponentDefinition[] } }; -// output will be packages/aws-cdk-lib/handler-framework///index.generated.ts export const config: HandlerFrameworkConfig = { 'aws-dynamodb': { 'replica-provider': [ diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts index a2831b3d6c1fd..3bc97041142d3 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts @@ -2,6 +2,7 @@ import { ExternalModule, Module, TypeScriptRenderer } from '@cdklabs/typewriter'; import * as fs from 'fs-extra'; import { CdkHandlerClassProps, CdkHandlerFrameworkClass } from './classes'; +import { CDK_HANDLER_MODULE, CONSTRUCTS_MODULE, CORE_MODULE, LAMBDA_MODULE } from './modules'; /** * Handler framework component types. @@ -57,16 +58,16 @@ export interface ComponentDefinition { readonly providerOptions?: any; } -export class CdkHandlerFramework extends Module { +export class CdkHandlerFrameworkModule extends Module { /** * Build a framework module with specified components. */ public static build(components: ComponentDefinition[]) { - return new CdkHandlerFramework(components); + return new CdkHandlerFrameworkModule(components); } private readonly renderer = new TypeScriptRenderer(); - private readonly constructs = new Map(); + private readonly externalModules = new Map(); private constructor(components: ComponentDefinition[]) { super('cdk-handler-framework'); @@ -78,28 +79,23 @@ export class CdkHandlerFramework extends Module { entrypoint: component.entrypoint, }; - let _class: CdkHandlerFrameworkClass; switch (component.type) { case ComponentType.CDK_FUNCTION: { - _class = CdkHandlerFrameworkClass.buildCdkFunction(this, props); + CdkHandlerFrameworkClass.buildCdkFunction(this, props); break; } case ComponentType.CDK_SINGLETON_FUNCTION: { - _class = CdkHandlerFrameworkClass.buildCdkSingletonFunction(this, props); + CdkHandlerFrameworkClass.buildCdkSingletonFunction(this, props); break; } case ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER: { - _class = CdkHandlerFrameworkClass.buildCdkCustomResourceProvider(this, props); + CdkHandlerFrameworkClass.buildCdkCustomResourceProvider(this, props); break; } } - - for (const [construct, module] of Object.entries(_class.constructs)) { - if (!this.constructs.has(construct)) { - this.constructs.set(construct, module); - } - } } + + this.importExternalModules(); } /** @@ -108,4 +104,36 @@ export class CdkHandlerFramework extends Module { public render(outputFileLocation: string) { fs.outputFileSync(`dist/${outputFileLocation}.generated.ts`, this.renderer.render(this)); } + + /** + * Add an external module to be imported. + */ + public addExternalModule(module: ExternalModule) { + if (!this.externalModules.has(module.fqn)) { + this.externalModules.set(module.fqn, true); + } + } + + private importExternalModules() { + for (const fqn of this.externalModules.keys()) { + switch (fqn) { + case CONSTRUCTS_MODULE.fqn: { + CONSTRUCTS_MODULE.import(this, 'constructs'); + break; + } + case CORE_MODULE.fqn: { + CORE_MODULE.import(this, 'core'); + break; + } + case LAMBDA_MODULE.fqn: { + LAMBDA_MODULE.import(this, 'lambda'); + break; + } + case CDK_HANDLER_MODULE.fqn: { + CDK_HANDLER_MODULE.import(this, 'handler'); + break; + } + } + } + } } diff --git a/packages/@aws-cdk/custom-resource-handlers/scripts/minify-and-bundle-sources.ts b/packages/@aws-cdk/custom-resource-handlers/scripts/minify-and-bundle-sources.ts index f326082025972..8b16670d3c3cc 100644 --- a/packages/@aws-cdk/custom-resource-handlers/scripts/minify-and-bundle-sources.ts +++ b/packages/@aws-cdk/custom-resource-handlers/scripts/minify-and-bundle-sources.ts @@ -2,7 +2,7 @@ import * as fs from 'fs'; import * as path from 'path'; import * as esbuild from 'esbuild'; import { config } from '../lib/handler-framework/config'; -import { CdkHandlerFramework, ComponentDefinition } from '../lib/handler-framework/framework'; +import { CdkHandlerFrameworkModule, ComponentDefinition } from '../lib/handler-framework/framework'; const framework: { [outputFileLocation: string]: ComponentDefinition[] } = {}; const entryPoints: string[] = []; @@ -79,7 +79,7 @@ async function main() { recurse(config, []); for (const [outputFileLocation, components] of Object.entries(framework)) { - const module = CdkHandlerFramework.build(components); + const module = CdkHandlerFrameworkModule.build(components); module.render(outputFileLocation); } @@ -97,11 +97,14 @@ async function main() { } function recurse(_config: any, _path: string[]) { + // base case - this is a framework component array and we will build a module with + // all defined components if (_config instanceof Array) { const outputFileLocation = _path.join('/'); framework[outputFileLocation] = _config; return; } + for (const key in _config) { if (_config.hasOwnProperty(key) && typeof _config[key] === 'object') { _path.push(key); From c920b1ef10d35eb44c10a04a6469250f861b288e Mon Sep 17 00:00:00 2001 From: Francis Date: Tue, 5 Dec 2023 23:00:59 -0800 Subject: [PATCH 11/73] refactored airlift custom resource handlers Signed-off-by: Francis --- .../airlift-custom-resource-handlers.sh | 57 ++++++++++--------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh b/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh index 875d5b764401b..7aeeb03a28a22 100755 --- a/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh +++ b/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh @@ -1,36 +1,39 @@ -#!/bin/bash +# !/bin/bash scriptdir=$(cd $(dirname $0) && pwd) -customresourcedir=$(node -p "path.dirname(require.resolve('@aws-cdk/custom-resource-handlers/package.json'))") awscdklibdir=${scriptdir}/.. +customresourcedir=$(node -p "path.dirname(require.resolve('@aws-cdk/custom-resource-handlers/package.json'))") -list_stable_custom_resources() { - for file in $customresourcedir/dist/*[^-alpha]/*/index.*; do - echo $file | rev | cut -d "/" -f 2-4 | rev - done +function airlift() { + mkdir -p $awscdklibdir/custom-resource-handlers/$1 + cp $customresourcedir/$2 $awscdklibdir/custom-resource-handlers/$1 } -list_dependent_python_modules() { - for file in $customresourcedir/dist/*[^-alpha]/*/*/__init__.py; do - echo $file | rev | cut -d "/" -f 2-5 | rev +function recurse() { + local dir=$1 + + for file in $dir/*; do + if [ -f $file ]; then + case $file in + $customresourcedir/dist/*[^-alpha]/*.generated.ts) + cr=$(echo $file | rev | cut -d "/" -f 2-3 | rev) + airlift $cr $cr/*.generated.ts + ;; + $customresourcedir/dist/*[^-alpha]/*/index.*) + cr=$(echo $file | rev | cut -d "/" -f 2-4 | rev) + airlift $cr $cr/index.* + ;; + $customresourcedir/dist/*[^-alpha]/*/*/__init__.py) + cr=$(echo $file | rev | cut -d "/" -f 2-5 | rev) + airlift $cr $cr/__init__.py + ;; + esac + fi + + if [ -d $file ]; then + recurse $file + fi done } -customresources=$(list_stable_custom_resources) -pythonmodules=$(list_dependent_python_modules) - -echo $customresources -echo $pythonmodules - -cd $awscdklibdir -mkdir -p $awscdklibdir/custom-resource-handlers - -for cr in $customresources; do - mkdir -p $awscdklibdir/custom-resource-handlers/$cr - cp $customresourcedir/$cr/index.* $awscdklibdir/custom-resource-handlers/$cr -done - -for pm in $pythonmodules; do - mkdir -p $awscdklibdir/custom-resource-handlers/$pm - cp $customresourcedir/$pm/__init__.py $awscdklibdir/custom-resource-handlers/$pm -done +recurse $customresourcedir/dist From c78566a30afa40b243ef19c8d2e35ed1d655e56f Mon Sep 17 00:00:00 2001 From: Francis Date: Tue, 5 Dec 2023 23:07:16 -0800 Subject: [PATCH 12/73] formatting Signed-off-by: Francis --- .../aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh b/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh index 7aeeb03a28a22..f92b47de5ffff 100755 --- a/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh +++ b/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh @@ -1,4 +1,4 @@ -# !/bin/bash +#!/bin/bash scriptdir=$(cd $(dirname $0) && pwd) awscdklibdir=${scriptdir}/.. From 0f8c8e567f333fa6ace7cf7ca6df9abf72b7e4e6 Mon Sep 17 00:00:00 2001 From: Francis Date: Tue, 5 Dec 2023 23:25:19 -0800 Subject: [PATCH 13/73] exclude dist from compilation Signed-off-by: Francis --- packages/@aws-cdk/custom-resource-handlers/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/@aws-cdk/custom-resource-handlers/tsconfig.json b/packages/@aws-cdk/custom-resource-handlers/tsconfig.json index 8930cca79e2bd..1b180bfa1f956 100644 --- a/packages/@aws-cdk/custom-resource-handlers/tsconfig.json +++ b/packages/@aws-cdk/custom-resource-handlers/tsconfig.json @@ -23,5 +23,6 @@ ], "exclude": [ "**/test/**/*.ts", + "dist" ], } From 00fc75afe6a3655343424cd557ca711ecfaf73b6 Mon Sep 17 00:00:00 2001 From: Francis Date: Wed, 6 Dec 2023 00:25:36 -0800 Subject: [PATCH 14/73] code to generate custom super props Signed-off-by: Francis --- .../lib/handler-framework/classes.ts | 44 +++++++- .../lib/handler-framework/config.ts | 16 ++- .../lib/handler-framework/constructors.ts | 2 + .../lib/handler-framework/framework.ts | 28 +---- .../lib/handler-framework/modules.ts | 6 +- .../handler-framework/lib/cdk-handler.ts | 62 +++++++++++ .../lib/utils/runtime-determiner.ts | 102 ++++++++++++++++++ 7 files changed, 227 insertions(+), 33 deletions(-) create mode 100644 packages/aws-cdk-lib/handler-framework/lib/cdk-handler.ts create mode 100644 packages/aws-cdk-lib/handler-framework/lib/utils/runtime-determiner.ts diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts index 73dfb7b763bd3..b993940007995 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts @@ -1,5 +1,5 @@ /* eslint-disable import/no-extraneous-dependencies */ -import { ClassType, stmt, expr, Type, ExternalModule } from '@cdklabs/typewriter'; +import { ClassType, stmt, expr, Type, ExternalModule, Expression } from '@cdklabs/typewriter'; import { CdkHandlerFrameworkConstructor } from './constructors'; import { CDK_HANDLER_MODULE, CONSTRUCTS_MODULE, LAMBDA_MODULE, CORE_MODULE } from './modules'; import { CdkHandlerFrameworkModule } from './framework'; @@ -25,6 +25,27 @@ export interface CdkHandlerClassProps { * @default 'index.handler' */ readonly entrypoint?: string; + + /** + * A unique identifier to identify this lambda + * + * The identifier should be unique across all custom resource providers. + * We recommend generating a UUID per provider. + * + * Note: This is only required for `CdkSingletonFunction` + */ + readonly uuid?: string; + + /** + * A descriptive name for the purpose of this Lambda. + * + * If the Lambda does not have a physical name, this string will be + * reflected its generated name. The combination of lambdaPurpose + * and uuid must be unique. + * + * @default SingletonLambda + */ + readonly lambdaPurpose?: string; } export abstract class CdkHandlerFrameworkClass extends ClassType { @@ -35,6 +56,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { return new (class CdkFunction extends CdkHandlerFrameworkClass { public readonly codeDirectory: string; public readonly entrypoint: string; + public readonly superProps: [string, Expression][] = []; protected readonly externalModules = [CONSTRUCTS_MODULE, LAMBDA_MODULE, CDK_HANDLER_MODULE]; @@ -47,6 +69,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { this.codeDirectory = props.codeDirectory; this.entrypoint = props.entrypoint ?? 'index.handler'; + this.buildSuperProps(props); this.externalModules.forEach(module => scope.addExternalModule(module)); CdkHandlerFrameworkConstructor.forCdkFunction(this); @@ -61,6 +84,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { return new (class CdkSingletonFunction extends CdkHandlerFrameworkClass { public readonly codeDirectory: string; public readonly entrypoint: string; + public readonly superProps: [string, Expression][] = []; protected readonly externalModules = [CONSTRUCTS_MODULE, LAMBDA_MODULE, CDK_HANDLER_MODULE]; @@ -73,6 +97,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { this.codeDirectory = props.codeDirectory; this.entrypoint = props.entrypoint ?? 'index.handler'; + this.buildSuperProps(props); this.externalModules.forEach(module => scope.addExternalModule(module)); CdkHandlerFrameworkConstructor.forCdkFunction(this); @@ -87,6 +112,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { return new (class CdkCustomResourceProvider extends CdkHandlerFrameworkClass { public readonly codeDirectory: string; public readonly entrypoint: string; + public readonly superProps: [string, Expression][] = []; protected readonly externalModules = [CONSTRUCTS_MODULE, CORE_MODULE, CDK_HANDLER_MODULE]; @@ -99,6 +125,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { this.codeDirectory = props.codeDirectory; this.entrypoint = props.entrypoint ?? 'index.handler'; + this.buildSuperProps(props); this.externalModules.forEach(module => scope.addExternalModule(module)); const getOrCreateMethod = this.addMethod({ @@ -160,8 +187,23 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { */ public abstract readonly entrypoint: string; + /** + * + */ + public abstract readonly superProps: [string, Expression][]; + /** * External modules that this class depends on. */ protected abstract readonly externalModules: ExternalModule[]; + + private buildSuperProps(props: CdkHandlerClassProps) { + if (props.uuid) { + this.superProps.push(['uuid', expr.directCode(`${props.uuid}`)]); + } + + if (props.lambdaPurpose) { + this.superProps.push(['lambdaPurpose', expr.directCode(`${props.lambdaPurpose}`)]); + } + } } diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts index a9f3e81327658..d355418900605 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts @@ -8,13 +8,13 @@ export const config: HandlerFrameworkConfig = { 'replica-provider': [ { type: ComponentType.CDK_FUNCTION, - name: 'ReplicaOnEventProvider', + className: 'ReplicaOnEventProvider', codeDirectory: path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-dynamodb', 'replica-handler'), entrypoint: 'index.onEventHandler', }, { type: ComponentType.CDK_FUNCTION, - name: 'ReplicaOnCompleteProvider', + className: 'ReplicaOnCompleteProvider', codeDirectory: path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-dynamodb', 'replica-handler'), entrypoint: 'index.onCompleteHandler', }, @@ -24,8 +24,18 @@ export const config: HandlerFrameworkConfig = { 'drop-spam-provider': [ { type: ComponentType.CDK_SINGLETON_FUNCTION, - name: 'DropSpamProvider', + className: 'DropSpamProvider', codeDirectory: path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-ses', 'drop-spam-handler'), + uuid: '224e77f9-a32e-4b4d-ac32-983477abba16', + }, + ], + }, + 'aws-cloudfront': { + 'cross-region-reader-provider': [ + { + type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + className: 'CrossRegionReaderProvider', + codeDirectory: path.join(__dirname, '..', '..', '..', 'custom-resource-handlers', 'dist', 'aws-cloudfront', 'edge-function'), }, ], }, diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts index bfe8547d63375..539b358c7112d 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts @@ -12,6 +12,7 @@ export class CdkHandlerFrameworkConstructor { ['code', expr.directCode('cdkHandler.code')], ['handler', expr.directCode('cdkHandler.entrypoint')], ['runtime', expr.directCode('cdkHandler.runtime')], + ..._class.superProps, ]); CdkHandlerFrameworkConstructor.forClass(_class, superProps); } @@ -30,6 +31,7 @@ export class CdkHandlerFrameworkConstructor { const superProps = new ObjectLiteral([ ['codeDirectory', expr.directCode('cdkHandler.codeDirectory')], ['runtimeName', expr.directCode('cdkHandler.runtime.name')], + ..._class.superProps, ]); CdkHandlerFrameworkConstructor.forClass(_class, superProps); } diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts index 3bc97041142d3..886fdecd8dd87 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts @@ -27,35 +27,11 @@ export enum ComponentType { /** * Properties used to generate a specific handler framework component */ -export interface ComponentDefinition { +export interface ComponentDefinition extends CdkHandlerClassProps { /** * The component type to generate. */ readonly type: ComponentType; - - /** - * The name to generate the component with. - * - * Note: This will be the name of the class, i.e., `MyCdkFunction`, etc. - */ - readonly name: string; - - /** - * The local file system directory with the source code. - */ - readonly codeDirectory: string; - - /** - * The name of the method within your code that Lambda calls to execute your function. - * - * @default 'index.handler' - */ - readonly entrypoint?: string; - - /** - * Configurable options for the underlying Lambda function. - */ - readonly providerOptions?: any; } export class CdkHandlerFrameworkModule extends Module { @@ -75,7 +51,7 @@ export class CdkHandlerFrameworkModule extends Module { for (let component of components) { const props: CdkHandlerClassProps = { codeDirectory: component.codeDirectory, - className: component.name, + className: component.className, entrypoint: component.entrypoint, }; diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts index 4ea7bec3b7f0c..5de5d52bb83ed 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts @@ -15,7 +15,7 @@ class CdkHandlerModule extends ExternalModule { public readonly CdkHandlerProps = Type.fromName(this, 'CdkHandlerProps'); public constructor() { - super('../../handler-framework/lib/cdk-handler'); + super('../../../handler-framework/lib/cdk-handler'); } } @@ -24,7 +24,7 @@ class CoreModule extends ExternalModule { public readonly CustomResourceProviderOptions = Type.fromName(this, 'CustomResourceProviderOptions'); public constructor() { - super('../../core'); + super('../../../core'); } } @@ -34,7 +34,7 @@ class LambdaModule extends ExternalModule { public readonly FunctionOptions = Type.fromName(this, 'FunctionOptions'); public constructor() { - super('../../aws-lambda'); + super('../../../aws-lambda'); } } diff --git a/packages/aws-cdk-lib/handler-framework/lib/cdk-handler.ts b/packages/aws-cdk-lib/handler-framework/lib/cdk-handler.ts new file mode 100644 index 0000000000000..833979393e8c2 --- /dev/null +++ b/packages/aws-cdk-lib/handler-framework/lib/cdk-handler.ts @@ -0,0 +1,62 @@ +import { Construct } from 'constructs'; +import { RuntimeDeterminer } from './utils/runtime-determiner'; +import { Code, Runtime } from '../../aws-lambda'; + +/** + * Properties used to initialize `CdkHandler`. + */ +export interface CdkHandlerProps { + /** + * A local file system directory with the provider's code. The code will be + * bundled into a zip asset and wired to the provider's AWS Lambda function. + */ + readonly codeDirectory: string; + + /** + * Runtimes that are compatible with the source code. + */ + readonly compatibleRuntimes: Runtime[]; + + /** + * The name of the method within your code that Lambda calls to execute your function. + */ + readonly entrypoint: string; +} + +/** + * Represents an instance of `CdkHandler`. + */ +export class CdkHandler extends Construct { + /** + * The latest nodejs runtime version available across all AWS regions + */ + private static readonly DEFAULT_RUNTIME = Runtime.NODEJS_LATEST; + + /** + * The source code loaded from a local disk path. + */ + public readonly codeDirectory: string; + + /** + * The source code of your Lambda function. + */ + public readonly code: Code; + + /** + * The name of the method within your code that Lambda calls to execute your function. + */ + public readonly entrypoint: string; + + /** + * The latest runtime that is compatible with the source code. + */ + public readonly runtime: Runtime; + + public constructor(scope: Construct, id: string, props: CdkHandlerProps) { + super(scope, id); + this.codeDirectory = props.codeDirectory; + this.code = Code.fromAsset(props.codeDirectory); + this.entrypoint = props.entrypoint; + this.runtime = RuntimeDeterminer.determineLatestRuntime(CdkHandler.DEFAULT_RUNTIME, props.compatibleRuntimes); + } +} diff --git a/packages/aws-cdk-lib/handler-framework/lib/utils/runtime-determiner.ts b/packages/aws-cdk-lib/handler-framework/lib/utils/runtime-determiner.ts new file mode 100644 index 0000000000000..348478c3a9478 --- /dev/null +++ b/packages/aws-cdk-lib/handler-framework/lib/utils/runtime-determiner.ts @@ -0,0 +1,102 @@ +import { Runtime, RuntimeFamily } from '../../../aws-lambda'; + +/** + * A utility class used to determine the latest runtime for a specific runtime family + */ +export class RuntimeDeterminer { + /** + * Determines the latest runtime from a list of runtimes. + * + * Note: runtimes must only be nodejs or python. Nodejs runtimes will be given preference over + * python runtimes. + * + * @param runtimes the list of runtimes to search in + * @returns the latest nodejs or python runtime found, otherwise undefined if no nodejs or python + * runtimes are specified + */ + public static determineLatestRuntime(defaultRuntime: Runtime, runtimes: Runtime[]) { + if (runtimes.length === 0) { + throw new Error('You must specify at least one compatible runtime'); + } + + const nodeJsRuntimes = runtimes.filter(runtime => runtime.family === RuntimeFamily.NODEJS); + const latestNodeJsRuntime = RuntimeDeterminer.latestNodeJsRuntime(defaultRuntime, nodeJsRuntimes); + if (latestNodeJsRuntime !== undefined) { + return latestNodeJsRuntime; + } + + const pythonRuntimes = runtimes.filter(runtime => runtime.family === RuntimeFamily.PYTHON); + const latestPythonRuntime = RuntimeDeterminer.latestPythonRuntime(pythonRuntimes); + if (latestPythonRuntime !== undefined) { + return latestPythonRuntime; + } + + throw new Error('Compatible runtimes must contain only nodejs or python runtimes'); + } + + private static latestNodeJsRuntime(defaultRuntime: Runtime, nodeJsRuntimes: Runtime[]) { + if (nodeJsRuntimes.length === 0) { + return undefined; + } + + if (nodeJsRuntimes.some(runtime => runtime.runtimeEquals(defaultRuntime))) { + return defaultRuntime; + } + + let latestRuntime = nodeJsRuntimes[0]; + for (let idx = 1; idx < nodeJsRuntimes.length; idx++) { + latestRuntime = RuntimeDeterminer.latestRuntime(latestRuntime, nodeJsRuntimes[idx], RuntimeFamily.NODEJS); + } + + return latestRuntime; + } + + private static latestPythonRuntime(pythonRuntimes: Runtime[]) { + if (pythonRuntimes.length === 0) { + return undefined; + } + + let latestRuntime = pythonRuntimes[0]; + for (let idx = 1; idx < pythonRuntimes.length; idx++) { + latestRuntime = RuntimeDeterminer.latestRuntime(latestRuntime, pythonRuntimes[idx], RuntimeFamily.PYTHON); + } + + return latestRuntime; + } + + private static latestRuntime(runtime1: Runtime, runtime2: Runtime, family: RuntimeFamily) { + let sliceStart: number; + switch (family) { + case RuntimeFamily.NODEJS: { + sliceStart = 'nodejs'.length; + break; + } + case RuntimeFamily.PYTHON: { + sliceStart = 'python'.length; + break; + } + default: { + sliceStart = 0; + break; + } + } + + const version1 = runtime1.name.slice(sliceStart).split('.'); + const version2 = runtime2.name.slice(sliceStart).split('.'); + + const versionLength = Math.min(version1.length, version2.length); + for (let idx = 0; idx < versionLength; idx++) { + if (parseInt(version1[idx]) > parseInt(version2[idx])) { + return runtime1; + } + + if (parseInt(version1[idx]) < parseInt(version2[idx])) { + return runtime2; + } + } + + return runtime1; + } + + private constructor() {} +} From ba9c570a19324e45faaa95f1e9ea3114ead89fd9 Mon Sep 17 00:00:00 2001 From: Francis Date: Wed, 6 Dec 2023 00:48:17 -0800 Subject: [PATCH 15/73] path import and path literal in config Signed-off-by: Francis --- .../lib/handler-framework/classes.ts | 6 +++--- .../lib/handler-framework/config.ts | 9 ++++----- .../lib/handler-framework/constructors.ts | 2 +- .../lib/handler-framework/framework.ts | 7 +++++-- .../lib/handler-framework/modules.ts | 7 +++++++ 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts index b993940007995..9688babf2b369 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts @@ -166,7 +166,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { }); getOrCreateProviderMethod.addBody( stmt.constVar(expr.ident('id'), expr.directCode('`${uniqueid}CustomResourceProvider`')), - stmt.constVar(expr.ident('stack'), expr.directCode('Stack.of(scope)')), + stmt.constVar(expr.ident('stack'), expr.directCode('cdk.Stack.of(scope)')), stmt.constVar(expr.ident('existing'), expr.directCode(`stack.node.tryFindChild(id) as ${this.type}`)), stmt.ret(expr.directCode(`existing ?? new ${this.name}(scope, id)`)), ); @@ -199,11 +199,11 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { private buildSuperProps(props: CdkHandlerClassProps) { if (props.uuid) { - this.superProps.push(['uuid', expr.directCode(`${props.uuid}`)]); + this.superProps.push(['uuid', expr.lit(props.uuid)]); } if (props.lambdaPurpose) { - this.superProps.push(['lambdaPurpose', expr.directCode(`${props.lambdaPurpose}`)]); + this.superProps.push(['lambdaPurpose', expr.lit(props.lambdaPurpose)]); } } } diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts index d355418900605..96dc75e4147ae 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts @@ -1,4 +1,3 @@ -import * as path from 'path'; import { ComponentType, ComponentDefinition } from './framework'; type HandlerFrameworkConfig = { [module: string]: { [identifier: string]: ComponentDefinition[] } }; @@ -9,13 +8,13 @@ export const config: HandlerFrameworkConfig = { { type: ComponentType.CDK_FUNCTION, className: 'ReplicaOnEventProvider', - codeDirectory: path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-dynamodb', 'replica-handler'), + codeDirectory: "path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-dynamodb', 'replica-handler')", entrypoint: 'index.onEventHandler', }, { type: ComponentType.CDK_FUNCTION, className: 'ReplicaOnCompleteProvider', - codeDirectory: path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-dynamodb', 'replica-handler'), + codeDirectory: "path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-dynamodb', 'replica-handler')", entrypoint: 'index.onCompleteHandler', }, ], @@ -25,7 +24,7 @@ export const config: HandlerFrameworkConfig = { { type: ComponentType.CDK_SINGLETON_FUNCTION, className: 'DropSpamProvider', - codeDirectory: path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-ses', 'drop-spam-handler'), + codeDirectory: "path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-ses', 'drop-spam-handler')", uuid: '224e77f9-a32e-4b4d-ac32-983477abba16', }, ], @@ -35,7 +34,7 @@ export const config: HandlerFrameworkConfig = { { type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, className: 'CrossRegionReaderProvider', - codeDirectory: path.join(__dirname, '..', '..', '..', 'custom-resource-handlers', 'dist', 'aws-cloudfront', 'edge-function'), + codeDirectory: "path.join(__dirname, '..', '..', '..', 'custom-resource-handlers', 'dist', 'aws-cloudfront', 'edge-function')", }, ], }, diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts index 539b358c7112d..36ad8a019e6ae 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts @@ -53,7 +53,7 @@ export class CdkHandlerFrameworkConstructor { stmt.constVar( expr.directCode('cdkHandlerProps: handler.CdkHandlerProps'), expr.object({ - codeDirectory: expr.lit(`${_class.codeDirectory}`), + codeDirectory: expr.directCode(_class.codeDirectory), entrypoint: _class.entrypoint ? expr.lit(`${_class.entrypoint}`) : expr.lit('index.handler'), diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts index 886fdecd8dd87..19656eeb1ce0b 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts @@ -2,7 +2,7 @@ import { ExternalModule, Module, TypeScriptRenderer } from '@cdklabs/typewriter'; import * as fs from 'fs-extra'; import { CdkHandlerClassProps, CdkHandlerFrameworkClass } from './classes'; -import { CDK_HANDLER_MODULE, CONSTRUCTS_MODULE, CORE_MODULE, LAMBDA_MODULE } from './modules'; +import { CDK_HANDLER_MODULE, CONSTRUCTS_MODULE, CORE_MODULE, LAMBDA_MODULE, PATH_MODULE } from './modules'; /** * Handler framework component types. @@ -53,6 +53,8 @@ export class CdkHandlerFrameworkModule extends Module { codeDirectory: component.codeDirectory, className: component.className, entrypoint: component.entrypoint, + uuid: component.uuid, + lambdaPurpose: component.lambdaPurpose, }; switch (component.type) { @@ -91,6 +93,7 @@ export class CdkHandlerFrameworkModule extends Module { } private importExternalModules() { + PATH_MODULE.import(this, 'path'); for (const fqn of this.externalModules.keys()) { switch (fqn) { case CONSTRUCTS_MODULE.fqn: { @@ -98,7 +101,7 @@ export class CdkHandlerFrameworkModule extends Module { break; } case CORE_MODULE.fqn: { - CORE_MODULE.import(this, 'core'); + CORE_MODULE.import(this, 'cdk'); break; } case LAMBDA_MODULE.fqn: { diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts index 5de5d52bb83ed..6d18f53c91686 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts @@ -1,6 +1,12 @@ /* eslint-disable import/no-extraneous-dependencies */ import { ExternalModule, Type } from '@cdklabs/typewriter'; +class PathModule extends ExternalModule { + public constructor() { + super('path'); + } +} + class ConstructsModule extends ExternalModule { public readonly Construct = Type.fromName(this, 'Construct'); public readonly IConstruct = Type.fromName(this, 'IConstruct'); @@ -38,6 +44,7 @@ class LambdaModule extends ExternalModule { } } +export const PATH_MODULE = new PathModule(); export const CONSTRUCTS_MODULE = new ConstructsModule(); export const CDK_HANDLER_MODULE = new CdkHandlerModule(); export const CORE_MODULE = new CoreModule(); From 3f450da3fcd3ee96b16c2e38968d50832d1fcb78 Mon Sep 17 00:00:00 2001 From: Francis Date: Wed, 6 Dec 2023 00:49:50 -0800 Subject: [PATCH 16/73] remove cdk handler and runtime determiner Signed-off-by: Francis --- .../handler-framework/lib/cdk-handler.ts | 62 ----------- .../lib/utils/runtime-determiner.ts | 102 ------------------ 2 files changed, 164 deletions(-) delete mode 100644 packages/aws-cdk-lib/handler-framework/lib/cdk-handler.ts delete mode 100644 packages/aws-cdk-lib/handler-framework/lib/utils/runtime-determiner.ts diff --git a/packages/aws-cdk-lib/handler-framework/lib/cdk-handler.ts b/packages/aws-cdk-lib/handler-framework/lib/cdk-handler.ts deleted file mode 100644 index 833979393e8c2..0000000000000 --- a/packages/aws-cdk-lib/handler-framework/lib/cdk-handler.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { Construct } from 'constructs'; -import { RuntimeDeterminer } from './utils/runtime-determiner'; -import { Code, Runtime } from '../../aws-lambda'; - -/** - * Properties used to initialize `CdkHandler`. - */ -export interface CdkHandlerProps { - /** - * A local file system directory with the provider's code. The code will be - * bundled into a zip asset and wired to the provider's AWS Lambda function. - */ - readonly codeDirectory: string; - - /** - * Runtimes that are compatible with the source code. - */ - readonly compatibleRuntimes: Runtime[]; - - /** - * The name of the method within your code that Lambda calls to execute your function. - */ - readonly entrypoint: string; -} - -/** - * Represents an instance of `CdkHandler`. - */ -export class CdkHandler extends Construct { - /** - * The latest nodejs runtime version available across all AWS regions - */ - private static readonly DEFAULT_RUNTIME = Runtime.NODEJS_LATEST; - - /** - * The source code loaded from a local disk path. - */ - public readonly codeDirectory: string; - - /** - * The source code of your Lambda function. - */ - public readonly code: Code; - - /** - * The name of the method within your code that Lambda calls to execute your function. - */ - public readonly entrypoint: string; - - /** - * The latest runtime that is compatible with the source code. - */ - public readonly runtime: Runtime; - - public constructor(scope: Construct, id: string, props: CdkHandlerProps) { - super(scope, id); - this.codeDirectory = props.codeDirectory; - this.code = Code.fromAsset(props.codeDirectory); - this.entrypoint = props.entrypoint; - this.runtime = RuntimeDeterminer.determineLatestRuntime(CdkHandler.DEFAULT_RUNTIME, props.compatibleRuntimes); - } -} diff --git a/packages/aws-cdk-lib/handler-framework/lib/utils/runtime-determiner.ts b/packages/aws-cdk-lib/handler-framework/lib/utils/runtime-determiner.ts deleted file mode 100644 index 348478c3a9478..0000000000000 --- a/packages/aws-cdk-lib/handler-framework/lib/utils/runtime-determiner.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { Runtime, RuntimeFamily } from '../../../aws-lambda'; - -/** - * A utility class used to determine the latest runtime for a specific runtime family - */ -export class RuntimeDeterminer { - /** - * Determines the latest runtime from a list of runtimes. - * - * Note: runtimes must only be nodejs or python. Nodejs runtimes will be given preference over - * python runtimes. - * - * @param runtimes the list of runtimes to search in - * @returns the latest nodejs or python runtime found, otherwise undefined if no nodejs or python - * runtimes are specified - */ - public static determineLatestRuntime(defaultRuntime: Runtime, runtimes: Runtime[]) { - if (runtimes.length === 0) { - throw new Error('You must specify at least one compatible runtime'); - } - - const nodeJsRuntimes = runtimes.filter(runtime => runtime.family === RuntimeFamily.NODEJS); - const latestNodeJsRuntime = RuntimeDeterminer.latestNodeJsRuntime(defaultRuntime, nodeJsRuntimes); - if (latestNodeJsRuntime !== undefined) { - return latestNodeJsRuntime; - } - - const pythonRuntimes = runtimes.filter(runtime => runtime.family === RuntimeFamily.PYTHON); - const latestPythonRuntime = RuntimeDeterminer.latestPythonRuntime(pythonRuntimes); - if (latestPythonRuntime !== undefined) { - return latestPythonRuntime; - } - - throw new Error('Compatible runtimes must contain only nodejs or python runtimes'); - } - - private static latestNodeJsRuntime(defaultRuntime: Runtime, nodeJsRuntimes: Runtime[]) { - if (nodeJsRuntimes.length === 0) { - return undefined; - } - - if (nodeJsRuntimes.some(runtime => runtime.runtimeEquals(defaultRuntime))) { - return defaultRuntime; - } - - let latestRuntime = nodeJsRuntimes[0]; - for (let idx = 1; idx < nodeJsRuntimes.length; idx++) { - latestRuntime = RuntimeDeterminer.latestRuntime(latestRuntime, nodeJsRuntimes[idx], RuntimeFamily.NODEJS); - } - - return latestRuntime; - } - - private static latestPythonRuntime(pythonRuntimes: Runtime[]) { - if (pythonRuntimes.length === 0) { - return undefined; - } - - let latestRuntime = pythonRuntimes[0]; - for (let idx = 1; idx < pythonRuntimes.length; idx++) { - latestRuntime = RuntimeDeterminer.latestRuntime(latestRuntime, pythonRuntimes[idx], RuntimeFamily.PYTHON); - } - - return latestRuntime; - } - - private static latestRuntime(runtime1: Runtime, runtime2: Runtime, family: RuntimeFamily) { - let sliceStart: number; - switch (family) { - case RuntimeFamily.NODEJS: { - sliceStart = 'nodejs'.length; - break; - } - case RuntimeFamily.PYTHON: { - sliceStart = 'python'.length; - break; - } - default: { - sliceStart = 0; - break; - } - } - - const version1 = runtime1.name.slice(sliceStart).split('.'); - const version2 = runtime2.name.slice(sliceStart).split('.'); - - const versionLength = Math.min(version1.length, version2.length); - for (let idx = 0; idx < versionLength; idx++) { - if (parseInt(version1[idx]) > parseInt(version2[idx])) { - return runtime1; - } - - if (parseInt(version1[idx]) < parseInt(version2[idx])) { - return runtime2; - } - } - - return runtime1; - } - - private constructor() {} -} From ba044b65fe3e4063bc02ae998d759669982e5beb Mon Sep 17 00:00:00 2001 From: Francis Date: Wed, 6 Dec 2023 09:34:17 -0800 Subject: [PATCH 17/73] compatible framework runtimes Signed-off-by: Francis --- .../lib/handler-framework/classes.ts | 36 +++++++++++++++++++ .../lib/handler-framework/config.ts | 5 +++ .../lib/handler-framework/constructors.ts | 1 + .../lib/handler-framework/framework.ts | 1 + 4 files changed, 43 insertions(+) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts index 9688babf2b369..759687e2dff82 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts @@ -4,6 +4,26 @@ import { CdkHandlerFrameworkConstructor } from './constructors'; import { CDK_HANDLER_MODULE, CONSTRUCTS_MODULE, LAMBDA_MODULE, CORE_MODULE } from './modules'; import { CdkHandlerFrameworkModule } from './framework'; +/** + * Runtimes that map to a Lambda runtime. + */ +export enum FrameworkRuntime { + /** + * The NodeJs 16.x runtime. + */ + NODEJS_16_X = 'lambda.Runtime.NODEJS_16_X', + + /** + * The NodeJS 18.x runtime. + */ + NODEJS_18_X = 'lambda.Runtime.NODEJS_18_X', + + /** + * The NodeJS 20.x runtime. + */ + NODEJS_20_X = 'lambda.Runtime.NODEJS_20_X', +} + /** * Initialization properties used to build a `CdkHandlerFrameworkClass` instance. */ @@ -19,6 +39,11 @@ export interface CdkHandlerClassProps { */ readonly codeDirectory: string; + /** + * + */ + readonly compatibleRuntimes: FrameworkRuntime[]; + /** * The name of the method within your code that Lambda calls to execute your function. * @@ -56,6 +81,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { return new (class CdkFunction extends CdkHandlerFrameworkClass { public readonly codeDirectory: string; public readonly entrypoint: string; + public readonly compatibleRuntimes: FrameworkRuntime[]; public readonly superProps: [string, Expression][] = []; protected readonly externalModules = [CONSTRUCTS_MODULE, LAMBDA_MODULE, CDK_HANDLER_MODULE]; @@ -68,6 +94,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { }); this.codeDirectory = props.codeDirectory; this.entrypoint = props.entrypoint ?? 'index.handler'; + this.compatibleRuntimes = props.compatibleRuntimes; this.buildSuperProps(props); this.externalModules.forEach(module => scope.addExternalModule(module)); @@ -84,6 +111,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { return new (class CdkSingletonFunction extends CdkHandlerFrameworkClass { public readonly codeDirectory: string; public readonly entrypoint: string; + public readonly compatibleRuntimes: FrameworkRuntime[]; public readonly superProps: [string, Expression][] = []; protected readonly externalModules = [CONSTRUCTS_MODULE, LAMBDA_MODULE, CDK_HANDLER_MODULE]; @@ -96,6 +124,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { }); this.codeDirectory = props.codeDirectory; this.entrypoint = props.entrypoint ?? 'index.handler'; + this.compatibleRuntimes = props.compatibleRuntimes; this.buildSuperProps(props); this.externalModules.forEach(module => scope.addExternalModule(module)); @@ -112,6 +141,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { return new (class CdkCustomResourceProvider extends CdkHandlerFrameworkClass { public readonly codeDirectory: string; public readonly entrypoint: string; + public readonly compatibleRuntimes: FrameworkRuntime[]; public readonly superProps: [string, Expression][] = []; protected readonly externalModules = [CONSTRUCTS_MODULE, CORE_MODULE, CDK_HANDLER_MODULE]; @@ -124,6 +154,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { }); this.codeDirectory = props.codeDirectory; this.entrypoint = props.entrypoint ?? 'index.handler'; + this.compatibleRuntimes = props.compatibleRuntimes; this.buildSuperProps(props); this.externalModules.forEach(module => scope.addExternalModule(module)); @@ -187,6 +218,11 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { */ public abstract readonly entrypoint: string; + /** + * + */ + public abstract readonly compatibleRuntimes: FrameworkRuntime[]; + /** * */ diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts index 96dc75e4147ae..bf84fe29d9aaf 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts @@ -1,4 +1,5 @@ import { ComponentType, ComponentDefinition } from './framework'; +import { FrameworkRuntime } from './classes'; type HandlerFrameworkConfig = { [module: string]: { [identifier: string]: ComponentDefinition[] } }; @@ -9,12 +10,14 @@ export const config: HandlerFrameworkConfig = { type: ComponentType.CDK_FUNCTION, className: 'ReplicaOnEventProvider', codeDirectory: "path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-dynamodb', 'replica-handler')", + compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], entrypoint: 'index.onEventHandler', }, { type: ComponentType.CDK_FUNCTION, className: 'ReplicaOnCompleteProvider', codeDirectory: "path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-dynamodb', 'replica-handler')", + compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], entrypoint: 'index.onCompleteHandler', }, ], @@ -25,6 +28,7 @@ export const config: HandlerFrameworkConfig = { type: ComponentType.CDK_SINGLETON_FUNCTION, className: 'DropSpamProvider', codeDirectory: "path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-ses', 'drop-spam-handler')", + compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], uuid: '224e77f9-a32e-4b4d-ac32-983477abba16', }, ], @@ -35,6 +39,7 @@ export const config: HandlerFrameworkConfig = { type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, className: 'CrossRegionReaderProvider', codeDirectory: "path.join(__dirname, '..', '..', '..', 'custom-resource-handlers', 'dist', 'aws-cloudfront', 'edge-function')", + compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], }, ], }, diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts index 36ad8a019e6ae..70131dc90806f 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts @@ -54,6 +54,7 @@ export class CdkHandlerFrameworkConstructor { expr.directCode('cdkHandlerProps: handler.CdkHandlerProps'), expr.object({ codeDirectory: expr.directCode(_class.codeDirectory), + compatibleRuntimes: expr.directCode(`[${ _class.compatibleRuntimes }]`), entrypoint: _class.entrypoint ? expr.lit(`${_class.entrypoint}`) : expr.lit('index.handler'), diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts index 19656eeb1ce0b..2a37636c02c93 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts @@ -52,6 +52,7 @@ export class CdkHandlerFrameworkModule extends Module { const props: CdkHandlerClassProps = { codeDirectory: component.codeDirectory, className: component.className, + compatibleRuntimes: component.compatibleRuntimes, entrypoint: component.entrypoint, uuid: component.uuid, lambdaPurpose: component.lambdaPurpose, From 43e60d702434681bd3d52ec0713a4f401fe6c962 Mon Sep 17 00:00:00 2001 From: Francis Date: Wed, 6 Dec 2023 14:03:17 -0800 Subject: [PATCH 18/73] singleton interface creation Signed-off-by: Francis --- .../lib/handler-framework/classes.ts | 100 ++++++++++-------- .../lib/handler-framework/config.ts | 52 ++++++++- .../lib/handler-framework/constructors.ts | 17 +-- .../lib/handler-framework/framework.ts | 19 +++- 4 files changed, 134 insertions(+), 54 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts index 759687e2dff82..abb4aab614113 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts @@ -1,15 +1,15 @@ /* eslint-disable import/no-extraneous-dependencies */ -import { ClassType, stmt, expr, Type, ExternalModule, Expression } from '@cdklabs/typewriter'; +import { ClassType, stmt, expr, Type, ExternalModule, PropertySpec, InterfaceSpec, InterfaceType } from '@cdklabs/typewriter'; import { CdkHandlerFrameworkConstructor } from './constructors'; import { CDK_HANDLER_MODULE, CONSTRUCTS_MODULE, LAMBDA_MODULE, CORE_MODULE } from './modules'; import { CdkHandlerFrameworkModule } from './framework'; /** - * Runtimes that map to a Lambda runtime. + * Runtimes that map to a Lambda runtime during codegen. */ export enum FrameworkRuntime { /** - * The NodeJs 16.x runtime. + * The NodeJS 16.x runtime. */ NODEJS_16_X = 'lambda.Runtime.NODEJS_16_X', @@ -22,6 +22,11 @@ export enum FrameworkRuntime { * The NodeJS 20.x runtime. */ NODEJS_20_X = 'lambda.Runtime.NODEJS_20_X', + + /** + * The Python 3.9 runtime. + */ + PYTHON_3_9 = 'lambda.Runtime.PYTHON_3_9', } /** @@ -40,7 +45,7 @@ export interface CdkHandlerClassProps { readonly codeDirectory: string; /** - * + * Runtimes that are compatible with the source code. */ readonly compatibleRuntimes: FrameworkRuntime[]; @@ -50,27 +55,6 @@ export interface CdkHandlerClassProps { * @default 'index.handler' */ readonly entrypoint?: string; - - /** - * A unique identifier to identify this lambda - * - * The identifier should be unique across all custom resource providers. - * We recommend generating a UUID per provider. - * - * Note: This is only required for `CdkSingletonFunction` - */ - readonly uuid?: string; - - /** - * A descriptive name for the purpose of this Lambda. - * - * If the Lambda does not have a physical name, this string will be - * reflected its generated name. The combination of lambdaPurpose - * and uuid must be unique. - * - * @default SingletonLambda - */ - readonly lambdaPurpose?: string; } export abstract class CdkHandlerFrameworkClass extends ClassType { @@ -82,7 +66,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { public readonly codeDirectory: string; public readonly entrypoint: string; public readonly compatibleRuntimes: FrameworkRuntime[]; - public readonly superProps: [string, Expression][] = []; + public readonly constructorPropsType = LAMBDA_MODULE.FunctionOptions; protected readonly externalModules = [CONSTRUCTS_MODULE, LAMBDA_MODULE, CDK_HANDLER_MODULE]; @@ -96,7 +80,6 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { this.entrypoint = props.entrypoint ?? 'index.handler'; this.compatibleRuntimes = props.compatibleRuntimes; - this.buildSuperProps(props); this.externalModules.forEach(module => scope.addExternalModule(module)); CdkHandlerFrameworkConstructor.forCdkFunction(this); @@ -112,7 +95,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { public readonly codeDirectory: string; public readonly entrypoint: string; public readonly compatibleRuntimes: FrameworkRuntime[]; - public readonly superProps: [string, Expression][] = []; + public readonly constructorPropsType: Type; protected readonly externalModules = [CONSTRUCTS_MODULE, LAMBDA_MODULE, CDK_HANDLER_MODULE]; @@ -126,9 +109,34 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { this.entrypoint = props.entrypoint ?? 'index.handler'; this.compatibleRuntimes = props.compatibleRuntimes; - this.buildSuperProps(props); this.externalModules.forEach(module => scope.addExternalModule(module)); + const uuid: PropertySpec = { + name: 'uuid', + type: Type.STRING, + docs: { + summary: 'A unique identifier to identify this Lambda.\n\nThe identifier should be unique across all custom resource providers.\nWe recommend generating a UUID per provider.', + }, + }; + const lambdaPurpose: PropertySpec = { + name: 'lambdaPurpose', + type: Type.STRING, + optional: true, + docs: { + summary: 'A descriptive name for the purpose of this Lambda.\n\nIf the Lambda does not have a physical name, this string will be\nreflected in its generated name. The combination of lambdaPurpose\nand uuid must be unique.', + docTags: { + default: 'SingletonLambda', + }, + }, + }; + const _interface = this.getOrCreateInterface(scope, { + name: 'CdkSingletonFunctionProps', + export: true, + extends: [LAMBDA_MODULE.FunctionOptions], + properties: [uuid, lambdaPurpose], + }); + this.constructorPropsType = _interface.type; + CdkHandlerFrameworkConstructor.forCdkFunction(this); } })(); @@ -142,7 +150,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { public readonly codeDirectory: string; public readonly entrypoint: string; public readonly compatibleRuntimes: FrameworkRuntime[]; - public readonly superProps: [string, Expression][] = []; + public readonly constructorPropsType = CORE_MODULE.CustomResourceProviderOptions; protected readonly externalModules = [CONSTRUCTS_MODULE, CORE_MODULE, CDK_HANDLER_MODULE]; @@ -156,7 +164,6 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { this.entrypoint = props.entrypoint ?? 'index.handler'; this.compatibleRuntimes = props.compatibleRuntimes; - this.buildSuperProps(props); this.externalModules.forEach(module => scope.addExternalModule(module)); const getOrCreateMethod = this.addMethod({ @@ -175,8 +182,12 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { name: 'uniqueid', type: Type.STRING, }); + getOrCreateMethod.addParameter({ + name: 'props', + type: CORE_MODULE.CustomResourceProviderOptions, + }); getOrCreateMethod.addBody( - stmt.ret(expr.directCode('this.getOrCreateProvider(scope, uniqueid).serviceToken')), + stmt.ret(expr.directCode('this.getOrCreateProvider(scope, uniqueid, props).serviceToken')), ); const getOrCreateProviderMethod = this.addMethod({ @@ -195,11 +206,15 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { name: 'uniqueid', type: Type.STRING, }); + getOrCreateProviderMethod.addParameter({ + name: 'props', + type: CORE_MODULE.CustomResourceProviderOptions, + }); getOrCreateProviderMethod.addBody( stmt.constVar(expr.ident('id'), expr.directCode('`${uniqueid}CustomResourceProvider`')), stmt.constVar(expr.ident('stack'), expr.directCode('cdk.Stack.of(scope)')), stmt.constVar(expr.ident('existing'), expr.directCode(`stack.node.tryFindChild(id) as ${this.type}`)), - stmt.ret(expr.directCode(`existing ?? new ${this.name}(scope, id)`)), + stmt.ret(expr.directCode(`existing ?? new ${this.name}(scope, id, props)`)), ); CdkHandlerFrameworkConstructor.forCdkCustomResourceProvider(this); @@ -219,27 +234,28 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { public abstract readonly entrypoint: string; /** - * + * Runtimes that are compatible with the code that this class will execute. */ public abstract readonly compatibleRuntimes: FrameworkRuntime[]; /** - * + * Properties used to initialize this class. */ - public abstract readonly superProps: [string, Expression][]; + public abstract readonly constructorPropsType: Type; /** * External modules that this class depends on. */ protected abstract readonly externalModules: ExternalModule[]; - private buildSuperProps(props: CdkHandlerClassProps) { - if (props.uuid) { - this.superProps.push(['uuid', expr.lit(props.uuid)]); + private getOrCreateInterface(scope: CdkHandlerFrameworkModule, spec: InterfaceSpec) { + const existing = scope.getInterface(spec.name); + if (existing) { + return existing; } - if (props.lambdaPurpose) { - this.superProps.push(['lambdaPurpose', expr.lit(props.lambdaPurpose)]); - } + const _interface = new InterfaceType(scope, { ...spec }); + scope.registerInterface(_interface); + return _interface; } } diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts index bf84fe29d9aaf..6829d046bb237 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts @@ -4,6 +4,47 @@ import { FrameworkRuntime } from './classes'; type HandlerFrameworkConfig = { [module: string]: { [identifier: string]: ComponentDefinition[] } }; export const config: HandlerFrameworkConfig = { + 'aws-certificatemanager': { + 'certificate-request-provider': [ + { + type: ComponentType.CDK_FUNCTION, + className: 'CertificateRequestProvider', + codeDirectory: "path.resolve(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-certificatemanager', 'dns-validated-certificate-handler')", + compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + }, + ], + }, + 'aws-ec2': { + 'restrict-default-sg-provider': [ + { + type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + className: 'RestrictDefaultSgProvider', + codeDirectory: "path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-ec2', 'restrict-default-security-group-handler')", + compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + }, + ], + }, + 'aws-ecr': { + 'auto-delete-images-provider': [ + { + type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + className: 'AutoDeleteImagesProvider', + codeDirectory: "path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-ecr', 'auto-delete-images-handler')", + compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + }, + ], + }, + 'aws-ecs': { + 'drain-hook-provider': [ + { + type: ComponentType.CDK_FUNCTION, + className: 'DrainHookProvider', + codeDirectory: "path.join(__dirname, '..', '..', '..', 'custom-resource-handlers', 'dist', 'aws-ecs', 'lambda-source')", + compatibleRuntimes: [FrameworkRuntime.PYTHON_3_9], + entrypoint: 'index.lambda_handler', + }, + ], + }, 'aws-dynamodb': { 'replica-provider': [ { @@ -22,6 +63,16 @@ export const config: HandlerFrameworkConfig = { }, ], }, + 'aws-events-targets': { + 'rule-target-provider': [ + { + type: ComponentType.CDK_SINGLETON_FUNCTION, + className: 'RuleTargetProvider', + codeDirectory: "path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-events-targets', 'aws-api-handler')", + compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + }, + ], + }, 'aws-ses': { 'drop-spam-provider': [ { @@ -29,7 +80,6 @@ export const config: HandlerFrameworkConfig = { className: 'DropSpamProvider', codeDirectory: "path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-ses', 'drop-spam-handler')", compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], - uuid: '224e77f9-a32e-4b4d-ac32-983477abba16', }, ], }, diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts index 70131dc90806f..89ac8ed99e3c2 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts @@ -1,5 +1,5 @@ /* eslint-disable import/no-extraneous-dependencies */ -import { Expression, Type, stmt, expr, SuperInitializer, ObjectLiteral } from '@cdklabs/typewriter'; +import { Expression, Type, stmt, expr, SuperInitializer, ObjectLiteral, Splat } from '@cdklabs/typewriter'; import { CdkHandlerFrameworkClass } from './classes'; import { CONSTRUCTS_MODULE } from './modules'; @@ -9,10 +9,10 @@ export class CdkHandlerFrameworkConstructor { */ public static forCdkFunction(_class: CdkHandlerFrameworkClass) { const superProps = new ObjectLiteral([ + new Splat(expr.ident('props')), ['code', expr.directCode('cdkHandler.code')], ['handler', expr.directCode('cdkHandler.entrypoint')], ['runtime', expr.directCode('cdkHandler.runtime')], - ..._class.superProps, ]); CdkHandlerFrameworkConstructor.forClass(_class, superProps); } @@ -29,14 +29,14 @@ export class CdkHandlerFrameworkConstructor { */ public static forCdkCustomResourceProvider(_class: CdkHandlerFrameworkClass) { const superProps = new ObjectLiteral([ + new Splat(expr.ident('props')), ['codeDirectory', expr.directCode('cdkHandler.codeDirectory')], ['runtimeName', expr.directCode('cdkHandler.runtime.name')], - ..._class.superProps, ]); CdkHandlerFrameworkConstructor.forClass(_class, superProps); } - private static forClass(_class: CdkHandlerFrameworkClass, superProps?: Expression) { + private static forClass(_class: CdkHandlerFrameworkClass, superProps: Expression) { // constructor const init = _class.addInitializer({}); const scope = init.addParameter({ @@ -47,6 +47,10 @@ export class CdkHandlerFrameworkConstructor { name: 'id', type: Type.STRING, }); + init.addParameter({ + name: 'props', + type: _class.constructorPropsType, + }); // build CdkHandler instance init.addBody( @@ -69,10 +73,7 @@ export class CdkHandlerFrameworkConstructor { ); // build super call - const superInitializerArgs: Expression[] = [scope, id]; - if (superProps) { - superInitializerArgs.push(superProps); - } + const superInitializerArgs: Expression[] = [scope, id, superProps]; init.addBody(new SuperInitializer(...superInitializerArgs)); } diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts index 2a37636c02c93..5e53ce32bd2dd 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts @@ -1,5 +1,5 @@ /* eslint-disable import/no-extraneous-dependencies */ -import { ExternalModule, Module, TypeScriptRenderer } from '@cdklabs/typewriter'; +import { ExternalModule, InterfaceType, Module, TypeScriptRenderer } from '@cdklabs/typewriter'; import * as fs from 'fs-extra'; import { CdkHandlerClassProps, CdkHandlerFrameworkClass } from './classes'; import { CDK_HANDLER_MODULE, CONSTRUCTS_MODULE, CORE_MODULE, LAMBDA_MODULE, PATH_MODULE } from './modules'; @@ -44,6 +44,7 @@ export class CdkHandlerFrameworkModule extends Module { private readonly renderer = new TypeScriptRenderer(); private readonly externalModules = new Map(); + private readonly _interfaces = new Map(); private constructor(components: ComponentDefinition[]) { super('cdk-handler-framework'); @@ -54,8 +55,6 @@ export class CdkHandlerFrameworkModule extends Module { className: component.className, compatibleRuntimes: component.compatibleRuntimes, entrypoint: component.entrypoint, - uuid: component.uuid, - lambdaPurpose: component.lambdaPurpose, }; switch (component.type) { @@ -93,6 +92,20 @@ export class CdkHandlerFrameworkModule extends Module { } } + /** + * Register an interface with this module. + */ + public registerInterface(_interface: InterfaceType) { + this._interfaces.set(_interface.name, _interface); + } + + /** + * Retrieve an interface that has been registered with this module. + */ + public getInterface(name: string) { + return this._interfaces.get(name); + } + private importExternalModules() { PATH_MODULE.import(this, 'path'); for (const fqn of this.externalModules.keys()) { From 8ef2e155f2da9d9e16fde6be720263abb8c6c110 Mon Sep 17 00:00:00 2001 From: Francis Date: Wed, 6 Dec 2023 15:42:24 -0800 Subject: [PATCH 19/73] refactor for generate script Signed-off-by: Francis --- .../lib/handler-framework/classes.ts | 12 +- .../lib/handler-framework/config.ts | 138 ++++++++--------- .../lib/handler-framework/constructors.ts | 2 +- .../lib/handler-framework/framework.ts | 90 ++++------- .../custom-resource-handlers/package.json | 2 +- .../scripts/generate.ts | 131 ++++++++++++++++ .../scripts/minify-and-bundle-sources.ts | 143 ------------------ 7 files changed, 231 insertions(+), 287 deletions(-) create mode 100644 packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts delete mode 100644 packages/@aws-cdk/custom-resource-handlers/scripts/minify-and-bundle-sources.ts diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts index abb4aab614113..0fdd2b25fbac3 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts @@ -1,8 +1,8 @@ /* eslint-disable import/no-extraneous-dependencies */ import { ClassType, stmt, expr, Type, ExternalModule, PropertySpec, InterfaceSpec, InterfaceType } from '@cdklabs/typewriter'; import { CdkHandlerFrameworkConstructor } from './constructors'; -import { CDK_HANDLER_MODULE, CONSTRUCTS_MODULE, LAMBDA_MODULE, CORE_MODULE } from './modules'; import { CdkHandlerFrameworkModule } from './framework'; +import { CDK_HANDLER_MODULE, CONSTRUCTS_MODULE, LAMBDA_MODULE, CORE_MODULE } from './modules'; /** * Runtimes that map to a Lambda runtime during codegen. @@ -34,9 +34,9 @@ export enum FrameworkRuntime { */ export interface CdkHandlerClassProps { /** - * The name of the class. + * The name of the component class. */ - readonly className: string; + readonly name: string; /** * A local file system directory with the provider's code. The code will be @@ -72,7 +72,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { public constructor() { super(scope, { - name: props.className, + name: props.name, extends: LAMBDA_MODULE.Function, export: true, }); @@ -101,7 +101,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { public constructor() { super(scope, { - name: props.className, + name: props.name, extends: LAMBDA_MODULE.SingletonFunction, export: true, }); @@ -156,7 +156,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { public constructor() { super(scope, { - name: props.className, + name: props.name, extends: CORE_MODULE.CustomResourceProviderBase, export: true, }); diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts index 6829d046bb237..9e533a3c3aa7c 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts @@ -1,15 +1,72 @@ -import { ComponentType, ComponentDefinition } from './framework'; +import * as path from 'path'; import { FrameworkRuntime } from './classes'; -type HandlerFrameworkConfig = { [module: string]: { [identifier: string]: ComponentDefinition[] } }; +/** + * Handler framework component types. + */ +export enum ComponentType { + /** + * `CdkFunction` + */ + CDK_FUNCTION = 'CdkFunction', + + /** + * `CdkSingletonFunction` + */ + CDK_SINGLETON_FUNCTION = 'CdkSingletonFunction', + + /** + * `CdkCustomResourceProvider` + */ + CDK_CUSTOM_RESOURCE_PROVIDER = 'CdkCustomResourceProvider', +} + +/** + * Properites used to initialize individual handler framework components. + */ +export interface ConfigProps { + /** + * The component type to generate. + */ + readonly type: ComponentType; + + /** + * + */ + readonly sourceCode: string; + + /** + * The name of the component class to generate, i.e., `MyCdkFunction`. + */ + readonly name: string; + + /** + * Runtimes that are compatible with the source code. + */ + readonly compatibleRuntimes: FrameworkRuntime[]; + + /** + * The name of the method within your code that Lambda calls to execute your function. + * + * @default 'index.handler' + */ + readonly entrypoint?: string; + + /** + * @default false + */ + readonly disableBundleAndMinify?: boolean; +} + +type HandlerFrameworkConfig = { [module: string]: { [identifier: string]: ConfigProps[] } }; export const config: HandlerFrameworkConfig = { 'aws-certificatemanager': { 'certificate-request-provider': [ { type: ComponentType.CDK_FUNCTION, - className: 'CertificateRequestProvider', - codeDirectory: "path.resolve(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-certificatemanager', 'dns-validated-certificate-handler')", + name: 'CertificateRequestProvider', + sourceCode: path.resolve(__dirname, '..', 'aws-certificatemanager', 'dns-validated-certificate-handler', 'index.js'), compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], }, ], @@ -18,77 +75,8 @@ export const config: HandlerFrameworkConfig = { 'restrict-default-sg-provider': [ { type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, - className: 'RestrictDefaultSgProvider', - codeDirectory: "path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-ec2', 'restrict-default-security-group-handler')", - compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], - }, - ], - }, - 'aws-ecr': { - 'auto-delete-images-provider': [ - { - type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, - className: 'AutoDeleteImagesProvider', - codeDirectory: "path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-ecr', 'auto-delete-images-handler')", - compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], - }, - ], - }, - 'aws-ecs': { - 'drain-hook-provider': [ - { - type: ComponentType.CDK_FUNCTION, - className: 'DrainHookProvider', - codeDirectory: "path.join(__dirname, '..', '..', '..', 'custom-resource-handlers', 'dist', 'aws-ecs', 'lambda-source')", - compatibleRuntimes: [FrameworkRuntime.PYTHON_3_9], - entrypoint: 'index.lambda_handler', - }, - ], - }, - 'aws-dynamodb': { - 'replica-provider': [ - { - type: ComponentType.CDK_FUNCTION, - className: 'ReplicaOnEventProvider', - codeDirectory: "path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-dynamodb', 'replica-handler')", - compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], - entrypoint: 'index.onEventHandler', - }, - { - type: ComponentType.CDK_FUNCTION, - className: 'ReplicaOnCompleteProvider', - codeDirectory: "path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-dynamodb', 'replica-handler')", - compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], - entrypoint: 'index.onCompleteHandler', - }, - ], - }, - 'aws-events-targets': { - 'rule-target-provider': [ - { - type: ComponentType.CDK_SINGLETON_FUNCTION, - className: 'RuleTargetProvider', - codeDirectory: "path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-events-targets', 'aws-api-handler')", - compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], - }, - ], - }, - 'aws-ses': { - 'drop-spam-provider': [ - { - type: ComponentType.CDK_SINGLETON_FUNCTION, - className: 'DropSpamProvider', - codeDirectory: "path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-ses', 'drop-spam-handler')", - compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], - }, - ], - }, - 'aws-cloudfront': { - 'cross-region-reader-provider': [ - { - type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, - className: 'CrossRegionReaderProvider', - codeDirectory: "path.join(__dirname, '..', '..', '..', 'custom-resource-handlers', 'dist', 'aws-cloudfront', 'edge-function')", + name: 'RestrictDefaultSgProvider', + sourceCode: path.resolve(__dirname, '..', 'aws-ec2', 'restrict-default-security-group-handler', 'index.ts'), compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], }, ], diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts index 89ac8ed99e3c2..026dd9f408227 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts @@ -57,7 +57,7 @@ export class CdkHandlerFrameworkConstructor { stmt.constVar( expr.directCode('cdkHandlerProps: handler.CdkHandlerProps'), expr.object({ - codeDirectory: expr.directCode(_class.codeDirectory), + codeDirectory: expr.lit(_class.codeDirectory), compatibleRuntimes: expr.directCode(`[${ _class.compatibleRuntimes }]`), entrypoint: _class.entrypoint ? expr.lit(`${_class.entrypoint}`) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts index 5e53ce32bd2dd..bc82cb9918abe 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts @@ -2,84 +2,52 @@ import { ExternalModule, InterfaceType, Module, TypeScriptRenderer } from '@cdklabs/typewriter'; import * as fs from 'fs-extra'; import { CdkHandlerClassProps, CdkHandlerFrameworkClass } from './classes'; +import { ComponentType, ConfigProps } from './config'; import { CDK_HANDLER_MODULE, CONSTRUCTS_MODULE, CORE_MODULE, LAMBDA_MODULE, PATH_MODULE } from './modules'; -/** - * Handler framework component types. - */ -export enum ComponentType { - /** - * `CdkFunction` - */ - CDK_FUNCTION = 'CdkFunction', - - /** - * `CdkSingletonFunction` - */ - CDK_SINGLETON_FUNCTION = 'CdkSingletonFunction', - - /** - * `CdkCustomResourceProvider` - */ - CDK_CUSTOM_RESOURCE_PROVIDER = 'CdkCustomResourceProvider', -} - -/** - * Properties used to generate a specific handler framework component - */ -export interface ComponentDefinition extends CdkHandlerClassProps { - /** - * The component type to generate. - */ - readonly type: ComponentType; -} - export class CdkHandlerFrameworkModule extends Module { - /** - * Build a framework module with specified components. - */ - public static build(components: ComponentDefinition[]) { - return new CdkHandlerFrameworkModule(components); - } - private readonly renderer = new TypeScriptRenderer(); private readonly externalModules = new Map(); private readonly _interfaces = new Map(); - private constructor(components: ComponentDefinition[]) { - super('cdk-handler-framework'); + public constructor(fqn: string) { + super(fqn); + } - for (let component of components) { - const props: CdkHandlerClassProps = { - codeDirectory: component.codeDirectory, - className: component.className, - compatibleRuntimes: component.compatibleRuntimes, - entrypoint: component.entrypoint, - }; + /** + * + * @param component + * @param sourceCodeDirectory + */ + public build(component: ConfigProps, sourceCodeDirectory: string) { + const props: CdkHandlerClassProps = { + codeDirectory: sourceCodeDirectory, + name: component.name, + compatibleRuntimes: component.compatibleRuntimes, + entrypoint: component.entrypoint, + }; - switch (component.type) { - case ComponentType.CDK_FUNCTION: { - CdkHandlerFrameworkClass.buildCdkFunction(this, props); - break; - } - case ComponentType.CDK_SINGLETON_FUNCTION: { - CdkHandlerFrameworkClass.buildCdkSingletonFunction(this, props); - break; - } - case ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER: { - CdkHandlerFrameworkClass.buildCdkCustomResourceProvider(this, props); - break; - } + switch (component.type) { + case ComponentType.CDK_FUNCTION: { + CdkHandlerFrameworkClass.buildCdkFunction(this, props); + break; + } + case ComponentType.CDK_SINGLETON_FUNCTION: { + CdkHandlerFrameworkClass.buildCdkSingletonFunction(this, props); + break; + } + case ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER: { + CdkHandlerFrameworkClass.buildCdkCustomResourceProvider(this, props); + break; } } - - this.importExternalModules(); } /** * Render built framework into an output file. */ public render(outputFileLocation: string) { + this.importExternalModules(); fs.outputFileSync(`dist/${outputFileLocation}.generated.ts`, this.renderer.render(this)); } diff --git a/packages/@aws-cdk/custom-resource-handlers/package.json b/packages/@aws-cdk/custom-resource-handlers/package.json index d177996b09edd..7e0a748fdd879 100644 --- a/packages/@aws-cdk/custom-resource-handlers/package.json +++ b/packages/@aws-cdk/custom-resource-handlers/package.json @@ -4,7 +4,7 @@ "private": true, "version": "0.0.0", "scripts": { - "build": "tsc -b && node scripts/minify-and-bundle-sources.js", + "build": "tsc -b && node scripts/generate.js", "integ": "integ-runner", "lint": "cdk-lint", "package": "cdk-package", diff --git a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts new file mode 100644 index 0000000000000..ec3ec977349cb --- /dev/null +++ b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts @@ -0,0 +1,131 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import * as esbuild from 'esbuild'; +import { config, ConfigProps } from '../lib/handler-framework/config'; +import { CdkHandlerFrameworkModule } from '../lib/handler-framework/framework'; + +/* eslint-disable no-console */ + +const framework: { [renderLocation: string]: ConfigProps[] } = {}; + +async function main() { + recurse(config, []); + for (const [renderLocation, components] of Object.entries(framework)) { + console.log(renderLocation); + const module = new CdkHandlerFrameworkModule('cdk-handler-framework'); + for (const component of components) { + const outfile = calculateOutfile(component.sourceCode); + if (component.disableBundleAndMinify) { + fs.mkdirSync(path.dirname(outfile), { recursive: true }); + fs.copyFileSync(component.sourceCode, outfile); + } else { + await bundleAndMinify(component.sourceCode, outfile); + } + const sourceCodeDirectory = path.dirname(outfile).split('/').pop(); + module.build(component, `./${sourceCodeDirectory}`); + } + module.render(renderLocation); + } + + function recurse(_config: any, _path: string[]) { + // base case - this is a framework component array and we will build a module with + // all defined components + if (_config instanceof Array) { + const outputFileLocation = _path.join('/'); + framework[outputFileLocation] = _config; + return; + } + + for (const key in _config) { + if (_config.hasOwnProperty(key) && typeof _config[key] === 'object') { + _path.push(key); + recurse(_config[key], _path); + _path.pop(); // backtrack + } + } + } +} + +async function bundleAndMinify(infile: string, outfile: string) { + const result = await esbuild.build({ + entryPoints: [infile], + outfile, + external: ['@aws-sdk/*', 'aws-sdk'], + format: 'cjs', + platform: 'node', + bundle: true, + minify: true, + minifyWhitespace: true, + minifySyntax: true, + minifyIdentifiers: true, + sourcemap: false, + tsconfig: 'tsconfig.json', + + // These should be checked because they can lead to runtime failures. There are + // false positives, and the esbuild API does not provide a way to suppress them, + // so we need to do some postprocessing. + logOverride: { + 'unsupported-dynamic-import': 'warning', + 'unsupported-require-call': 'warning', + 'indirect-require': 'warning', + }, + logLevel: 'error', + }); + + const failures = [ + ...result.errors, + ...ignoreWarnings(result), + ]; + + if (failures.length > 0) { + const messages = esbuild.formatMessagesSync(failures, { + kind: 'error', + color: true, + }); + // eslint-disable-next-line no-console + console.log(messages.join('\n')); + // eslint-disable-next-line no-console + console.log(`${messages.length} errors. For false positives, put '// esbuild-disable - ' on the line before`); + process.exitCode = 1; + } +} + +function calculateOutfile(file: string) { + // turn ts extension into js extension + if (file.includes('index.ts')) { + file = path.join(path.dirname(file), path.basename(file, path.extname(file)) + '.js'); + } + + // replace /lib with /dist + const fileContents = file.split(path.sep); + fileContents[fileContents.lastIndexOf('lib')] = 'dist'; + + return fileContents.join(path.sep); +} + +function ignoreWarnings(result: esbuild.BuildResult) { + const ret: esbuild.Message[] = []; + for (const warning of result.warnings) { + let suppressed = false; + if (warning.location?.file) { + const contents = fs.readFileSync(warning.location.file, { encoding: 'utf-8' }); + const lines = contents.split('\n'); + const lineBefore = lines[warning.location.line - 1 - 1]; + + if (lineBefore.includes(`esbuild-disable ${warning.id}`)) { + suppressed = true; + } + } + + if (!suppressed) { + ret.push(warning); + } + } + return ret; +} + +main().catch((e) => { + // eslint-disable-next-line no-console + console.error(e); + process.exitCode = 1; +}); diff --git a/packages/@aws-cdk/custom-resource-handlers/scripts/minify-and-bundle-sources.ts b/packages/@aws-cdk/custom-resource-handlers/scripts/minify-and-bundle-sources.ts deleted file mode 100644 index 8b16670d3c3cc..0000000000000 --- a/packages/@aws-cdk/custom-resource-handlers/scripts/minify-and-bundle-sources.ts +++ /dev/null @@ -1,143 +0,0 @@ -import * as fs from 'fs'; -import * as path from 'path'; -import * as esbuild from 'esbuild'; -import { config } from '../lib/handler-framework/config'; -import { CdkHandlerFrameworkModule, ComponentDefinition } from '../lib/handler-framework/framework'; - -const framework: { [outputFileLocation: string]: ComponentDefinition[] } = {}; -const entryPoints: string[] = []; - -function recFolderStructure(fileOrDir: string) { - if (fs.statSync(fileOrDir).isDirectory()) { - const items = fs.readdirSync(fileOrDir); - for (const i of items) { - recFolderStructure(path.join(fileOrDir, i)); - } - } else { - if (!fileOrDir.includes('nodejs-entrypoint-handler/index.ts') && - ['index.ts', 'index.js', 'index.py', '__init__.py'].some(fileName => fileOrDir.includes(fileName))) { - entryPoints.push(fileOrDir); - } - } -} - -async function main() { - const bindingsDir = path.join(__dirname, '..', 'lib'); - - recFolderStructure(bindingsDir); - - for (const ep of entryPoints) { - // Do not bundle python files. Additionally, do not bundle nodejs-entrypoint-handler due to require import - if (['index.py', '__init__.py', 'nodejs-entrypoint-handler/index.js'].some(fileName => ep.includes(fileName))) { - const outfile = calculateOutfile(ep); - fs.mkdirSync(path.dirname(outfile), { recursive: true }); - fs.copyFileSync(ep, outfile); - } else { - const result = await esbuild.build({ - entryPoints: [ep], - outfile: calculateOutfile(ep), - external: ['@aws-sdk/*', 'aws-sdk'], - format: 'cjs', - platform: 'node', - bundle: true, - minify: true, - minifyWhitespace: true, - minifySyntax: true, - minifyIdentifiers: true, - sourcemap: false, - tsconfig: 'tsconfig.json', - - // These should be checked because they can lead to runtime failures. There are - // false positives, and the esbuild API does not provide a way to suppress them, - // so we need to do some postprocessing. - logOverride: { - 'unsupported-dynamic-import': 'warning', - 'unsupported-require-call': 'warning', - 'indirect-require': 'warning', - }, - logLevel: 'error', - }); - - const failures = [ - ...result.errors, - ...ignoreWarnings(result), - ]; - - if (failures.length > 0) { - const messages = esbuild.formatMessagesSync(failures, { - kind: 'error', - color: true, - }); - // eslint-disable-next-line no-console - console.log(messages.join('\n')); - // eslint-disable-next-line no-console - console.log(`${messages.length} errors. For false positives, put '// esbuild-disable - ' on the line before`); - process.exitCode = 1; - } - } - } - - recurse(config, []); - for (const [outputFileLocation, components] of Object.entries(framework)) { - const module = CdkHandlerFrameworkModule.build(components); - module.render(outputFileLocation); - } - - function calculateOutfile(file: string) { - // turn ts extension into js extension - if (file.includes('index.ts')) { - file = path.join(path.dirname(file), path.basename(file, path.extname(file)) + '.js'); - } - - // replace /lib with /dist - const fileContents = file.split(path.sep); - fileContents[fileContents.lastIndexOf('lib')] = 'dist'; - - return fileContents.join(path.sep); - } - - function recurse(_config: any, _path: string[]) { - // base case - this is a framework component array and we will build a module with - // all defined components - if (_config instanceof Array) { - const outputFileLocation = _path.join('/'); - framework[outputFileLocation] = _config; - return; - } - - for (const key in _config) { - if (_config.hasOwnProperty(key) && typeof _config[key] === 'object') { - _path.push(key); - recurse(_config[key], _path); - _path.pop(); // backtrack - } - } - } -} - -function ignoreWarnings(result: esbuild.BuildResult) { - const ret: esbuild.Message[] = []; - for (const warning of result.warnings) { - let suppressed = false; - if (warning.location?.file) { - const contents = fs.readFileSync(warning.location.file, { encoding: 'utf-8' }); - const lines = contents.split('\n'); - const lineBefore = lines[warning.location.line - 1 - 1]; - - if (lineBefore.includes(`esbuild-disable ${warning.id}`)) { - suppressed = true; - } - } - - if (!suppressed) { - ret.push(warning); - } - } - return ret; -} - -main().catch((e) => { - // eslint-disable-next-line no-console - console.error(e); - process.exitCode = 1; -}); \ No newline at end of file From 7ef51398be88cbde3c7fb211101a976cf9746dbf Mon Sep 17 00:00:00 2001 From: Francis Date: Wed, 6 Dec 2023 15:44:18 -0800 Subject: [PATCH 20/73] removed unneeded console log Signed-off-by: Francis --- packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts index ec3ec977349cb..83a020abc8e4a 100644 --- a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts +++ b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts @@ -4,14 +4,11 @@ import * as esbuild from 'esbuild'; import { config, ConfigProps } from '../lib/handler-framework/config'; import { CdkHandlerFrameworkModule } from '../lib/handler-framework/framework'; -/* eslint-disable no-console */ - const framework: { [renderLocation: string]: ConfigProps[] } = {}; async function main() { recurse(config, []); for (const [renderLocation, components] of Object.entries(framework)) { - console.log(renderLocation); const module = new CdkHandlerFrameworkModule('cdk-handler-framework'); for (const component of components) { const outfile = calculateOutfile(component.sourceCode); From 0c45784345944d371b46bc5eb63d177876544248 Mon Sep 17 00:00:00 2001 From: Francis Date: Wed, 6 Dec 2023 16:54:25 -0800 Subject: [PATCH 21/73] added to config Signed-off-by: Francis --- .../lib/handler-framework/config.ts | 70 ++++++++++++++++++- .../lib/handler-framework/framework.ts | 3 +- .../lib/handler-framework/modules.ts | 7 -- 3 files changed, 70 insertions(+), 10 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts index 9e533a3c3aa7c..667c1fef8ca7f 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts @@ -65,12 +65,40 @@ export const config: HandlerFrameworkConfig = { 'certificate-request-provider': [ { type: ComponentType.CDK_FUNCTION, - name: 'CertificateRequestProvider', + name: 'CertificateRequestFunction', sourceCode: path.resolve(__dirname, '..', 'aws-certificatemanager', 'dns-validated-certificate-handler', 'index.js'), compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], }, ], }, + 'aws-cloudfront': { + 'cross-region-string-param-reader-provider': [ + { + type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + name: 'CrossRegionStringParamReaderProvider', + sourceCode: path.resolve(__dirname, '..', 'aws-cloudfront', 'edge-function', 'index.js'), + compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + }, + ], + }, + 'aws-dynamodb': { + 'replica-provider': [ + { + type: ComponentType.CDK_FUNCTION, + name: 'OnEventFunction', + sourceCode: path.resolve(__dirname, '..', 'aws-dynamodb', 'replica-handler', 'index.ts'), + compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + entrypoint: 'index.onEventHandler', + }, + { + type: ComponentType.CDK_FUNCTION, + name: 'IsCompleteFunction', + sourceCode: path.resolve(__dirname, '..', 'aws-dynamodb', 'replica-handler', 'index.ts'), + compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + entrypoint: 'index.isCompleteHandler', + }, + ], + }, 'aws-ec2': { 'restrict-default-sg-provider': [ { @@ -81,4 +109,44 @@ export const config: HandlerFrameworkConfig = { }, ], }, + 'aws-ecr': { + 'auto-delete-images-provider': [ + { + type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + name: 'AutoDeleteImagesProvider', + sourceCode: path.resolve(__dirname, '..', 'aws-ecr', 'auto-delete-images-handler', 'index.ts'), + compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + }, + ], + }, + 'aws-ecs': { + 'drain-hook-provider': [ + { + type: ComponentType.CDK_FUNCTION, + name: 'DrainHookFunction', + sourceCode: path.resolve(__dirname, '..', 'aws-ecs', 'lambda-source', 'index.py'), + compatibleRuntimes: [FrameworkRuntime.PYTHON_3_9], + entrypoint: 'index.lambda_handler', + disableBundleAndMinify: true, + }, + ], + }, + 'aws-eks': { + 'cluster-resource-provider': [ + { + type: ComponentType.CDK_FUNCTION, + name: 'OnEventFunction', + sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'cluster-resource-handler', 'index.ts'), + compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + entrypoint: 'index.onEvent', + }, + { + type: ComponentType.CDK_FUNCTION, + name: 'IsCompleteFunction', + sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'cluster-resource-handler', 'index.ts'), + compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + entrypoint: 'index.isComplete', + }, + ], + }, }; diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts index bc82cb9918abe..8056039fb4e7f 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts @@ -3,7 +3,7 @@ import { ExternalModule, InterfaceType, Module, TypeScriptRenderer } from '@cdkl import * as fs from 'fs-extra'; import { CdkHandlerClassProps, CdkHandlerFrameworkClass } from './classes'; import { ComponentType, ConfigProps } from './config'; -import { CDK_HANDLER_MODULE, CONSTRUCTS_MODULE, CORE_MODULE, LAMBDA_MODULE, PATH_MODULE } from './modules'; +import { CDK_HANDLER_MODULE, CONSTRUCTS_MODULE, CORE_MODULE, LAMBDA_MODULE } from './modules'; export class CdkHandlerFrameworkModule extends Module { private readonly renderer = new TypeScriptRenderer(); @@ -75,7 +75,6 @@ export class CdkHandlerFrameworkModule extends Module { } private importExternalModules() { - PATH_MODULE.import(this, 'path'); for (const fqn of this.externalModules.keys()) { switch (fqn) { case CONSTRUCTS_MODULE.fqn: { diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts index 6d18f53c91686..5de5d52bb83ed 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts @@ -1,12 +1,6 @@ /* eslint-disable import/no-extraneous-dependencies */ import { ExternalModule, Type } from '@cdklabs/typewriter'; -class PathModule extends ExternalModule { - public constructor() { - super('path'); - } -} - class ConstructsModule extends ExternalModule { public readonly Construct = Type.fromName(this, 'Construct'); public readonly IConstruct = Type.fromName(this, 'IConstruct'); @@ -44,7 +38,6 @@ class LambdaModule extends ExternalModule { } } -export const PATH_MODULE = new PathModule(); export const CONSTRUCTS_MODULE = new ConstructsModule(); export const CDK_HANDLER_MODULE = new CdkHandlerModule(); export const CORE_MODULE = new CoreModule(); From 1647a821325fbf9d6b26d90bc75fe816bfe22bfe Mon Sep 17 00:00:00 2001 From: Francis Date: Wed, 6 Dec 2023 17:47:18 -0800 Subject: [PATCH 22/73] no op Signed-off-by: Francis --- .../lib/handler-framework/classes.ts | 5 +++ .../lib/handler-framework/config.ts | 42 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts index 0fdd2b25fbac3..b88a038b99090 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts @@ -27,6 +27,11 @@ export enum FrameworkRuntime { * The Python 3.9 runtime. */ PYTHON_3_9 = 'lambda.Runtime.PYTHON_3_9', + + /** + * The Python 3.10 runtime. + */ + PYTHON_3_10 = 'lambda.Runtime.PYTHON_3_10', } /** diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts index 667c1fef8ca7f..884711a2eb88e 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts @@ -19,6 +19,11 @@ export enum ComponentType { * `CdkCustomResourceProvider` */ CDK_CUSTOM_RESOURCE_PROVIDER = 'CdkCustomResourceProvider', + + /** + * Do not create a framework component. This is used to just move source code for airlifting. + */ + NO_OP = 'NoOp' } /** @@ -148,5 +153,42 @@ export const config: HandlerFrameworkConfig = { entrypoint: 'index.isComplete', }, ], + 'kubectl-provider': [ + { + type: ComponentType.CDK_FUNCTION, + name: 'KubectlFunction', + sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'index.py'), + compatibleRuntimes: [FrameworkRuntime.PYTHON_3_10], + disableBundleAndMinify: true, + }, + { + type: ComponentType.NO_OP, + name: 'ApplyHandler', + sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'apply', '__init__.py'), + compatibleRuntimes: [FrameworkRuntime.PYTHON_3_10], + disableBundleAndMinify: true, + }, + { + type: ComponentType.NO_OP, + name: 'GetHandler', + sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'get', '__init__.py'), + compatibleRuntimes: [FrameworkRuntime.PYTHON_3_10], + disableBundleAndMinify: true, + }, + { + type: ComponentType.NO_OP, + name: 'HelmHandler', + sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'helm', '__init__.py'), + compatibleRuntimes: [FrameworkRuntime.PYTHON_3_10], + disableBundleAndMinify: true, + }, + { + type: ComponentType.NO_OP, + name: 'PatchHandler', + sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'patch', '__init__.py'), + compatibleRuntimes: [FrameworkRuntime.PYTHON_3_10], + disableBundleAndMinify: true, + }, + ], }, }; From 7da55a3cfde304198aa8bfd65d30f64b95afb682 Mon Sep 17 00:00:00 2001 From: Francis Date: Wed, 6 Dec 2023 22:56:21 -0800 Subject: [PATCH 23/73] config Signed-off-by: Francis --- .../lib/handler-framework/config.ts | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts index 884711a2eb88e..4b338e22b233b 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts @@ -23,7 +23,7 @@ export enum ComponentType { /** * Do not create a framework component. This is used to just move source code for airlifting. */ - NO_OP = 'NoOp' + CDK_NO_OP = 'CdkNoOp', } /** @@ -36,7 +36,7 @@ export interface ConfigProps { readonly type: ComponentType; /** - * + * The source code that will be executed by the provider. */ readonly sourceCode: string; @@ -162,28 +162,28 @@ export const config: HandlerFrameworkConfig = { disableBundleAndMinify: true, }, { - type: ComponentType.NO_OP, + type: ComponentType.CDK_NO_OP, name: 'ApplyHandler', sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'apply', '__init__.py'), compatibleRuntimes: [FrameworkRuntime.PYTHON_3_10], disableBundleAndMinify: true, }, { - type: ComponentType.NO_OP, + type: ComponentType.CDK_NO_OP, name: 'GetHandler', sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'get', '__init__.py'), compatibleRuntimes: [FrameworkRuntime.PYTHON_3_10], disableBundleAndMinify: true, }, { - type: ComponentType.NO_OP, + type: ComponentType.CDK_NO_OP, name: 'HelmHandler', sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'helm', '__init__.py'), compatibleRuntimes: [FrameworkRuntime.PYTHON_3_10], disableBundleAndMinify: true, }, { - type: ComponentType.NO_OP, + type: ComponentType.CDK_NO_OP, name: 'PatchHandler', sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'patch', '__init__.py'), compatibleRuntimes: [FrameworkRuntime.PYTHON_3_10], @@ -191,4 +191,14 @@ export const config: HandlerFrameworkConfig = { }, ], }, + 'aws-events-targets': { + 'aws-api-provider': [ + { + type: ComponentType.CDK_SINGLETON_FUNCTION, + name: 'AwsApiFunction', + sourceCode: path.resolve(__dirname, '..', 'aws-events-targets', 'aws-api-handler', 'index.ts'), + compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + }, + ], + }, }; From 2210613d6f72abfda316e55ef3d5238280cb622e Mon Sep 17 00:00:00 2001 From: Francis Date: Thu, 7 Dec 2023 01:04:16 -0800 Subject: [PATCH 24/73] refactor for cdk handler changes and added immutable to property spec for singleton props interface Signed-off-by: Francis --- .../lib/handler-framework/classes.ts | 2 ++ .../lib/handler-framework/constructors.ts | 7 ++----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts index b88a038b99090..1c725992ec927 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts @@ -119,6 +119,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { const uuid: PropertySpec = { name: 'uuid', type: Type.STRING, + immutable: true, docs: { summary: 'A unique identifier to identify this Lambda.\n\nThe identifier should be unique across all custom resource providers.\nWe recommend generating a UUID per provider.', }, @@ -126,6 +127,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { const lambdaPurpose: PropertySpec = { name: 'lambdaPurpose', type: Type.STRING, + immutable: true, optional: true, docs: { summary: 'A descriptive name for the purpose of this Lambda.\n\nIf the Lambda does not have a physical name, this string will be\nreflected in its generated name. The combination of lambdaPurpose\nand uuid must be unique.', diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts index 026dd9f408227..da1571f7ab33e 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts @@ -11,7 +11,7 @@ export class CdkHandlerFrameworkConstructor { const superProps = new ObjectLiteral([ new Splat(expr.ident('props')), ['code', expr.directCode('cdkHandler.code')], - ['handler', expr.directCode('cdkHandler.entrypoint')], + ['handler', expr.lit(_class.entrypoint)], ['runtime', expr.directCode('cdkHandler.runtime')], ]); CdkHandlerFrameworkConstructor.forClass(_class, superProps); @@ -30,7 +30,7 @@ export class CdkHandlerFrameworkConstructor { public static forCdkCustomResourceProvider(_class: CdkHandlerFrameworkClass) { const superProps = new ObjectLiteral([ new Splat(expr.ident('props')), - ['codeDirectory', expr.directCode('cdkHandler.codeDirectory')], + ['codeDirectory', expr.lit(_class.codeDirectory)], ['runtimeName', expr.directCode('cdkHandler.runtime.name')], ]); CdkHandlerFrameworkConstructor.forClass(_class, superProps); @@ -59,9 +59,6 @@ export class CdkHandlerFrameworkConstructor { expr.object({ codeDirectory: expr.lit(_class.codeDirectory), compatibleRuntimes: expr.directCode(`[${ _class.compatibleRuntimes }]`), - entrypoint: _class.entrypoint - ? expr.lit(`${_class.entrypoint}`) - : expr.lit('index.handler'), }), ), ); From 9d950d90355266973f3e68c64c36b6cf375c9df7 Mon Sep 17 00:00:00 2001 From: Francis Date: Thu, 7 Dec 2023 01:10:17 -0800 Subject: [PATCH 25/73] cdk-handler and runtime determiner Signed-off-by: Francis --- .../aws-cdk-lib/aws-lambda/lib/runtime.ts | 81 ++++- .../aws-lambda/test/runtime.test.ts | 23 ++ .../custom-resource-provider-base.ts | 281 +++++++++++++++ .../custom-resource-provider.ts | 339 +----------------- .../lib/custom-resource-provider/index.ts | 4 +- .../lib/custom-resource-provider/shared.ts | 72 ++++ .../aws-cdk-lib/handler-framework/README.md | 29 ++ .../handler-framework/lib/cdk-handler.ts | 45 +++ .../lib/utils/runtime-determiner.ts | 108 ++++++ .../test/cdk-handler.test.ts | 25 ++ .../test/mock-provider/.gitignore | 1 + .../test/mock-provider/index.js | 0 .../test/test-handler/index.ts | 3 + .../test/utils/runtime-determiner.test.ts | 79 ++++ 14 files changed, 742 insertions(+), 348 deletions(-) create mode 100644 packages/aws-cdk-lib/core/lib/custom-resource-provider/custom-resource-provider-base.ts create mode 100644 packages/aws-cdk-lib/core/lib/custom-resource-provider/shared.ts create mode 100644 packages/aws-cdk-lib/handler-framework/README.md create mode 100644 packages/aws-cdk-lib/handler-framework/lib/cdk-handler.ts create mode 100644 packages/aws-cdk-lib/handler-framework/lib/utils/runtime-determiner.ts create mode 100644 packages/aws-cdk-lib/handler-framework/test/cdk-handler.test.ts create mode 100644 packages/aws-cdk-lib/handler-framework/test/mock-provider/.gitignore create mode 100644 packages/aws-cdk-lib/handler-framework/test/mock-provider/index.js create mode 100644 packages/aws-cdk-lib/handler-framework/test/test-handler/index.ts create mode 100644 packages/aws-cdk-lib/handler-framework/test/utils/runtime-determiner.test.ts diff --git a/packages/aws-cdk-lib/aws-lambda/lib/runtime.ts b/packages/aws-cdk-lib/aws-lambda/lib/runtime.ts index 0013d67ad2079..07d5e00c8e0d0 100644 --- a/packages/aws-cdk-lib/aws-lambda/lib/runtime.ts +++ b/packages/aws-cdk-lib/aws-lambda/lib/runtime.ts @@ -30,6 +30,12 @@ export interface LambdaRuntimeProps { * @default false */ readonly supportsSnapStart?: boolean; + + /** + * Whether this runtime is deprecated. + * @default false + */ + readonly isDeprecated?: boolean; } export enum RuntimeFamily { @@ -56,43 +62,64 @@ export class Runtime { * The NodeJS runtime (nodejs) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS runtime. */ - public static readonly NODEJS = new Runtime('nodejs', RuntimeFamily.NODEJS, { supportsInlineCode: true }); + public static readonly NODEJS = new Runtime('nodejs', RuntimeFamily.NODEJS, { + supportsInlineCode: true, + isDeprecated: true, + }); /** * The NodeJS 4.3 runtime (nodejs4.3) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS runtime. */ - public static readonly NODEJS_4_3 = new Runtime('nodejs4.3', RuntimeFamily.NODEJS, { supportsInlineCode: true }); + public static readonly NODEJS_4_3 = new Runtime('nodejs4.3', RuntimeFamily.NODEJS, { + supportsInlineCode: true, + isDeprecated: true, + }); /** * The NodeJS 6.10 runtime (nodejs6.10) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS runtime. */ - public static readonly NODEJS_6_10 = new Runtime('nodejs6.10', RuntimeFamily.NODEJS, { supportsInlineCode: true }); + public static readonly NODEJS_6_10 = new Runtime('nodejs6.10', RuntimeFamily.NODEJS, { + supportsInlineCode: true, + isDeprecated: true, + }); /** * The NodeJS 8.10 runtime (nodejs8.10) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS runtime. */ - public static readonly NODEJS_8_10 = new Runtime('nodejs8.10', RuntimeFamily.NODEJS, { supportsInlineCode: true }); + public static readonly NODEJS_8_10 = new Runtime('nodejs8.10', RuntimeFamily.NODEJS, { + supportsInlineCode: true, + isDeprecated: true, + }); /** * The NodeJS 10.x runtime (nodejs10.x) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS runtime. */ - public static readonly NODEJS_10_X = new Runtime('nodejs10.x', RuntimeFamily.NODEJS, { supportsInlineCode: true }); + public static readonly NODEJS_10_X = new Runtime('nodejs10.x', RuntimeFamily.NODEJS, { + supportsInlineCode: true, + isDeprecated: true, + }); /** * The NodeJS 12.x runtime (nodejs12.x) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS runtime. */ - public static readonly NODEJS_12_X = new Runtime('nodejs12.x', RuntimeFamily.NODEJS, { supportsInlineCode: true }); + public static readonly NODEJS_12_X = new Runtime('nodejs12.x', RuntimeFamily.NODEJS, { + supportsInlineCode: true, + isDeprecated: true, + }); /** * The NodeJS 14.x runtime (nodejs14.x) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS runtime. */ - public static readonly NODEJS_14_X = new Runtime('nodejs14.x', RuntimeFamily.NODEJS, { supportsInlineCode: true }); + public static readonly NODEJS_14_X = new Runtime('nodejs14.x', RuntimeFamily.NODEJS, { + supportsInlineCode: true, + isDeprecated: true, + }); /** * The NodeJS 16.x runtime (nodejs16.x) @@ -119,7 +146,10 @@ export class Runtime { * The Python 2.7 runtime (python2.7) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest Python runtime. */ - public static readonly PYTHON_2_7 = new Runtime('python2.7', RuntimeFamily.PYTHON, { supportsInlineCode: true }); + public static readonly PYTHON_2_7 = new Runtime('python2.7', RuntimeFamily.PYTHON, { + supportsInlineCode: true, + isDeprecated: true, + }); /** * The Python 3.6 runtime (python3.6) (not recommended) @@ -131,6 +161,7 @@ export class Runtime { public static readonly PYTHON_3_6 = new Runtime('python3.6', RuntimeFamily.PYTHON, { supportsInlineCode: true, supportsCodeGuruProfiling: true, + isDeprecated: true, }); /** @@ -228,37 +259,49 @@ export class Runtime { * The .NET Core 1.0 runtime (dotnetcore1.0) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest .NET Core runtime. */ - public static readonly DOTNET_CORE_1 = new Runtime('dotnetcore1.0', RuntimeFamily.DOTNET_CORE); + public static readonly DOTNET_CORE_1 = new Runtime('dotnetcore1.0', RuntimeFamily.DOTNET_CORE, { + isDeprecated: true, + }); /** * The .NET Core 2.0 runtime (dotnetcore2.0) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest .NET Core runtime. */ - public static readonly DOTNET_CORE_2 = new Runtime('dotnetcore2.0', RuntimeFamily.DOTNET_CORE); + public static readonly DOTNET_CORE_2 = new Runtime('dotnetcore2.0', RuntimeFamily.DOTNET_CORE, { + isDeprecated: true, + }); /** * The .NET Core 2.1 runtime (dotnetcore2.1) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest .NET Core runtime. */ - public static readonly DOTNET_CORE_2_1 = new Runtime('dotnetcore2.1', RuntimeFamily.DOTNET_CORE); + public static readonly DOTNET_CORE_2_1 = new Runtime('dotnetcore2.1', RuntimeFamily.DOTNET_CORE, { + isDeprecated: true, + }); /** * The .NET Core 3.1 runtime (dotnetcore3.1) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest .NET Core runtime. */ - public static readonly DOTNET_CORE_3_1 = new Runtime('dotnetcore3.1', RuntimeFamily.DOTNET_CORE); + public static readonly DOTNET_CORE_3_1 = new Runtime('dotnetcore3.1', RuntimeFamily.DOTNET_CORE, { + isDeprecated: true, + }); /** * The Go 1.x runtime (go1.x) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the PROVIDED_AL2023 runtime. */ - public static readonly GO_1_X = new Runtime('go1.x', RuntimeFamily.GO); + public static readonly GO_1_X = new Runtime('go1.x', RuntimeFamily.GO, { + isDeprecated: true, + }); /** * The Ruby 2.5 runtime (ruby2.5) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest Ruby runtime. */ - public static readonly RUBY_2_5 = new Runtime('ruby2.5', RuntimeFamily.RUBY); + public static readonly RUBY_2_5 = new Runtime('ruby2.5', RuntimeFamily.RUBY, { + isDeprecated: true, + }); /** * The Ruby 2.7 runtime (ruby2.7) @@ -274,7 +317,9 @@ export class Runtime { * The custom provided runtime (provided) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest provided.al2023 runtime. */ - public static readonly PROVIDED = new Runtime('provided', RuntimeFamily.OTHER); + public static readonly PROVIDED = new Runtime('provided', RuntimeFamily.OTHER, { + isDeprecated: true, + }); /** * The custom provided runtime with Amazon Linux 2 (provided.al2) @@ -333,11 +378,17 @@ export class Runtime { */ public readonly isVariable: boolean; + /** + * Whether the runtime is deprecated. + */ + public readonly isDeprecated: boolean; + constructor(name: string, family?: RuntimeFamily, props: LambdaRuntimeProps = {}) { this.name = name; this.supportsInlineCode = !!props.supportsInlineCode; this.family = family; this.isVariable = !!props.isVariable; + this.isDeprecated = props.isDeprecated ?? false; const imageName = props.bundlingDockerImage ?? `public.ecr.aws/sam/build-${name}`; this.bundlingDockerImage = DockerImage.fromRegistry(imageName); diff --git a/packages/aws-cdk-lib/aws-lambda/test/runtime.test.ts b/packages/aws-cdk-lib/aws-lambda/test/runtime.test.ts index f3976e70c4327..5a6a421a9cb80 100644 --- a/packages/aws-cdk-lib/aws-lambda/test/runtime.test.ts +++ b/packages/aws-cdk-lib/aws-lambda/test/runtime.test.ts @@ -55,3 +55,26 @@ describe('runtime', () => { expect(runtime.bundlingDockerImage.image).toEqual('my-docker-image'); }); }); + +describe('deprecated runtimes', () => { + test.each([ + [lambda.Runtime.PYTHON_2_7], + [lambda.Runtime.PYTHON_3_6], + [lambda.Runtime.NODEJS], + [lambda.Runtime.NODEJS_4_3], + [lambda.Runtime.NODEJS_6_10], + [lambda.Runtime.NODEJS_8_10], + [lambda.Runtime.NODEJS_10_X], + [lambda.Runtime.NODEJS_12_X], + [lambda.Runtime.NODEJS_14_X], + [lambda.Runtime.DOTNET_CORE_1], + [lambda.Runtime.DOTNET_CORE_2], + [lambda.Runtime.DOTNET_CORE_2_1], + [lambda.Runtime.DOTNET_CORE_3_1], + [lambda.Runtime.GO_1_X], + [lambda.Runtime.RUBY_2_5], + [lambda.Runtime.PROVIDED], + ])('%s is deprecated', (runtime) => { + expect(runtime.isDeprecated).toEqual(true); + }); +}); diff --git a/packages/aws-cdk-lib/core/lib/custom-resource-provider/custom-resource-provider-base.ts b/packages/aws-cdk-lib/core/lib/custom-resource-provider/custom-resource-provider-base.ts new file mode 100644 index 0000000000000..ecd8f72f42d41 --- /dev/null +++ b/packages/aws-cdk-lib/core/lib/custom-resource-provider/custom-resource-provider-base.ts @@ -0,0 +1,281 @@ +import * as path from 'path'; +import { Construct } from 'constructs'; +import * as fs from 'fs-extra'; +import { CustomResourceProviderOptions, INLINE_CUSTOM_RESOURCE_CONTEXT } from './shared'; +import * as cxapi from '../../../cx-api'; +import { AssetStaging } from '../asset-staging'; +import { FileAssetPackaging } from '../assets'; +import { CfnResource } from '../cfn-resource'; +import { Duration } from '../duration'; +import { FileSystem } from '../fs'; +import { PolicySynthesizer, getPrecreatedRoleConfig } from '../helpers-internal'; +import { Lazy } from '../lazy'; +import { Size } from '../size'; +import { Stack } from '../stack'; +import { Token } from '../token'; + +const ENTRYPOINT_FILENAME = '__entrypoint__'; +const ENTRYPOINT_NODEJS_SOURCE = path.join(__dirname, '..', '..', '..', 'custom-resource-handlers', 'dist', 'core', 'nodejs-entrypoint-handler', 'index.js'); + +/** + * Initialization properties for `CustomResourceProviderBase` + */ +export interface CustomResourceProviderBaseProps extends CustomResourceProviderOptions { + /** + * A local file system directory with the provider's code. The code will be + * bundled into a zip asset and wired to the provider's AWS Lambda function. + */ + readonly codeDirectory: string; + + /** + * The AWS Lambda runtime and version name to use for the provider. + */ + readonly runtimeName: string; +} + +/** + * Base class for creating a custom resource provider + */ +export abstract class CustomResourceProviderBase extends Construct { + /** + * The hash of the lambda code backing this provider. Can be used to trigger updates + * on code changes, even when the properties of a custom resource remain unchanged. + */ + public get codeHash(): string { + if (!this._codeHash) { + throw new Error('This custom resource uses inlineCode: true and does not have a codeHash'); + } + return this._codeHash; + } + + private _codeHash?: string; + private policyStatements?: any[]; + private role?: CfnResource; + + /** + * The ARN of the provider's AWS Lambda function which should be used as the `serviceToken` when defining a custom + * resource. + */ + public readonly serviceToken: string; + + /** + * The ARN of the provider's AWS Lambda function role. + */ + public readonly roleArn: string; + + protected constructor(scope: Construct, id: string, props: CustomResourceProviderBaseProps) { + super(scope, id); + + const stack = Stack.of(scope); + + // verify we have an index file there + if (!fs.existsSync(path.join(props.codeDirectory, 'index.js'))) { + throw new Error(`cannot find ${props.codeDirectory}/index.js`); + } + + if (props.policyStatements) { + for (const statement of props.policyStatements) { + this.addToRolePolicy(statement); + } + } + + const { code, codeHandler, metadata } = this.createCodePropAndMetadata(props, stack); + + const config = getPrecreatedRoleConfig(this, `${this.node.path}/Role`); + const assumeRolePolicyDoc = [{ Action: 'sts:AssumeRole', Effect: 'Allow', Principal: { Service: 'lambda.amazonaws.com' } }]; + const managedPolicyArn = 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'; + + // need to initialize this attribute, but there should never be an instance + // where config.enabled=true && config.preventSynthesis=true + this.roleArn = ''; + if (config.enabled) { + // gives policyStatements a chance to resolve + this.node.addValidation({ + validate: () => { + PolicySynthesizer.getOrCreate(this).addRole(`${this.node.path}/Role`, { + missing: !config.precreatedRoleName, + roleName: config.precreatedRoleName ?? id+'Role', + managedPolicies: [{ managedPolicyArn: managedPolicyArn }], + policyStatements: this.policyStatements ?? [], + assumeRolePolicy: assumeRolePolicyDoc as any, + }); + return []; + }, + }); + this.roleArn = Stack.of(this).formatArn({ + region: '', + service: 'iam', + resource: 'role', + resourceName: config.precreatedRoleName, + }); + } + if (!config.preventSynthesis) { + this.role = new CfnResource(this, 'Role', { + type: 'AWS::IAM::Role', + properties: { + AssumeRolePolicyDocument: { + Version: '2012-10-17', + Statement: assumeRolePolicyDoc, + }, + ManagedPolicyArns: [ + { 'Fn::Sub': managedPolicyArn }, + ], + Policies: Lazy.any({ produce: () => this.renderPolicies() }), + }, + }); + this.roleArn = Token.asString(this.role.getAtt('Arn')); + } + + const timeout = props.timeout ?? Duration.minutes(15); + const memory = props.memorySize ?? Size.mebibytes(128); + + const handler = new CfnResource(this, 'Handler', { + type: 'AWS::Lambda::Function', + properties: { + Code: code, + Timeout: timeout.toSeconds(), + MemorySize: memory.toMebibytes(), + Handler: codeHandler, + Role: this.roleArn, + Runtime: props.runtimeName, + Environment: this.renderEnvironmentVariables(props.environment), + Description: props.description ?? undefined, + }, + }); + + if (this.role) { + handler.addDependency(this.role); + } + + if (metadata) { + Object.entries(metadata).forEach(([k, v]) => handler.addMetadata(k, v)); + } + + this.serviceToken = Token.asString(handler.getAtt('Arn')); + } + + /** + * Add an IAM policy statement to the inline policy of the + * provider's lambda function's role. + * + * **Please note**: this is a direct IAM JSON policy blob, *not* a `iam.PolicyStatement` + * object like you will see in the rest of the CDK. + * + * + * @example + * declare const myProvider: CustomResourceProvider; + * + * myProvider.addToRolePolicy({ + * Effect: 'Allow', + * Action: 's3:GetObject', + * Resource: '*', + * }); + */ + public addToRolePolicy(statement: any): void { + if (!this.policyStatements) { + this.policyStatements = []; + } + this.policyStatements.push(statement); + } + + private renderPolicies() { + if (!this.policyStatements) { + return undefined; + } + + const policies = [{ + PolicyName: 'Inline', + PolicyDocument: { + Version: '2012-10-17', + Statement: this.policyStatements, + }, + }]; + + return policies; + } + + private renderEnvironmentVariables(env?: { [key: string]: string }) { + if (!env || Object.keys(env).length === 0) { + return undefined; + } + + env = { ...env }; // Copy + + // Always use regional endpoints + env.AWS_STS_REGIONAL_ENDPOINTS = 'regional'; + + // Sort environment so the hash of the function used to create + // `currentVersion` is not affected by key order (this is how lambda does + // it) + const variables: { [key: string]: string } = {}; + const keys = Object.keys(env).sort(); + + for (const key of keys) { + variables[key] = env[key]; + } + + return { Variables: variables }; + } + + /** + * Returns the code property for the custom resource as well as any metadata. + * If the code is to be uploaded as an asset, the asset gets created in this function. + */ + private createCodePropAndMetadata(props: CustomResourceProviderBaseProps, stack: Stack): { + code: Code, + codeHandler: string, + metadata?: {[key: string]: string}, + } { + let codeHandler = 'index.handler'; + const inlineCode = this.node.tryGetContext(INLINE_CUSTOM_RESOURCE_CONTEXT); + if (!inlineCode) { + const stagingDirectory = FileSystem.mkdtemp('cdk-custom-resource'); + fs.copySync(props.codeDirectory, stagingDirectory, { filter: (src, _dest) => !src.endsWith('.ts') }); + + if (props.useCfnResponseWrapper ?? true) { + fs.copyFileSync(ENTRYPOINT_NODEJS_SOURCE, path.join(stagingDirectory, `${ENTRYPOINT_FILENAME}.js`)); + codeHandler = `${ENTRYPOINT_FILENAME}.handler`; + } + + const staging = new AssetStaging(this, 'Staging', { + sourcePath: stagingDirectory, + }); + + const assetFileName = staging.relativeStagedPath(stack); + + const asset = stack.synthesizer.addFileAsset({ + fileName: assetFileName, + sourceHash: staging.assetHash, + packaging: FileAssetPackaging.ZIP_DIRECTORY, + }); + + this._codeHash = staging.assetHash; + + return { + code: { + S3Bucket: asset.bucketName, + S3Key: asset.objectKey, + }, + codeHandler, + metadata: this.node.tryGetContext(cxapi.ASSET_RESOURCE_METADATA_ENABLED_CONTEXT) ? { + [cxapi.ASSET_RESOURCE_METADATA_PATH_KEY]: assetFileName, + [cxapi.ASSET_RESOURCE_METADATA_PROPERTY_KEY]: 'Code', + } : undefined, + }; + } + + return { + code: { + ZipFile: fs.readFileSync(path.join(props.codeDirectory, 'index.js'), 'utf-8'), + }, + codeHandler, + }; + } +} + +export type Code = { + ZipFile: string, +} | { + S3Bucket: string, + S3Key: string, +}; diff --git a/packages/aws-cdk-lib/core/lib/custom-resource-provider/custom-resource-provider.ts b/packages/aws-cdk-lib/core/lib/custom-resource-provider/custom-resource-provider.ts index e2a15e3211263..1b0dd0e81a984 100644 --- a/packages/aws-cdk-lib/core/lib/custom-resource-provider/custom-resource-provider.ts +++ b/packages/aws-cdk-lib/core/lib/custom-resource-provider/custom-resource-provider.ts @@ -1,37 +1,13 @@ -import * as path from 'path'; import { Construct } from 'constructs'; -import * as fs from 'fs-extra'; -import * as cxapi from '../../../cx-api'; -import { AssetStaging } from '../asset-staging'; -import { FileAssetPackaging } from '../assets'; -import { CfnResource } from '../cfn-resource'; -import { Duration } from '../duration'; -import { FileSystem } from '../fs'; -import { PolicySynthesizer, getPrecreatedRoleConfig } from '../helpers-internal'; -import { Lazy } from '../lazy'; -import { Size } from '../size'; +import { CustomResourceProviderBase } from './custom-resource-provider-base'; +import { CustomResourceProviderOptions } from './shared'; import { Stack } from '../stack'; -import { Token } from '../token'; - -const ENTRYPOINT_FILENAME = '__entrypoint__'; -const ENTRYPOINT_NODEJS_SOURCE = path.join(__dirname, '..', '..', '..', 'custom-resource-handlers', 'dist', 'core', 'nodejs-entrypoint-handler', 'index.js'); -export const INLINE_CUSTOM_RESOURCE_CONTEXT = '@aws-cdk/core:inlineCustomResourceIfPossible'; /** * Initialization properties for `CustomResourceProvider`. * */ -export interface CustomResourceProviderProps { - /** - * Whether or not the cloudformation response wrapper (`nodejs-entrypoint.ts`) is used. - * If set to `true`, `nodejs-entrypoint.js` is bundled in the same asset as the custom resource - * and set as the entrypoint. If set to `false`, the custom resource provided is the - * entrypoint. - * - * @default - `true` if `inlineCode: false` and `false` otherwise. - */ - readonly useCfnResponseWrapper?: boolean; - +export interface CustomResourceProviderProps extends CustomResourceProviderOptions { /** * A local file system directory with the provider's code. The code will be * bundled into a zip asset and wired to the provider's AWS Lambda function. @@ -42,59 +18,6 @@ export interface CustomResourceProviderProps { * The AWS Lambda runtime and version to use for the provider. */ readonly runtime: CustomResourceProviderRuntime; - - /** - * A set of IAM policy statements to include in the inline policy of the - * provider's lambda function. - * - * **Please note**: these are direct IAM JSON policy blobs, *not* `iam.PolicyStatement` - * objects like you will see in the rest of the CDK. - * - * @default - no additional inline policy - * - * @example - * const provider = CustomResourceProvider.getOrCreateProvider(this, 'Custom::MyCustomResourceType', { - * codeDirectory: `${__dirname}/my-handler`, - * runtime: CustomResourceProviderRuntime.NODEJS_18_X, - * policyStatements: [ - * { - * Effect: 'Allow', - * Action: 's3:PutObject*', - * Resource: '*', - * } - * ], - * }); - */ - readonly policyStatements?: any[]; - - /** - * AWS Lambda timeout for the provider. - * - * @default Duration.minutes(15) - */ - readonly timeout?: Duration; - - /** - * The amount of memory that your function has access to. Increasing the - * function's memory also increases its CPU allocation. - * - * @default Size.mebibytes(128) - */ - readonly memorySize?: Size; - - /** - * Key-value pairs that are passed to Lambda as Environment - * - * @default - No environment variables. - */ - readonly environment?: { [key: string]: string }; - - /** - * A description of the function. - * - * @default - No description. - */ - readonly description?: string; } /** @@ -154,7 +77,7 @@ export enum CustomResourceProviderRuntime { * in that module a read, regardless of whether you end up using the Provider * class in there or this one. */ -export class CustomResourceProvider extends Construct { +export class CustomResourceProvider extends CustomResourceProviderBase { /** * Returns a stack-level singleton ARN (service token) for the custom resource * provider. @@ -187,255 +110,14 @@ export class CustomResourceProvider extends Construct { const stack = Stack.of(scope); const provider = stack.node.tryFindChild(id) as CustomResourceProvider ?? new CustomResourceProvider(stack, id, props); - return provider; } - /** - * The ARN of the provider's AWS Lambda function which should be used as the - * `serviceToken` when defining a custom resource. - * - * @example - * declare const myProvider: CustomResourceProvider; - * - * new CustomResource(this, 'MyCustomResource', { - * serviceToken: myProvider.serviceToken, - * properties: { - * myPropertyOne: 'one', - * myPropertyTwo: 'two', - * }, - * }); - */ - public readonly serviceToken: string; - - /** - * The ARN of the provider's AWS Lambda function role. - */ - public readonly roleArn: string; - - /** - * The hash of the lambda code backing this provider. Can be used to trigger updates - * on code changes, even when the properties of a custom resource remain unchanged. - */ - public get codeHash(): string { - if (!this._codeHash) { - throw new Error('This custom resource uses inlineCode: true and does not have a codeHash'); - } - return this._codeHash; - } - - private _codeHash?: string; - - private policyStatements?: any[]; - private _role?: CfnResource; - protected constructor(scope: Construct, id: string, props: CustomResourceProviderProps) { - super(scope, id); - - const stack = Stack.of(scope); - - // verify we have an index file there - if (!fs.existsSync(path.join(props.codeDirectory, 'index.js'))) { - throw new Error(`cannot find ${props.codeDirectory}/index.js`); - } - - const { code, codeHandler, metadata } = this.createCodePropAndMetadata(props, stack); - - if (props.policyStatements) { - for (const statement of props.policyStatements) { - this.addToRolePolicy(statement); - } - } - - const config = getPrecreatedRoleConfig(this, `${this.node.path}/Role`); - const assumeRolePolicyDoc = [{ Action: 'sts:AssumeRole', Effect: 'Allow', Principal: { Service: 'lambda.amazonaws.com' } }]; - const managedPolicyArn = 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'; - - // need to initialize this attribute, but there should never be an instance - // where config.enabled=true && config.preventSynthesis=true - this.roleArn = ''; - if (config.enabled) { - // gives policyStatements a chance to resolve - this.node.addValidation({ - validate: () => { - PolicySynthesizer.getOrCreate(this).addRole(`${this.node.path}/Role`, { - missing: !config.precreatedRoleName, - roleName: config.precreatedRoleName ?? id+'Role', - managedPolicies: [{ managedPolicyArn: managedPolicyArn }], - policyStatements: this.policyStatements ?? [], - assumeRolePolicy: assumeRolePolicyDoc as any, - }); - return []; - }, - }); - this.roleArn = Stack.of(this).formatArn({ - region: '', - service: 'iam', - resource: 'role', - resourceName: config.precreatedRoleName, - }); - } - if (!config.preventSynthesis) { - this._role = new CfnResource(this, 'Role', { - type: 'AWS::IAM::Role', - properties: { - AssumeRolePolicyDocument: { - Version: '2012-10-17', - Statement: assumeRolePolicyDoc, - }, - ManagedPolicyArns: [ - { 'Fn::Sub': managedPolicyArn }, - ], - Policies: Lazy.any({ produce: () => this.renderPolicies() }), - }, - }); - this.roleArn = Token.asString(this._role.getAtt('Arn')); - } - - const timeout = props.timeout ?? Duration.minutes(15); - const memory = props.memorySize ?? Size.mebibytes(128); - - const handler = new CfnResource(this, 'Handler', { - type: 'AWS::Lambda::Function', - properties: { - Code: code, - Timeout: timeout.toSeconds(), - MemorySize: memory.toMebibytes(), - Handler: codeHandler, - Role: this.roleArn, - Runtime: customResourceProviderRuntimeToString(props.runtime), - Environment: this.renderEnvironmentVariables(props.environment), - Description: props.description ?? undefined, - }, + super(scope, id, { + ...props, + runtimeName: customResourceProviderRuntimeToString(props.runtime), }); - - if (this._role) { - handler.addDependency(this._role); - } - - if (metadata) { - Object.entries(metadata).forEach(([k, v]) => handler.addMetadata(k, v)); - } - - this.serviceToken = Token.asString(handler.getAtt('Arn')); - } - - /** - * Returns the code property for the custom resource as well as any metadata. - * If the code is to be uploaded as an asset, the asset gets created in this function. - */ - private createCodePropAndMetadata(props: CustomResourceProviderProps, stack: Stack): { - code: Code, - codeHandler: string, - metadata?: {[key: string]: string}, - } { - let codeHandler = 'index.handler'; - const inlineCode = this.node.tryGetContext(INLINE_CUSTOM_RESOURCE_CONTEXT); - if (!inlineCode) { - const stagingDirectory = FileSystem.mkdtemp('cdk-custom-resource'); - fs.copySync(props.codeDirectory, stagingDirectory, { filter: (src, _dest) => !src.endsWith('.ts') }); - - if (props.useCfnResponseWrapper ?? true) { - fs.copyFileSync(ENTRYPOINT_NODEJS_SOURCE, path.join(stagingDirectory, `${ENTRYPOINT_FILENAME}.js`)); - codeHandler = `${ENTRYPOINT_FILENAME}.handler`; - } - - const staging = new AssetStaging(this, 'Staging', { - sourcePath: stagingDirectory, - }); - - const assetFileName = staging.relativeStagedPath(stack); - - const asset = stack.synthesizer.addFileAsset({ - fileName: assetFileName, - sourceHash: staging.assetHash, - packaging: FileAssetPackaging.ZIP_DIRECTORY, - }); - - this._codeHash = staging.assetHash; - - return { - code: { - S3Bucket: asset.bucketName, - S3Key: asset.objectKey, - }, - codeHandler, - metadata: this.node.tryGetContext(cxapi.ASSET_RESOURCE_METADATA_ENABLED_CONTEXT) ? { - [cxapi.ASSET_RESOURCE_METADATA_PATH_KEY]: assetFileName, - [cxapi.ASSET_RESOURCE_METADATA_PROPERTY_KEY]: 'Code', - } : undefined, - }; - } - - return { - code: { - ZipFile: fs.readFileSync(path.join(props.codeDirectory, 'index.js'), 'utf-8'), - }, - codeHandler, - }; - } - - /** - * Add an IAM policy statement to the inline policy of the - * provider's lambda function's role. - * - * **Please note**: this is a direct IAM JSON policy blob, *not* a `iam.PolicyStatement` - * object like you will see in the rest of the CDK. - * - * - * @example - * declare const myProvider: CustomResourceProvider; - * - * myProvider.addToRolePolicy({ - * Effect: 'Allow', - * Action: 's3:GetObject', - * Resource: '*', - * }); - */ - public addToRolePolicy(statement: any): void { - if (!this.policyStatements) { - this.policyStatements = []; - } - this.policyStatements.push(statement); - } - - private renderPolicies() { - if (!this.policyStatements) { - return undefined; - } - - const policies = [{ - PolicyName: 'Inline', - PolicyDocument: { - Version: '2012-10-17', - Statement: this.policyStatements, - }, - }]; - - return policies; - } - - private renderEnvironmentVariables(env?: { [key: string]: string }) { - if (!env || Object.keys(env).length === 0) { - return undefined; - } - - env = { ...env }; // Copy - - // Always use regional endpoints - env.AWS_STS_REGIONAL_ENDPOINTS = 'regional'; - - // Sort environment so the hash of the function used to create - // `currentVersion` is not affected by key order (this is how lambda does - // it) - const variables: { [key: string]: string } = {}; - const keys = Object.keys(env).sort(); - - for (const key of keys) { - variables[key] = env[key]; - } - - return { Variables: variables }; } } @@ -452,10 +134,3 @@ function customResourceProviderRuntimeToString(x: CustomResourceProviderRuntime) return 'nodejs18.x'; } } - -type Code = { - ZipFile: string, -} | { - S3Bucket: string, - S3Key: string, -}; diff --git a/packages/aws-cdk-lib/core/lib/custom-resource-provider/index.ts b/packages/aws-cdk-lib/core/lib/custom-resource-provider/index.ts index 9ff36ec201b71..95841d8fa7525 100644 --- a/packages/aws-cdk-lib/core/lib/custom-resource-provider/index.ts +++ b/packages/aws-cdk-lib/core/lib/custom-resource-provider/index.ts @@ -1 +1,3 @@ -export * from './custom-resource-provider'; \ No newline at end of file +export * from './custom-resource-provider-base'; +export * from './custom-resource-provider'; +export * from './shared'; \ No newline at end of file diff --git a/packages/aws-cdk-lib/core/lib/custom-resource-provider/shared.ts b/packages/aws-cdk-lib/core/lib/custom-resource-provider/shared.ts new file mode 100644 index 0000000000000..0f5f13e250afc --- /dev/null +++ b/packages/aws-cdk-lib/core/lib/custom-resource-provider/shared.ts @@ -0,0 +1,72 @@ +import { Duration } from '../duration'; +import { Size } from '../size'; + +export const INLINE_CUSTOM_RESOURCE_CONTEXT = '@aws-cdk/core:inlineCustomResourceIfPossible'; + +/** + * Initialization options for custom resource providers + */ +export interface CustomResourceProviderOptions { + /** + * Whether or not the cloudformation response wrapper (`nodejs-entrypoint.ts`) is used. + * If set to `true`, `nodejs-entrypoint.js` is bundled in the same asset as the custom resource + * and set as the entrypoint. If set to `false`, the custom resource provided is the + * entrypoint. + * + * @default - `true` if `inlineCode: false` and `false` otherwise. + */ + readonly useCfnResponseWrapper?: boolean; + + /** + * A set of IAM policy statements to include in the inline policy of the + * provider's lambda function. + * + * **Please note**: these are direct IAM JSON policy blobs, *not* `iam.PolicyStatement` + * objects like you will see in the rest of the CDK. + * + * @default - no additional inline policy + * + * @example + * const provider = CustomResourceProvider.getOrCreateProvider(this, 'Custom::MyCustomResourceType', { + * codeDirectory: `${__dirname}/my-handler`, + * runtime: CustomResourceProviderRuntime.NODEJS_18_X, + * policyStatements: [ + * { + * Effect: 'Allow', + * Action: 's3:PutObject*', + * Resource: '*', + * } + * ], + * }); + */ + readonly policyStatements?: any[]; + + /** + * AWS Lambda timeout for the provider. + * + * @default Duration.minutes(15) + */ + readonly timeout?: Duration; + + /** + * The amount of memory that your function has access to. Increasing the + * function's memory also increases its CPU allocation. + * + * @default Size.mebibytes(128) + */ + readonly memorySize?: Size; + + /** + * Key-value pairs that are passed to Lambda as Environment + * + * @default - No environment variables. + */ + readonly environment?: { [key: string]: string }; + + /** + * A description of the function. + * + * @default - No description. + */ + readonly description?: string; +} diff --git a/packages/aws-cdk-lib/handler-framework/README.md b/packages/aws-cdk-lib/handler-framework/README.md new file mode 100644 index 0000000000000..6bc0a9d9e83c1 --- /dev/null +++ b/packages/aws-cdk-lib/handler-framework/README.md @@ -0,0 +1,29 @@ +# AWS CDK Vended Handler Framework + +Note: This is framework intended for internal use only. + +The handler framework module is an internal framework used to establish best practices for vending Lambda handlers that are deployed to user accounts. Primarily, this framework includes a centralized definition of the default runtime version which is the latest version of NodeJs available across all AWS Regions. + +In addition to including a default runtime version, this framework forces the user to specify `compatibleRuntimes` for each Lambda handler being used. The framework first checks for the default runtime in the list of `compatibleRuntimes`. If found, the default runtime is used. If not found, the framework will look for the latest defined runtime in the list of `compatibleRuntimes`. If the latest runtime found is marked as deprecated, then the framework will force the build to fail. To continue, the user must specify a non-deprecated runtime version that the handler code is compatible with. + +## CDK Handler + +`CdkHandler` is a class that represents the source code that will be executed within a Lambda `Function` acting as a custom resource provider. Once constructed, this class contains four attributes: +1. `codeDirectory` - the local file system directory with the provider's code. This the code that will be bundled into a zip asset and wired to the provider's AWS Lambda function. +2. `code` - the source code that is loaded from a local disk path +3. `entrypoint` - the name of the method within your `code` that Lambda calls to execute your `Function`. Note that the default entrypoint is 'index.handler' +4. `compatibleRuntimes` - the runtimes that your `code` is compatible with + +Note that `compatibleRuntimes` can be any python or nodejs runtimes, but the nodejs runtime family is preferred. Python runtimes are supported to provide support for legacy handler code that was written using python. + +The following is an example of how to use `CdkHandler`: + +```ts +const stack = new Stack(); + +const handler = new CdkHandler(stack, 'Handler', { + codeDirectory: path.join(__dirname, 'my-handler'), + entrypoint: 'index.onEventHandler', + compatibleRuntimes: [Runtime.NODEJS_16_X, Runtime.NODEJS_18_X], +}); +``` diff --git a/packages/aws-cdk-lib/handler-framework/lib/cdk-handler.ts b/packages/aws-cdk-lib/handler-framework/lib/cdk-handler.ts new file mode 100644 index 0000000000000..0af8ab1df99b2 --- /dev/null +++ b/packages/aws-cdk-lib/handler-framework/lib/cdk-handler.ts @@ -0,0 +1,45 @@ +import { Construct } from 'constructs'; +import { RuntimeDeterminer } from './utils/runtime-determiner'; +import { Code, Runtime } from '../../aws-lambda'; + +/** + * Properties used to initialize `CdkHandler`. + */ +export interface CdkHandlerProps { + /** + * A local file system directory with the provider's code. The code will be + * bundled into a zip asset and wired to the provider's AWS Lambda function. + */ + readonly codeDirectory: string; + + /** + * Runtimes that are compatible with the source code. + */ + readonly compatibleRuntimes: Runtime[]; +} + +/** + * Represents an instance of `CdkHandler`. + */ +export class CdkHandler extends Construct { + /** + * The latest nodejs runtime version available across all AWS regions + */ + private static readonly DEFAULT_RUNTIME = Runtime.NODEJS_LATEST; + + /** + * The source code of your Lambda function. + */ + public readonly code: Code; + + /** + * The latest runtime that is compatible with the source code. + */ + public readonly runtime: Runtime; + + public constructor(scope: Construct, id: string, props: CdkHandlerProps) { + super(scope, id); + this.code = Code.fromAsset(props.codeDirectory); + this.runtime = RuntimeDeterminer.determineLatestRuntime(CdkHandler.DEFAULT_RUNTIME, props.compatibleRuntimes); + } +} diff --git a/packages/aws-cdk-lib/handler-framework/lib/utils/runtime-determiner.ts b/packages/aws-cdk-lib/handler-framework/lib/utils/runtime-determiner.ts new file mode 100644 index 0000000000000..c36d831e37aa9 --- /dev/null +++ b/packages/aws-cdk-lib/handler-framework/lib/utils/runtime-determiner.ts @@ -0,0 +1,108 @@ +import { Runtime, RuntimeFamily } from '../../../aws-lambda'; + +/** + * A utility class used to determine the latest runtime for a specific runtime family + */ +export class RuntimeDeterminer { + /** + * Determines the latest runtime from a list of runtimes. + * + * Note: runtimes must only be nodejs or python. Nodejs runtimes will be given preference over + * python runtimes. + * + * @param runtimes the list of runtimes to search in + * @returns the latest nodejs or python runtime found, otherwise undefined if no nodejs or python + * runtimes are specified + */ + public static determineLatestRuntime(defaultRuntime: Runtime, runtimes: Runtime[]) { + if (runtimes.length === 0) { + throw new Error('You must specify at least one compatible runtime'); + } + + const nodeJsRuntimes = runtimes.filter(runtime => runtime.family === RuntimeFamily.NODEJS); + const latestNodeJsRuntime = RuntimeDeterminer.latestNodeJsRuntime(defaultRuntime, nodeJsRuntimes); + if (latestNodeJsRuntime !== undefined) { + if (latestNodeJsRuntime.isDeprecated) { + throw new Error(`Latest nodejs runtime ${latestNodeJsRuntime} is deprecated. You must upgrade to the latest code compatible nodejs runtime`); + } + return latestNodeJsRuntime; + } + + const pythonRuntimes = runtimes.filter(runtime => runtime.family === RuntimeFamily.PYTHON); + const latestPythonRuntime = RuntimeDeterminer.latestPythonRuntime(pythonRuntimes); + if (latestPythonRuntime !== undefined) { + if (latestPythonRuntime.isDeprecated) { + throw new Error(`Latest python runtime ${latestPythonRuntime} is deprecated. You must upgrade to the latest code compatible python runtime`); + } + return latestPythonRuntime; + } + + throw new Error('Compatible runtimes must contain only nodejs or python runtimes'); + } + + private static latestNodeJsRuntime(defaultRuntime: Runtime, nodeJsRuntimes: Runtime[]) { + if (nodeJsRuntimes.length === 0) { + return undefined; + } + + if (nodeJsRuntimes.some(runtime => runtime.runtimeEquals(defaultRuntime))) { + return defaultRuntime; + } + + let latestRuntime = nodeJsRuntimes[0]; + for (let idx = 1; idx < nodeJsRuntimes.length; idx++) { + latestRuntime = RuntimeDeterminer.latestRuntime(latestRuntime, nodeJsRuntimes[idx], RuntimeFamily.NODEJS); + } + + return latestRuntime; + } + + private static latestPythonRuntime(pythonRuntimes: Runtime[]) { + if (pythonRuntimes.length === 0) { + return undefined; + } + + let latestRuntime = pythonRuntimes[0]; + for (let idx = 1; idx < pythonRuntimes.length; idx++) { + latestRuntime = RuntimeDeterminer.latestRuntime(latestRuntime, pythonRuntimes[idx], RuntimeFamily.PYTHON); + } + + return latestRuntime; + } + + private static latestRuntime(runtime1: Runtime, runtime2: Runtime, family: RuntimeFamily) { + let sliceStart: number; + switch (family) { + case RuntimeFamily.NODEJS: { + sliceStart = 'nodejs'.length; + break; + } + case RuntimeFamily.PYTHON: { + sliceStart = 'python'.length; + break; + } + default: { + sliceStart = 0; + break; + } + } + + const version1 = runtime1.name.slice(sliceStart).split('.'); + const version2 = runtime2.name.slice(sliceStart).split('.'); + + const versionLength = Math.min(version1.length, version2.length); + for (let idx = 0; idx < versionLength; idx++) { + if (parseInt(version1[idx]) > parseInt(version2[idx])) { + return runtime1; + } + + if (parseInt(version1[idx]) < parseInt(version2[idx])) { + return runtime2; + } + } + + return runtime1; + } + + private constructor() {} +} \ No newline at end of file diff --git a/packages/aws-cdk-lib/handler-framework/test/cdk-handler.test.ts b/packages/aws-cdk-lib/handler-framework/test/cdk-handler.test.ts new file mode 100644 index 0000000000000..873df759b58fe --- /dev/null +++ b/packages/aws-cdk-lib/handler-framework/test/cdk-handler.test.ts @@ -0,0 +1,25 @@ +import * as path from 'path'; +import { Runtime } from '../../aws-lambda'; +import { Stack } from '../../core'; +import { CdkHandler } from '../lib/cdk-handler'; + +describe('cdk handler', () => { + let codeDirectory: string; + beforeAll(() => { + codeDirectory = path.join(__dirname, 'test-handler'); + }); + + test('runtime property is correctly set', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + const handler = new CdkHandler(stack, 'CdkHandler', { + codeDirectory, + compatibleRuntimes: [Runtime.NODEJS_16_X, Runtime.NODEJS_LATEST, Runtime.PYTHON_3_12], + }); + + // THEN + expect(handler.runtime.runtimeEquals(Runtime.NODEJS_LATEST)).toBe(true); + }); +}); diff --git a/packages/aws-cdk-lib/handler-framework/test/mock-provider/.gitignore b/packages/aws-cdk-lib/handler-framework/test/mock-provider/.gitignore new file mode 100644 index 0000000000000..033e6722bb6e0 --- /dev/null +++ b/packages/aws-cdk-lib/handler-framework/test/mock-provider/.gitignore @@ -0,0 +1 @@ +!index.js diff --git a/packages/aws-cdk-lib/handler-framework/test/mock-provider/index.js b/packages/aws-cdk-lib/handler-framework/test/mock-provider/index.js new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/packages/aws-cdk-lib/handler-framework/test/test-handler/index.ts b/packages/aws-cdk-lib/handler-framework/test/test-handler/index.ts new file mode 100644 index 0000000000000..4c6a8a3937bf8 --- /dev/null +++ b/packages/aws-cdk-lib/handler-framework/test/test-handler/index.ts @@ -0,0 +1,3 @@ +export async function handler() { + return { message: 'Hello, world!' }; +} diff --git a/packages/aws-cdk-lib/handler-framework/test/utils/runtime-determiner.test.ts b/packages/aws-cdk-lib/handler-framework/test/utils/runtime-determiner.test.ts new file mode 100644 index 0000000000000..ce8fca15ebd6d --- /dev/null +++ b/packages/aws-cdk-lib/handler-framework/test/utils/runtime-determiner.test.ts @@ -0,0 +1,79 @@ +import { Runtime } from '../../../aws-lambda'; +import { RuntimeDeterminer } from '../../lib/utils/runtime-determiner'; + +const DEFAULT_RUNTIME = Runtime.NODEJS_LATEST; + +describe('latest runtime', () => { + test('selects default runtime', () => { + // GIVEN + const runtimes = [Runtime.NODEJS_16_X, Runtime.PYTHON_3_12, Runtime.NODEJS_18_X]; + + // WHEN + const latestRuntime = RuntimeDeterminer.determineLatestRuntime(DEFAULT_RUNTIME, runtimes); + + // THEN + expect(latestRuntime?.runtimeEquals(DEFAULT_RUNTIME)).toEqual(true); + }); + + test('selects latest nodejs runtime', () => { + // GIVEN + const runtimes = [Runtime.NODEJS_16_X, Runtime.PYTHON_3_12, Runtime.NODEJS_14_X, Runtime.PYTHON_3_11, Runtime.NODEJS_20_X]; + + // WHEN + const latestRuntime = RuntimeDeterminer.determineLatestRuntime(DEFAULT_RUNTIME, runtimes); + + // THEN + expect(latestRuntime?.runtimeEquals(Runtime.NODEJS_20_X)).toEqual(true); + }); + + test('selects latest python runtime', () => { + // GIVEN + const runtimes = [Runtime.PYTHON_3_10, Runtime.PYTHON_3_11, Runtime.PYTHON_3_7]; + + // WHEN + const latestRuntime = RuntimeDeterminer.determineLatestRuntime(DEFAULT_RUNTIME, runtimes); + + // THEN + expect(latestRuntime?.runtimeEquals(Runtime.PYTHON_3_11)).toEqual(true); + }); + + test('throws if no runtimes are specified', () => { + // GIVEN + const runtimes = []; + + // WHEN / THEN + expect(() => { + RuntimeDeterminer.determineLatestRuntime(DEFAULT_RUNTIME, runtimes); + }).toThrow('You must specify at least one compatible runtime'); + }); + + test('throws if latest nodejs runtime is deprecated', () => { + // GIVEN + const runtimes = [Runtime.NODEJS_12_X, Runtime.NODEJS_14_X]; + + // WHEN / THEN + expect(() => { + RuntimeDeterminer.determineLatestRuntime(DEFAULT_RUNTIME, runtimes); + }).toThrow(`Latest nodejs runtime ${Runtime.NODEJS_14_X} is deprecated. You must upgrade to the latest code compatible nodejs runtime`); + }); + + test('throws if latest python runtime is deprecated', () => { + // GIVEN + const runtimes = [Runtime.PYTHON_2_7, Runtime.PYTHON_3_6]; + + // WHEN / THEN + expect(() => { + RuntimeDeterminer.determineLatestRuntime(DEFAULT_RUNTIME, runtimes); + }).toThrow(`Latest python runtime ${Runtime.PYTHON_3_6} is deprecated. You must upgrade to the latest code compatible python runtime`); + }); + + test('throws if runtimes are neither nodejs nor python', () => { + // GIVEN + const runtimes = [Runtime.JAVA_17, Runtime.RUBY_3_2]; + + // WHEN / THEN + expect(() => { + RuntimeDeterminer.determineLatestRuntime(DEFAULT_RUNTIME, runtimes); + }).toThrow('Compatible runtimes must contain only nodejs or python runtimes'); + }); +}); From e19f09073f2ede87a94ed0bfb8df04c776faa8e7 Mon Sep 17 00:00:00 2001 From: Francis Date: Thu, 7 Dec 2023 10:33:44 -0800 Subject: [PATCH 26/73] get or create cdk handler to force singleton cdk handler and migration Signed-off-by: Francis --- .../lib/handler-framework/classes.ts | 55 +++++++++++++++++-- .../lib/handler-framework/config.ts | 1 + .../lib/handler-framework/constructors.ts | 15 +---- .../lib/handler-framework/framework.ts | 3 +- .../lib/handler-framework/modules.ts | 7 +++ .../scripts/generate.ts | 2 +- .../lib/dns-validated-certificate.ts | 8 +-- .../lib/experimental/edge-function.ts | 8 +-- .../lib/drain-hook/instance-drain-hook.ts | 9 +-- .../aws-events-targets/lib/aws-api.ts | 7 +-- 10 files changed, 72 insertions(+), 43 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts index 1c725992ec927..648eeacdc9e62 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts @@ -1,5 +1,5 @@ /* eslint-disable import/no-extraneous-dependencies */ -import { ClassType, stmt, expr, Type, ExternalModule, PropertySpec, InterfaceSpec, InterfaceType } from '@cdklabs/typewriter'; +import { ClassType, stmt, expr, Type, MemberVisibility, ExternalModule, PropertySpec, InterfaceSpec, InterfaceType } from '@cdklabs/typewriter'; import { CdkHandlerFrameworkConstructor } from './constructors'; import { CdkHandlerFrameworkModule } from './framework'; import { CDK_HANDLER_MODULE, CONSTRUCTS_MODULE, LAMBDA_MODULE, CORE_MODULE } from './modules'; @@ -73,7 +73,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { public readonly compatibleRuntimes: FrameworkRuntime[]; public readonly constructorPropsType = LAMBDA_MODULE.FunctionOptions; - protected readonly externalModules = [CONSTRUCTS_MODULE, LAMBDA_MODULE, CDK_HANDLER_MODULE]; + protected readonly externalModules = [CONSTRUCTS_MODULE, CORE_MODULE, LAMBDA_MODULE, CDK_HANDLER_MODULE]; public constructor() { super(scope, { @@ -86,6 +86,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { this.compatibleRuntimes = props.compatibleRuntimes; this.externalModules.forEach(module => scope.addExternalModule(module)); + this.buildGetOrCreateCdkHandler(); CdkHandlerFrameworkConstructor.forCdkFunction(this); } @@ -102,7 +103,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { public readonly compatibleRuntimes: FrameworkRuntime[]; public readonly constructorPropsType: Type; - protected readonly externalModules = [CONSTRUCTS_MODULE, LAMBDA_MODULE, CDK_HANDLER_MODULE]; + protected readonly externalModules = [CONSTRUCTS_MODULE, CORE_MODULE, LAMBDA_MODULE, CDK_HANDLER_MODULE]; public constructor() { super(scope, { @@ -115,6 +116,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { this.compatibleRuntimes = props.compatibleRuntimes; this.externalModules.forEach(module => scope.addExternalModule(module)); + this.buildGetOrCreateCdkHandler(); const uuid: PropertySpec = { name: 'uuid', @@ -159,7 +161,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { public readonly compatibleRuntimes: FrameworkRuntime[]; public readonly constructorPropsType = CORE_MODULE.CustomResourceProviderOptions; - protected readonly externalModules = [CONSTRUCTS_MODULE, CORE_MODULE, CDK_HANDLER_MODULE]; + protected readonly externalModules = [CONSTRUCTS_MODULE, CORE_MODULE, LAMBDA_MODULE, CDK_HANDLER_MODULE]; public constructor() { super(scope, { @@ -172,6 +174,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { this.compatibleRuntimes = props.compatibleRuntimes; this.externalModules.forEach(module => scope.addExternalModule(module)); + this.buildGetOrCreateCdkHandler(); const getOrCreateMethod = this.addMethod({ name: 'getOrCreate', @@ -221,7 +224,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { stmt.constVar(expr.ident('id'), expr.directCode('`${uniqueid}CustomResourceProvider`')), stmt.constVar(expr.ident('stack'), expr.directCode('cdk.Stack.of(scope)')), stmt.constVar(expr.ident('existing'), expr.directCode(`stack.node.tryFindChild(id) as ${this.type}`)), - stmt.ret(expr.directCode(`existing ?? new ${this.name}(scope, id, props)`)), + stmt.ret(expr.directCode(`existing ?? new ${this.name}(stack, id, props)`)), ); CdkHandlerFrameworkConstructor.forCdkCustomResourceProvider(this); @@ -265,4 +268,46 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { scope.registerInterface(_interface); return _interface; } + + private buildGetOrCreateCdkHandler() { + const getOrCreateCdkHandler = this.addMethod({ + name: 'getOrCreateCdkHandler', + returnType: CDK_HANDLER_MODULE.CdkHandler, + static: true, + visibility: MemberVisibility.Private, + }); + + getOrCreateCdkHandler.addParameter({ + name: 'scope', + type: CONSTRUCTS_MODULE.Construct, + }); + getOrCreateCdkHandler.addParameter({ + name: 'uniqueid', + type: Type.STRING, + }); + + getOrCreateCdkHandler.addBody( + stmt.constVar( + expr.ident('id'), + expr.directCode('`${uniqueid}Handler`'), + ), + stmt.constVar( + expr.ident('stack'), + expr.directCode('cdk.Stack.of(scope)'), + ), + stmt.constVar( + expr.ident('existing'), + expr.directCode('stack.node.tryFindChild(id) as handler.CdkHandler'), + ), + stmt.if_(expr.ident('existing')).then(stmt.ret(expr.ident('existing'))), + stmt.constVar( + expr.directCode('cdkHandlerProps: handler.CdkHandlerProps'), + expr.object({ + codeDirectory: expr.directCode(`path.join(__dirname, '${this.codeDirectory}')`), + compatibleRuntimes: expr.directCode(`[${this.compatibleRuntimes }]`), + }), + ), + stmt.ret(expr.directCode('new handler.CdkHandler(stack, id, cdkHandlerProps)')), + ); + } } diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts index 4b338e22b233b..aef08fbbef0d3 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts @@ -73,6 +73,7 @@ export const config: HandlerFrameworkConfig = { name: 'CertificateRequestFunction', sourceCode: path.resolve(__dirname, '..', 'aws-certificatemanager', 'dns-validated-certificate-handler', 'index.js'), compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + entrypoint: 'index.certificateRequestHandler', }, ], }, diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts index da1571f7ab33e..679150d951c8c 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts @@ -30,7 +30,7 @@ export class CdkHandlerFrameworkConstructor { public static forCdkCustomResourceProvider(_class: CdkHandlerFrameworkClass) { const superProps = new ObjectLiteral([ new Splat(expr.ident('props')), - ['codeDirectory', expr.lit(_class.codeDirectory)], + ['codeDirectory', expr.directCode(`path.join(__dirname, '${_class.codeDirectory}')`)], ['runtimeName', expr.directCode('cdkHandler.runtime.name')], ]); CdkHandlerFrameworkConstructor.forClass(_class, superProps); @@ -52,20 +52,11 @@ export class CdkHandlerFrameworkConstructor { type: _class.constructorPropsType, }); - // build CdkHandler instance - init.addBody( - stmt.constVar( - expr.directCode('cdkHandlerProps: handler.CdkHandlerProps'), - expr.object({ - codeDirectory: expr.lit(_class.codeDirectory), - compatibleRuntimes: expr.directCode(`[${ _class.compatibleRuntimes }]`), - }), - ), - ); + // get or create cdk handler init.addBody( stmt.constVar( expr.ident('cdkHandler'), - expr.directCode("new handler.CdkHandler(scope, 'Handler', cdkHandlerProps)"), + expr.directCode(`${_class.name}.getOrCreateCdkHandler(scope, id)`), ), ); diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts index 8056039fb4e7f..bc82cb9918abe 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts @@ -3,7 +3,7 @@ import { ExternalModule, InterfaceType, Module, TypeScriptRenderer } from '@cdkl import * as fs from 'fs-extra'; import { CdkHandlerClassProps, CdkHandlerFrameworkClass } from './classes'; import { ComponentType, ConfigProps } from './config'; -import { CDK_HANDLER_MODULE, CONSTRUCTS_MODULE, CORE_MODULE, LAMBDA_MODULE } from './modules'; +import { CDK_HANDLER_MODULE, CONSTRUCTS_MODULE, CORE_MODULE, LAMBDA_MODULE, PATH_MODULE } from './modules'; export class CdkHandlerFrameworkModule extends Module { private readonly renderer = new TypeScriptRenderer(); @@ -75,6 +75,7 @@ export class CdkHandlerFrameworkModule extends Module { } private importExternalModules() { + PATH_MODULE.import(this, 'path'); for (const fqn of this.externalModules.keys()) { switch (fqn) { case CONSTRUCTS_MODULE.fqn: { diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts index 5de5d52bb83ed..6d18f53c91686 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts @@ -1,6 +1,12 @@ /* eslint-disable import/no-extraneous-dependencies */ import { ExternalModule, Type } from '@cdklabs/typewriter'; +class PathModule extends ExternalModule { + public constructor() { + super('path'); + } +} + class ConstructsModule extends ExternalModule { public readonly Construct = Type.fromName(this, 'Construct'); public readonly IConstruct = Type.fromName(this, 'IConstruct'); @@ -38,6 +44,7 @@ class LambdaModule extends ExternalModule { } } +export const PATH_MODULE = new PathModule(); export const CONSTRUCTS_MODULE = new ConstructsModule(); export const CDK_HANDLER_MODULE = new CdkHandlerModule(); export const CORE_MODULE = new CoreModule(); diff --git a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts index 83a020abc8e4a..9773099abdd21 100644 --- a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts +++ b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts @@ -19,7 +19,7 @@ async function main() { await bundleAndMinify(component.sourceCode, outfile); } const sourceCodeDirectory = path.dirname(outfile).split('/').pop(); - module.build(component, `./${sourceCodeDirectory}`); + module.build(component, `${sourceCodeDirectory}`); } module.render(renderLocation); } diff --git a/packages/aws-cdk-lib/aws-certificatemanager/lib/dns-validated-certificate.ts b/packages/aws-cdk-lib/aws-certificatemanager/lib/dns-validated-certificate.ts index d2826d6fd796b..7f730a28eab42 100644 --- a/packages/aws-cdk-lib/aws-certificatemanager/lib/dns-validated-certificate.ts +++ b/packages/aws-cdk-lib/aws-certificatemanager/lib/dns-validated-certificate.ts @@ -1,12 +1,11 @@ -import * as path from 'path'; import { Construct } from 'constructs'; import { CertificateProps, ICertificate } from './certificate'; import { CertificateBase } from './certificate-base'; import * as iam from '../../aws-iam'; -import * as lambda from '../../aws-lambda'; import * as route53 from '../../aws-route53'; import * as cdk from '../../core'; import { Token } from '../../core'; +import { CertificateRequestFunction } from '../../custom-resource-handlers/dist/aws-certificatemanager/certificate-request-provider.generated'; /** * Properties to create a DNS validated certificate managed by AWS Certificate Manager @@ -106,10 +105,7 @@ export class DnsValidatedCertificate extends CertificateBase implements ICertifi certificateTransparencyLoggingPreference = props.transparencyLoggingEnabled ? 'ENABLED' : 'DISABLED'; } - const requestorFunction = new lambda.Function(this, 'CertificateRequestorFunction', { - code: lambda.Code.fromAsset(path.resolve(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-certificatemanager', 'dns-validated-certificate-handler')), - handler: 'index.certificateRequestHandler', - runtime: lambda.Runtime.NODEJS_18_X, + const requestorFunction = new CertificateRequestFunction(this, 'CertificateRequestorFunction', { timeout: cdk.Duration.minutes(15), role: props.customResourceRole, }); diff --git a/packages/aws-cdk-lib/aws-cloudfront/lib/experimental/edge-function.ts b/packages/aws-cdk-lib/aws-cloudfront/lib/experimental/edge-function.ts index 27ac19d11c25b..0dcf4c581598c 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/lib/experimental/edge-function.ts +++ b/packages/aws-cdk-lib/aws-cloudfront/lib/experimental/edge-function.ts @@ -1,4 +1,3 @@ -import * as path from 'path'; import { Construct, Node } from 'constructs'; import * as cloudwatch from '../../../aws-cloudwatch'; import * as ec2 from '../../../aws-ec2'; @@ -8,14 +7,13 @@ import * as ssm from '../../../aws-ssm'; import { CfnResource, CustomResource, - CustomResourceProvider, - CustomResourceProviderRuntime, Lazy, Resource, Stack, Stage, Token, } from '../../../core'; +import { CrossRegionStringParamReaderProvider } from '../../../custom-resource-handlers/dist/aws-cloudfront/cross-region-string-param-reader-provider.generated'; /** * Properties for creating a Lambda@Edge function @@ -199,9 +197,7 @@ export class EdgeFunction extends Resource implements lambda.IVersion { }); const resourceType = 'Custom::CrossRegionStringParameterReader'; - const serviceToken = CustomResourceProvider.getOrCreate(this, resourceType, { - codeDirectory: path.join(__dirname, '..', '..', '..', 'custom-resource-handlers', 'dist', 'aws-cloudfront', 'edge-function'), - runtime: CustomResourceProviderRuntime.NODEJS_18_X, + const serviceToken = CrossRegionStringParamReaderProvider.getOrCreate(this, resourceType, { policyStatements: [{ Effect: 'Allow', Resource: parameterArnPrefix, diff --git a/packages/aws-cdk-lib/aws-ecs/lib/drain-hook/instance-drain-hook.ts b/packages/aws-cdk-lib/aws-ecs/lib/drain-hook/instance-drain-hook.ts index 3390e47001cc0..598ff6befdc8c 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/drain-hook/instance-drain-hook.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/drain-hook/instance-drain-hook.ts @@ -1,12 +1,10 @@ -import * as fs from 'fs'; -import * as path from 'path'; import { Construct } from 'constructs'; import * as autoscaling from '../../../aws-autoscaling'; import * as hooks from '../../../aws-autoscaling-hooktargets'; import * as iam from '../../../aws-iam'; import * as kms from '../../../aws-kms'; -import * as lambda from '../../../aws-lambda'; import * as cdk from '../../../core'; +import { DrainHookFunction } from '../../../custom-resource-handlers/dist/aws-ecs/drain-hook-provider.generated'; import { ICluster } from '../cluster'; // Reference for the source in this package: @@ -60,10 +58,7 @@ export class InstanceDrainHook extends Construct { const drainTime = props.drainTime || cdk.Duration.minutes(5); // Invoke Lambda via SNS Topic - const fn = new lambda.Function(this, 'Function', { - code: lambda.Code.fromInline(fs.readFileSync(path.join(__dirname, '..', '..', '..', 'custom-resource-handlers', 'dist', 'aws-ecs', 'lambda-source', 'index.py'), { encoding: 'utf-8' })), - handler: 'index.lambda_handler', - runtime: lambda.Runtime.PYTHON_3_9, + const fn = new DrainHookFunction(this, 'Function', { // Timeout: some extra margin for additional API calls made by the Lambda, // up to a maximum of 15 minutes. timeout: cdk.Duration.seconds(Math.min(drainTime.toSeconds() + 10, 900)), diff --git a/packages/aws-cdk-lib/aws-events-targets/lib/aws-api.ts b/packages/aws-cdk-lib/aws-events-targets/lib/aws-api.ts index 1dd24040ee6a2..a515a2feeb602 100644 --- a/packages/aws-cdk-lib/aws-events-targets/lib/aws-api.ts +++ b/packages/aws-cdk-lib/aws-events-targets/lib/aws-api.ts @@ -1,10 +1,10 @@ -import * as path from 'path'; import { metadata } from './sdk-api-metadata.generated'; import { addLambdaPermission } from './util'; import * as events from '../../aws-events'; import * as iam from '../../aws-iam'; import * as lambda from '../../aws-lambda'; import { Annotations, Duration } from '../../core'; +import { AwsApiFunction } from '../../custom-resource-handlers/dist/aws-events-targets/aws-api-provider.generated'; /** * AWS SDK service metadata. @@ -81,12 +81,9 @@ export class AwsApi implements events.IRuleTarget { * result from an EventBridge event. */ public bind(rule: events.IRule, id?: string): events.RuleTargetConfig { - const handler = new lambda.SingletonFunction(rule as events.Rule, `${rule.node.id}${id}Handler`, { - code: lambda.Code.fromAsset(path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-events-targets', 'aws-api-handler')), - runtime: lambda.Runtime.NODEJS_18_X, + const handler = new AwsApiFunction(rule as events.Rule, `${rule.node.id}${id}Handler`, { timeout: Duration.seconds(60), memorySize: 256, - handler: 'index.handler', uuid: 'b4cf1abd-4e4f-4bc6-9944-1af7ccd9ec37', lambdaPurpose: 'AWS', }); From 6712a4e7cc1b9cecc3250c1f94410444ee590e87 Mon Sep 17 00:00:00 2001 From: Francis Date: Thu, 7 Dec 2023 10:39:17 -0800 Subject: [PATCH 27/73] merge conflicts Signed-off-by: Francis --- .../handler-framework/lib/cdk-handler.ts | 6 ++++++ .../handler-framework/test/cdk-handler.test.ts | 14 ++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/packages/aws-cdk-lib/handler-framework/lib/cdk-handler.ts b/packages/aws-cdk-lib/handler-framework/lib/cdk-handler.ts index 0af8ab1df99b2..d82edcfe9bde2 100644 --- a/packages/aws-cdk-lib/handler-framework/lib/cdk-handler.ts +++ b/packages/aws-cdk-lib/handler-framework/lib/cdk-handler.ts @@ -27,6 +27,11 @@ export class CdkHandler extends Construct { */ private static readonly DEFAULT_RUNTIME = Runtime.NODEJS_LATEST; + /** + * The local file system directory with the provider's code. + */ + public readonly codeDirectory: string; + /** * The source code of your Lambda function. */ @@ -39,6 +44,7 @@ export class CdkHandler extends Construct { public constructor(scope: Construct, id: string, props: CdkHandlerProps) { super(scope, id); + this.codeDirectory = props.codeDirectory; this.code = Code.fromAsset(props.codeDirectory); this.runtime = RuntimeDeterminer.determineLatestRuntime(CdkHandler.DEFAULT_RUNTIME, props.compatibleRuntimes); } diff --git a/packages/aws-cdk-lib/handler-framework/test/cdk-handler.test.ts b/packages/aws-cdk-lib/handler-framework/test/cdk-handler.test.ts index 873df759b58fe..f3e712056a518 100644 --- a/packages/aws-cdk-lib/handler-framework/test/cdk-handler.test.ts +++ b/packages/aws-cdk-lib/handler-framework/test/cdk-handler.test.ts @@ -9,6 +9,20 @@ describe('cdk handler', () => { codeDirectory = path.join(__dirname, 'test-handler'); }); + test('code directory property is correctly set', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + const handler = new CdkHandler(stack, 'CdkHandler', { + codeDirectory, + compatibleRuntimes: [Runtime.NODEJS_16_X, Runtime.NODEJS_LATEST, Runtime.PYTHON_3_12], + }); + + // THEN + expect(handler.codeDirectory).toEqual(codeDirectory); + }); + test('runtime property is correctly set', () => { // GIVEN const stack = new Stack(); From fd0b93f6451fb828454295e06ed62624138e3ea5 Mon Sep 17 00:00:00 2001 From: Francis Date: Thu, 7 Dec 2023 11:01:31 -0800 Subject: [PATCH 28/73] refactor for cdk handler change and updated config Signed-off-by: Francis --- .../lib/handler-framework/classes.ts | 3 ++ .../lib/handler-framework/config.ts | 48 +++++++++++++++++++ .../lib/handler-framework/constructors.ts | 2 +- 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts index 648eeacdc9e62..baa2e018d1186 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts @@ -275,6 +275,9 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { returnType: CDK_HANDLER_MODULE.CdkHandler, static: true, visibility: MemberVisibility.Private, + docs: { + summary: 'Returns a stack-level singleton cdk handler instance.', + }, }); getOrCreateCdkHandler.addParameter({ diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts index aef08fbbef0d3..16971ea6407f2 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts @@ -202,4 +202,52 @@ export const config: HandlerFrameworkConfig = { }, ], }, + 'aws-iam': { + 'oidc-provider': [ + { + type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + name: 'OidcProvider', + sourceCode: path.resolve(__dirname, '..', 'aws-iam', 'oidc-handler', 'index.ts'), + compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + }, + ], + }, + 'aws-route53': { + 'cross-account-zone-delegation-provider': [ + { + type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + name: 'CrossAccountZoneDelegationProvider', + sourceCode: path.resolve(__dirname, '..', 'aws-route53', 'cross-account-zone-delegation-handler', 'index.ts'), + compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + }, + ], + 'delete-existing-record-set-provider': [ + { + type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + name: 'DeleteExistingRecordSetProvider', + sourceCode: path.resolve(__dirname, '..', 'aws-route53', 'cdelete-existing-record-set-handler', 'index.ts'), + compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + }, + ], + }, + 'aws-s3': { + 'auto-delete-objects-provider': [ + { + type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + name: 'AutoDeleteObjectsProvider', + sourceCode: path.resolve(__dirname, '..', 'aws-s3', 'auto-delete-objects-handler', 'index.ts'), + compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + }, + ], + }, + 'core': { + 'nodejs-entrypoint': [ + { + type: ComponentType.CDK_NO_OP, + name: 'NodejsEntrypointHandler', + sourceCode: path.resolve(__dirname, '..', 'core', 'nodejs-entrypoint-handler', 'index.ts'), + compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + }, + ], + }, }; diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts index 679150d951c8c..9b245218c1e1a 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts @@ -30,7 +30,7 @@ export class CdkHandlerFrameworkConstructor { public static forCdkCustomResourceProvider(_class: CdkHandlerFrameworkClass) { const superProps = new ObjectLiteral([ new Splat(expr.ident('props')), - ['codeDirectory', expr.directCode(`path.join(__dirname, '${_class.codeDirectory}')`)], + ['codeDirectory', expr.directCode('cdkHandler.codeDirectory')], ['runtimeName', expr.directCode('cdkHandler.runtime.name')], ]); CdkHandlerFrameworkConstructor.forClass(_class, superProps); From bacd7b63b9250ac745d670b841ddf4ee1b383e12 Mon Sep 17 00:00:00 2001 From: Francis Date: Thu, 7 Dec 2023 17:50:07 -0800 Subject: [PATCH 29/73] move runtime determiner and create runtime class to break circular dependency Signed-off-by: Francis --- .../lib/handler-framework/classes.ts | 111 +++-------------- .../lib/handler-framework/config.ts | 88 ++++++++++---- .../lib/handler-framework/constructors.ts | 28 ++--- .../lib/handler-framework/framework.ts | 41 +++++-- .../lib/handler-framework/modules.ts | 10 +- .../handler-framework}/runtime-determiner.ts | 10 +- .../lib/handler-framework/runtime.ts | 79 ++++++++++++ .../custom-resource-handlers/tsconfig.json | 3 +- .../aws-eks/lib/cluster-resource-provider.ts | 14 +-- .../aws-eks/lib/kubectl-provider.ts | 8 +- .../aws-cdk-lib/aws-lambda/lib/runtime.ts | 81 +++---------- .../aws-lambda/test/runtime.test.ts | 23 ---- .../aws-cdk-lib/aws-route53/lib/record-set.ts | 15 +-- .../aws-custom-resource.ts | 8 +- .../handler-framework/lib/cdk-handler.ts | 51 -------- .../lib/runtime-determiner.ts | 113 ++++++++++++++++++ .../test/cdk-handler.test.ts | 39 ------ .../{utils => }/runtime-determiner.test.ts | 18 +-- 18 files changed, 359 insertions(+), 381 deletions(-) rename packages/{aws-cdk-lib/handler-framework/lib/utils => @aws-cdk/custom-resource-handlers/lib/handler-framework}/runtime-determiner.ts (92%) create mode 100644 packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/runtime.ts delete mode 100644 packages/aws-cdk-lib/handler-framework/lib/cdk-handler.ts create mode 100644 packages/aws-cdk-lib/handler-framework/lib/runtime-determiner.ts delete mode 100644 packages/aws-cdk-lib/handler-framework/test/cdk-handler.test.ts rename packages/aws-cdk-lib/handler-framework/test/{utils => }/runtime-determiner.test.ts (81%) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts index baa2e018d1186..eaf4b05aeb49f 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts @@ -1,38 +1,9 @@ /* eslint-disable import/no-extraneous-dependencies */ -import { ClassType, stmt, expr, Type, MemberVisibility, ExternalModule, PropertySpec, InterfaceSpec, InterfaceType } from '@cdklabs/typewriter'; +import { ClassType, stmt, expr, Type, ExternalModule, PropertySpec, InterfaceSpec, InterfaceType } from '@cdklabs/typewriter'; import { CdkHandlerFrameworkConstructor } from './constructors'; import { CdkHandlerFrameworkModule } from './framework'; -import { CDK_HANDLER_MODULE, CONSTRUCTS_MODULE, LAMBDA_MODULE, CORE_MODULE } from './modules'; - -/** - * Runtimes that map to a Lambda runtime during codegen. - */ -export enum FrameworkRuntime { - /** - * The NodeJS 16.x runtime. - */ - NODEJS_16_X = 'lambda.Runtime.NODEJS_16_X', - - /** - * The NodeJS 18.x runtime. - */ - NODEJS_18_X = 'lambda.Runtime.NODEJS_18_X', - - /** - * The NodeJS 20.x runtime. - */ - NODEJS_20_X = 'lambda.Runtime.NODEJS_20_X', - - /** - * The Python 3.9 runtime. - */ - PYTHON_3_9 = 'lambda.Runtime.PYTHON_3_9', - - /** - * The Python 3.10 runtime. - */ - PYTHON_3_10 = 'lambda.Runtime.PYTHON_3_10', -} +import { HANDLER_FRAMEWORK_MODULE, CONSTRUCTS_MODULE, LAMBDA_MODULE, CORE_MODULE } from './modules'; +import { Runtime } from './runtime'; /** * Initialization properties used to build a `CdkHandlerFrameworkClass` instance. @@ -50,9 +21,9 @@ export interface CdkHandlerClassProps { readonly codeDirectory: string; /** - * Runtimes that are compatible with the source code. + * The runtime environment for the Lambda function. */ - readonly compatibleRuntimes: FrameworkRuntime[]; + readonly runtime: Runtime; /** * The name of the method within your code that Lambda calls to execute your function. @@ -70,10 +41,10 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { return new (class CdkFunction extends CdkHandlerFrameworkClass { public readonly codeDirectory: string; public readonly entrypoint: string; - public readonly compatibleRuntimes: FrameworkRuntime[]; + public readonly runtime: Runtime; public readonly constructorPropsType = LAMBDA_MODULE.FunctionOptions; - protected readonly externalModules = [CONSTRUCTS_MODULE, CORE_MODULE, LAMBDA_MODULE, CDK_HANDLER_MODULE]; + protected readonly externalModules = [CONSTRUCTS_MODULE, LAMBDA_MODULE, HANDLER_FRAMEWORK_MODULE]; public constructor() { super(scope, { @@ -83,10 +54,9 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { }); this.codeDirectory = props.codeDirectory; this.entrypoint = props.entrypoint ?? 'index.handler'; - this.compatibleRuntimes = props.compatibleRuntimes; + this.runtime = props.runtime; this.externalModules.forEach(module => scope.addExternalModule(module)); - this.buildGetOrCreateCdkHandler(); CdkHandlerFrameworkConstructor.forCdkFunction(this); } @@ -100,10 +70,10 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { return new (class CdkSingletonFunction extends CdkHandlerFrameworkClass { public readonly codeDirectory: string; public readonly entrypoint: string; - public readonly compatibleRuntimes: FrameworkRuntime[]; + public readonly runtime: Runtime; public readonly constructorPropsType: Type; - protected readonly externalModules = [CONSTRUCTS_MODULE, CORE_MODULE, LAMBDA_MODULE, CDK_HANDLER_MODULE]; + protected readonly externalModules = [CONSTRUCTS_MODULE, LAMBDA_MODULE, HANDLER_FRAMEWORK_MODULE]; public constructor() { super(scope, { @@ -113,10 +83,9 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { }); this.codeDirectory = props.codeDirectory; this.entrypoint = props.entrypoint ?? 'index.handler'; - this.compatibleRuntimes = props.compatibleRuntimes; + this.runtime = props.runtime; this.externalModules.forEach(module => scope.addExternalModule(module)); - this.buildGetOrCreateCdkHandler(); const uuid: PropertySpec = { name: 'uuid', @@ -158,10 +127,10 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { return new (class CdkCustomResourceProvider extends CdkHandlerFrameworkClass { public readonly codeDirectory: string; public readonly entrypoint: string; - public readonly compatibleRuntimes: FrameworkRuntime[]; + public readonly runtime: Runtime; public readonly constructorPropsType = CORE_MODULE.CustomResourceProviderOptions; - protected readonly externalModules = [CONSTRUCTS_MODULE, CORE_MODULE, LAMBDA_MODULE, CDK_HANDLER_MODULE]; + protected readonly externalModules = [CONSTRUCTS_MODULE, CORE_MODULE, HANDLER_FRAMEWORK_MODULE]; public constructor() { super(scope, { @@ -171,10 +140,9 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { }); this.codeDirectory = props.codeDirectory; this.entrypoint = props.entrypoint ?? 'index.handler'; - this.compatibleRuntimes = props.compatibleRuntimes; + this.runtime = props.runtime; this.externalModules.forEach(module => scope.addExternalModule(module)); - this.buildGetOrCreateCdkHandler(); const getOrCreateMethod = this.addMethod({ name: 'getOrCreate', @@ -222,7 +190,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { }); getOrCreateProviderMethod.addBody( stmt.constVar(expr.ident('id'), expr.directCode('`${uniqueid}CustomResourceProvider`')), - stmt.constVar(expr.ident('stack'), expr.directCode('cdk.Stack.of(scope)')), + stmt.constVar(expr.ident('stack'), expr.directCode('Stack.of(scope)')), stmt.constVar(expr.ident('existing'), expr.directCode(`stack.node.tryFindChild(id) as ${this.type}`)), stmt.ret(expr.directCode(`existing ?? new ${this.name}(stack, id, props)`)), ); @@ -244,9 +212,9 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { public abstract readonly entrypoint: string; /** - * Runtimes that are compatible with the code that this class will execute. + * The runtime environment for the Lambda function. */ - public abstract readonly compatibleRuntimes: FrameworkRuntime[]; + public abstract readonly runtime: Runtime; /** * Properties used to initialize this class. @@ -268,49 +236,4 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { scope.registerInterface(_interface); return _interface; } - - private buildGetOrCreateCdkHandler() { - const getOrCreateCdkHandler = this.addMethod({ - name: 'getOrCreateCdkHandler', - returnType: CDK_HANDLER_MODULE.CdkHandler, - static: true, - visibility: MemberVisibility.Private, - docs: { - summary: 'Returns a stack-level singleton cdk handler instance.', - }, - }); - - getOrCreateCdkHandler.addParameter({ - name: 'scope', - type: CONSTRUCTS_MODULE.Construct, - }); - getOrCreateCdkHandler.addParameter({ - name: 'uniqueid', - type: Type.STRING, - }); - - getOrCreateCdkHandler.addBody( - stmt.constVar( - expr.ident('id'), - expr.directCode('`${uniqueid}Handler`'), - ), - stmt.constVar( - expr.ident('stack'), - expr.directCode('cdk.Stack.of(scope)'), - ), - stmt.constVar( - expr.ident('existing'), - expr.directCode('stack.node.tryFindChild(id) as handler.CdkHandler'), - ), - stmt.if_(expr.ident('existing')).then(stmt.ret(expr.ident('existing'))), - stmt.constVar( - expr.directCode('cdkHandlerProps: handler.CdkHandlerProps'), - expr.object({ - codeDirectory: expr.directCode(`path.join(__dirname, '${this.codeDirectory}')`), - compatibleRuntimes: expr.directCode(`[${this.compatibleRuntimes }]`), - }), - ), - stmt.ret(expr.directCode('new handler.CdkHandler(stack, id, cdkHandlerProps)')), - ); - } } diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts index 16971ea6407f2..7eb420fd90546 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts @@ -1,5 +1,5 @@ import * as path from 'path'; -import { FrameworkRuntime } from './classes'; +import { Runtime } from './runtime'; /** * Handler framework component types. @@ -48,7 +48,7 @@ export interface ConfigProps { /** * Runtimes that are compatible with the source code. */ - readonly compatibleRuntimes: FrameworkRuntime[]; + readonly compatibleRuntimes: Runtime[]; /** * The name of the method within your code that Lambda calls to execute your function. @@ -72,7 +72,7 @@ export const config: HandlerFrameworkConfig = { type: ComponentType.CDK_FUNCTION, name: 'CertificateRequestFunction', sourceCode: path.resolve(__dirname, '..', 'aws-certificatemanager', 'dns-validated-certificate-handler', 'index.js'), - compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + compatibleRuntimes: [Runtime.NODEJS_18_X], entrypoint: 'index.certificateRequestHandler', }, ], @@ -83,7 +83,7 @@ export const config: HandlerFrameworkConfig = { type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, name: 'CrossRegionStringParamReaderProvider', sourceCode: path.resolve(__dirname, '..', 'aws-cloudfront', 'edge-function', 'index.js'), - compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], }, @@ -93,14 +93,14 @@ export const config: HandlerFrameworkConfig = { type: ComponentType.CDK_FUNCTION, name: 'OnEventFunction', sourceCode: path.resolve(__dirname, '..', 'aws-dynamodb', 'replica-handler', 'index.ts'), - compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + compatibleRuntimes: [Runtime.NODEJS_18_X], entrypoint: 'index.onEventHandler', }, { type: ComponentType.CDK_FUNCTION, name: 'IsCompleteFunction', sourceCode: path.resolve(__dirname, '..', 'aws-dynamodb', 'replica-handler', 'index.ts'), - compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + compatibleRuntimes: [Runtime.NODEJS_18_X], entrypoint: 'index.isCompleteHandler', }, ], @@ -111,7 +111,7 @@ export const config: HandlerFrameworkConfig = { type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, name: 'RestrictDefaultSgProvider', sourceCode: path.resolve(__dirname, '..', 'aws-ec2', 'restrict-default-security-group-handler', 'index.ts'), - compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], }, @@ -121,7 +121,7 @@ export const config: HandlerFrameworkConfig = { type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, name: 'AutoDeleteImagesProvider', sourceCode: path.resolve(__dirname, '..', 'aws-ecr', 'auto-delete-images-handler', 'index.ts'), - compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], }, @@ -131,7 +131,7 @@ export const config: HandlerFrameworkConfig = { type: ComponentType.CDK_FUNCTION, name: 'DrainHookFunction', sourceCode: path.resolve(__dirname, '..', 'aws-ecs', 'lambda-source', 'index.py'), - compatibleRuntimes: [FrameworkRuntime.PYTHON_3_9], + compatibleRuntimes: [Runtime.PYTHON_3_9], entrypoint: 'index.lambda_handler', disableBundleAndMinify: true, }, @@ -143,14 +143,14 @@ export const config: HandlerFrameworkConfig = { type: ComponentType.CDK_FUNCTION, name: 'OnEventFunction', sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'cluster-resource-handler', 'index.ts'), - compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + compatibleRuntimes: [Runtime.NODEJS_18_X], entrypoint: 'index.onEvent', }, { type: ComponentType.CDK_FUNCTION, name: 'IsCompleteFunction', sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'cluster-resource-handler', 'index.ts'), - compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + compatibleRuntimes: [Runtime.NODEJS_18_X], entrypoint: 'index.isComplete', }, ], @@ -159,35 +159,35 @@ export const config: HandlerFrameworkConfig = { type: ComponentType.CDK_FUNCTION, name: 'KubectlFunction', sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'index.py'), - compatibleRuntimes: [FrameworkRuntime.PYTHON_3_10], + compatibleRuntimes: [Runtime.PYTHON_3_10], disableBundleAndMinify: true, }, { type: ComponentType.CDK_NO_OP, name: 'ApplyHandler', sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'apply', '__init__.py'), - compatibleRuntimes: [FrameworkRuntime.PYTHON_3_10], + compatibleRuntimes: [Runtime.PYTHON_3_10], disableBundleAndMinify: true, }, { type: ComponentType.CDK_NO_OP, name: 'GetHandler', sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'get', '__init__.py'), - compatibleRuntimes: [FrameworkRuntime.PYTHON_3_10], + compatibleRuntimes: [Runtime.PYTHON_3_10], disableBundleAndMinify: true, }, { type: ComponentType.CDK_NO_OP, name: 'HelmHandler', sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'helm', '__init__.py'), - compatibleRuntimes: [FrameworkRuntime.PYTHON_3_10], + compatibleRuntimes: [Runtime.PYTHON_3_10], disableBundleAndMinify: true, }, { type: ComponentType.CDK_NO_OP, name: 'PatchHandler', sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'patch', '__init__.py'), - compatibleRuntimes: [FrameworkRuntime.PYTHON_3_10], + compatibleRuntimes: [Runtime.PYTHON_3_10], disableBundleAndMinify: true, }, ], @@ -198,7 +198,7 @@ export const config: HandlerFrameworkConfig = { type: ComponentType.CDK_SINGLETON_FUNCTION, name: 'AwsApiFunction', sourceCode: path.resolve(__dirname, '..', 'aws-events-targets', 'aws-api-handler', 'index.ts'), - compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], }, @@ -208,7 +208,17 @@ export const config: HandlerFrameworkConfig = { type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, name: 'OidcProvider', sourceCode: path.resolve(__dirname, '..', 'aws-iam', 'oidc-handler', 'index.ts'), - compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + compatibleRuntimes: [Runtime.NODEJS_18_X], + }, + ], + }, + 'aws-logs': { + 'log-retention': [ + { + type: ComponentType.CDK_NO_OP, + name: 'LogRetentionHandler', + sourceCode: path.resolve(__dirname, '..', 'aws-logs', 'log-retention-handler', 'index.ts'), + compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], }, @@ -218,15 +228,15 @@ export const config: HandlerFrameworkConfig = { type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, name: 'CrossAccountZoneDelegationProvider', sourceCode: path.resolve(__dirname, '..', 'aws-route53', 'cross-account-zone-delegation-handler', 'index.ts'), - compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], 'delete-existing-record-set-provider': [ { type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, name: 'DeleteExistingRecordSetProvider', - sourceCode: path.resolve(__dirname, '..', 'aws-route53', 'cdelete-existing-record-set-handler', 'index.ts'), - compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + sourceCode: path.resolve(__dirname, '..', 'aws-route53', 'delete-existing-record-set-handler', 'index.ts'), + compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], }, @@ -236,17 +246,47 @@ export const config: HandlerFrameworkConfig = { type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, name: 'AutoDeleteObjectsProvider', sourceCode: path.resolve(__dirname, '..', 'aws-s3', 'auto-delete-objects-handler', 'index.ts'), - compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + compatibleRuntimes: [Runtime.NODEJS_18_X], + }, + ], + }, + 'aws-s3-deployment': { + 'bucket-deployment-provider': [ + { + type: ComponentType.CDK_SINGLETON_FUNCTION, + name: 'BucketDeploymentProvider', + sourceCode: path.resolve(__dirname, '..', 'aws-s3-deployment', 'bucket-deployment-handler', 'index.py'), + compatibleRuntimes: [Runtime.PYTHON_3_9], + disableBundleAndMinify: true, }, ], }, 'core': { + 'cfn-utils-provider': [ + { + type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + name: 'CdkCfnUtilsProvider', + sourceCode: path.resolve(__dirname, '..', 'core', 'cfn-utils-provider', 'index.ts'), + compatibleRuntimes: [Runtime.NODEJS_18_X], + }, + ], 'nodejs-entrypoint': [ { type: ComponentType.CDK_NO_OP, name: 'NodejsEntrypointHandler', - sourceCode: path.resolve(__dirname, '..', 'core', 'nodejs-entrypoint-handler', 'index.ts'), - compatibleRuntimes: [FrameworkRuntime.NODEJS_18_X], + sourceCode: path.resolve(__dirname, '..', 'core', 'nodejs-entrypoint-handler', 'index.js'), + compatibleRuntimes: [Runtime.NODEJS_18_X], + disableBundleAndMinify: true, + }, + ], + }, + 'custom-resources': { + 'aws-custom-resource-provider': [ + { + type: ComponentType.CDK_SINGLETON_FUNCTION, + name: 'AwsCustomResourceFunction', + sourceCode: path.resolve(__dirname, '..', 'custom-resources', 'aws-custom-resource-handler', 'index.ts'), + compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], }, diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts index 9b245218c1e1a..24fa7d4938960 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts @@ -1,5 +1,5 @@ /* eslint-disable import/no-extraneous-dependencies */ -import { Expression, Type, stmt, expr, SuperInitializer, ObjectLiteral, Splat } from '@cdklabs/typewriter'; +import { Expression, Type, expr, SuperInitializer, ObjectLiteral, Splat, MemberVisibility } from '@cdklabs/typewriter'; import { CdkHandlerFrameworkClass } from './classes'; import { CONSTRUCTS_MODULE } from './modules'; @@ -10,11 +10,11 @@ export class CdkHandlerFrameworkConstructor { public static forCdkFunction(_class: CdkHandlerFrameworkClass) { const superProps = new ObjectLiteral([ new Splat(expr.ident('props')), - ['code', expr.directCode('cdkHandler.code')], + ['code', expr.directCode(`lambda.Code.fromAsset(path.join(__dirname, '${_class.codeDirectory}'))`)], ['handler', expr.lit(_class.entrypoint)], - ['runtime', expr.directCode('cdkHandler.runtime')], + ['runtime', expr.directCode(_class.runtime.toLambdaRuntime())], ]); - CdkHandlerFrameworkConstructor.forClass(_class, superProps); + CdkHandlerFrameworkConstructor.forClass(_class, superProps, MemberVisibility.Public); } /** @@ -30,15 +30,14 @@ export class CdkHandlerFrameworkConstructor { public static forCdkCustomResourceProvider(_class: CdkHandlerFrameworkClass) { const superProps = new ObjectLiteral([ new Splat(expr.ident('props')), - ['codeDirectory', expr.directCode('cdkHandler.codeDirectory')], - ['runtimeName', expr.directCode('cdkHandler.runtime.name')], + ['codeDirectory', expr.directCode(`path.join(__dirname, '${_class.codeDirectory}')`)], + ['runtimeName', expr.directCode(_class.runtime.name)], ]); - CdkHandlerFrameworkConstructor.forClass(_class, superProps); + CdkHandlerFrameworkConstructor.forClass(_class, superProps, MemberVisibility.Private); } - private static forClass(_class: CdkHandlerFrameworkClass, superProps: Expression) { - // constructor - const init = _class.addInitializer({}); + private static forClass(_class: CdkHandlerFrameworkClass, superProps: Expression, visibility: MemberVisibility) { + const init = _class.addInitializer({ visibility }); const scope = init.addParameter({ name: 'scope', type: CONSTRUCTS_MODULE.Construct, @@ -52,15 +51,6 @@ export class CdkHandlerFrameworkConstructor { type: _class.constructorPropsType, }); - // get or create cdk handler - init.addBody( - stmt.constVar( - expr.ident('cdkHandler'), - expr.directCode(`${_class.name}.getOrCreateCdkHandler(scope, id)`), - ), - ); - - // build super call const superInitializerArgs: Expression[] = [scope, id, superProps]; init.addBody(new SuperInitializer(...superInitializerArgs)); } diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts index bc82cb9918abe..8406cdf6a54ff 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts @@ -3,30 +3,43 @@ import { ExternalModule, InterfaceType, Module, TypeScriptRenderer } from '@cdkl import * as fs from 'fs-extra'; import { CdkHandlerClassProps, CdkHandlerFrameworkClass } from './classes'; import { ComponentType, ConfigProps } from './config'; -import { CDK_HANDLER_MODULE, CONSTRUCTS_MODULE, CORE_MODULE, LAMBDA_MODULE, PATH_MODULE } from './modules'; +import { HANDLER_FRAMEWORK_MODULE, CONSTRUCTS_MODULE, CORE_MODULE, LAMBDA_MODULE, PATH_MODULE } from './modules'; +import { Runtime } from './runtime'; +import { RuntimeDeterminer } from './runtime-determiner'; export class CdkHandlerFrameworkModule extends Module { + /** + * The latest nodejs runtime version available across all AWS regions + */ + private static readonly DEFAULT_RUNTIME = Runtime.NODEJS_18_X; + private readonly renderer = new TypeScriptRenderer(); private readonly externalModules = new Map(); private readonly _interfaces = new Map(); + private hasComponents = false; public constructor(fqn: string) { super(fqn); } /** - * - * @param component - * @param sourceCodeDirectory + * Build a framework component inside of this module. */ public build(component: ConfigProps, sourceCodeDirectory: string) { const props: CdkHandlerClassProps = { codeDirectory: sourceCodeDirectory, name: component.name, - compatibleRuntimes: component.compatibleRuntimes, + runtime: RuntimeDeterminer.determineLatestRuntime( + component.compatibleRuntimes, + CdkHandlerFrameworkModule.DEFAULT_RUNTIME, + ), entrypoint: component.entrypoint, }; + if (!this.hasComponents && component.type !== ComponentType.CDK_NO_OP) { + this.hasComponents = true; + } + switch (component.type) { case ComponentType.CDK_FUNCTION: { CdkHandlerFrameworkClass.buildCdkFunction(this, props); @@ -47,8 +60,10 @@ export class CdkHandlerFrameworkModule extends Module { * Render built framework into an output file. */ public render(outputFileLocation: string) { - this.importExternalModules(); - fs.outputFileSync(`dist/${outputFileLocation}.generated.ts`, this.renderer.render(this)); + if (this.hasComponents) { + this.importExternalModules(); + fs.outputFileSync(`dist/${outputFileLocation}.generated.ts`, this.renderer.render(this)); + } } /** @@ -79,19 +94,23 @@ export class CdkHandlerFrameworkModule extends Module { for (const fqn of this.externalModules.keys()) { switch (fqn) { case CONSTRUCTS_MODULE.fqn: { - CONSTRUCTS_MODULE.import(this, 'constructs'); + CONSTRUCTS_MODULE.importSelective(this, ['Construct']); break; } case CORE_MODULE.fqn: { - CORE_MODULE.import(this, 'cdk'); + CORE_MODULE.importSelective(this, [ + 'Stack', + 'CustomResourceProviderBase', + 'CustomResourceProviderOptions', + ]); break; } case LAMBDA_MODULE.fqn: { LAMBDA_MODULE.import(this, 'lambda'); break; } - case CDK_HANDLER_MODULE.fqn: { - CDK_HANDLER_MODULE.import(this, 'handler'); + case HANDLER_FRAMEWORK_MODULE.fqn: { + HANDLER_FRAMEWORK_MODULE.importSelective(this, ['RuntimeDeterminer']); break; } } diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts index 6d18f53c91686..bef49f96d02e6 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts @@ -9,19 +9,17 @@ class PathModule extends ExternalModule { class ConstructsModule extends ExternalModule { public readonly Construct = Type.fromName(this, 'Construct'); - public readonly IConstruct = Type.fromName(this, 'IConstruct'); public constructor() { super('constructs'); } } -class CdkHandlerModule extends ExternalModule { - public readonly CdkHandler = Type.fromName(this, 'CdkHandler'); - public readonly CdkHandlerProps = Type.fromName(this, 'CdkHandlerProps'); +class HandlerFrameworkModule extends ExternalModule { + public readonly RuntimeDeterminer = Type.fromName(this, 'RuntimeDeterminer'); public constructor() { - super('../../../handler-framework/lib/cdk-handler'); + super('../../../handler-framework/lib/runtime-determiner'); } } @@ -46,6 +44,6 @@ class LambdaModule extends ExternalModule { export const PATH_MODULE = new PathModule(); export const CONSTRUCTS_MODULE = new ConstructsModule(); -export const CDK_HANDLER_MODULE = new CdkHandlerModule(); +export const HANDLER_FRAMEWORK_MODULE = new HandlerFrameworkModule(); export const CORE_MODULE = new CoreModule(); export const LAMBDA_MODULE = new LambdaModule(); diff --git a/packages/aws-cdk-lib/handler-framework/lib/utils/runtime-determiner.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/runtime-determiner.ts similarity index 92% rename from packages/aws-cdk-lib/handler-framework/lib/utils/runtime-determiner.ts rename to packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/runtime-determiner.ts index c36d831e37aa9..05bd84ed5f410 100644 --- a/packages/aws-cdk-lib/handler-framework/lib/utils/runtime-determiner.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/runtime-determiner.ts @@ -1,4 +1,4 @@ -import { Runtime, RuntimeFamily } from '../../../aws-lambda'; +import { Runtime, RuntimeFamily } from './runtime'; /** * A utility class used to determine the latest runtime for a specific runtime family @@ -14,13 +14,13 @@ export class RuntimeDeterminer { * @returns the latest nodejs or python runtime found, otherwise undefined if no nodejs or python * runtimes are specified */ - public static determineLatestRuntime(defaultRuntime: Runtime, runtimes: Runtime[]) { + public static determineLatestRuntime(runtimes: Runtime[], defaultRuntime: Runtime) { if (runtimes.length === 0) { throw new Error('You must specify at least one compatible runtime'); } const nodeJsRuntimes = runtimes.filter(runtime => runtime.family === RuntimeFamily.NODEJS); - const latestNodeJsRuntime = RuntimeDeterminer.latestNodeJsRuntime(defaultRuntime, nodeJsRuntimes); + const latestNodeJsRuntime = RuntimeDeterminer.latestNodeJsRuntime(nodeJsRuntimes, defaultRuntime); if (latestNodeJsRuntime !== undefined) { if (latestNodeJsRuntime.isDeprecated) { throw new Error(`Latest nodejs runtime ${latestNodeJsRuntime} is deprecated. You must upgrade to the latest code compatible nodejs runtime`); @@ -40,7 +40,7 @@ export class RuntimeDeterminer { throw new Error('Compatible runtimes must contain only nodejs or python runtimes'); } - private static latestNodeJsRuntime(defaultRuntime: Runtime, nodeJsRuntimes: Runtime[]) { + private static latestNodeJsRuntime(nodeJsRuntimes: Runtime[], defaultRuntime: Runtime) { if (nodeJsRuntimes.length === 0) { return undefined; } @@ -105,4 +105,4 @@ export class RuntimeDeterminer { } private constructor() {} -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/runtime.ts b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/runtime.ts new file mode 100644 index 0000000000000..c6966c734b4fd --- /dev/null +++ b/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/runtime.ts @@ -0,0 +1,79 @@ +export enum RuntimeFamily { + NODEJS, + PYTHON, +} + +/** + * Properties used to initialize a framework runtime. + */ +interface RuntimeProps { + /** + * Whether or not this runtime is deprecated. + * + * @default false + */ + readonly isDeprecated?: boolean; +} + +/** + * Custom resource framework runtimes used for code generation. + */ +export class Runtime { + /** + * The NodeJS 16.x runtime (nodejs16.x) + */ + public static readonly NODEJS_16_X = new Runtime('nodejs16.x', RuntimeFamily.NODEJS); + + /** + * The NodeJS 18.x runtime (nodejs18.x) + */ + public static readonly NODEJS_18_X = new Runtime('nodejs18.x', RuntimeFamily.NODEJS); + + /** + * The NodeJS 20.x runtime (nodejs20.x) + */ + public static readonly NODEJS_20_X = new Runtime('nodejs20.x', RuntimeFamily.NODEJS); + + /** + * The Python 3.9 runtime (python3.9) + */ + public static readonly PYTHON_3_9 = new Runtime('python3.9', RuntimeFamily.PYTHON); + + /** + * The Python 3.10 runtime (python3.10) + */ + public static readonly PYTHON_3_10 = new Runtime('python3.10', RuntimeFamily.PYTHON); + + public readonly name: string; + public readonly family: RuntimeFamily; + public readonly isDeprecated: boolean; + + private constructor(name: string, family: RuntimeFamily, props: RuntimeProps = {}) { + this.name = name; + this.family = family; + this.isDeprecated = props.isDeprecated ?? false; + } + + public runtimeEquals(other: Runtime): boolean { + return other.name === this.name && other.family === this.family; + } + + public toLambdaRuntime() { + switch (this.name) { + case 'nodejs16.x': { + return 'lambda.Runtime.NODEJS_16_X'; + } + case 'nodejs18.x': { + return 'lambda.Runtime.NODEJS_18_X'; + } + case 'python3.9': { + return 'lambda.Runtime.PYTHON_3_9'; + } + case 'python3.10': { + return 'lambda.Runtime.PYTHON_3_10'; + } + } + throw new Error('Unable to convert runtime to lambda runtime'); + } +} + diff --git a/packages/@aws-cdk/custom-resource-handlers/tsconfig.json b/packages/@aws-cdk/custom-resource-handlers/tsconfig.json index 1b180bfa1f956..66153ae4aeb2f 100644 --- a/packages/@aws-cdk/custom-resource-handlers/tsconfig.json +++ b/packages/@aws-cdk/custom-resource-handlers/tsconfig.json @@ -23,6 +23,7 @@ ], "exclude": [ "**/test/**/*.ts", - "dist" + "dist", + "runtime-determiner", ], } diff --git a/packages/aws-cdk-lib/aws-eks/lib/cluster-resource-provider.ts b/packages/aws-cdk-lib/aws-eks/lib/cluster-resource-provider.ts index 55a0caa27a6c0..eab87cae1aa21 100644 --- a/packages/aws-cdk-lib/aws-eks/lib/cluster-resource-provider.ts +++ b/packages/aws-cdk-lib/aws-eks/lib/cluster-resource-provider.ts @@ -1,13 +1,11 @@ -import * as path from 'path'; import { Construct } from 'constructs'; import * as ec2 from '../../aws-ec2'; import * as lambda from '../../aws-lambda'; import { Duration, NestedStack, Stack } from '../../core'; +import { OnEventFunction, IsCompleteFunction } from '../../custom-resource-handlers/dist/aws-eks/cluster-resource-provider.generated'; import * as cr from '../../custom-resources'; import { NodeProxyAgentLayer } from '../../lambda-layer-node-proxy-agent'; -const HANDLER_DIR = path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-eks', 'cluster-resource-handler'); - export interface ClusterResourceProviderProps { /** @@ -66,15 +64,12 @@ export class ClusterResourceProvider extends NestedStack { // The NPM dependency proxy-agent is required in order to support proxy routing with the AWS JS SDK. const nodeProxyAgentLayer = new NodeProxyAgentLayer(this, 'NodeProxyAgentLayer'); - const onEvent = new lambda.Function(this, 'OnEventHandler', { - code: lambda.Code.fromAsset(HANDLER_DIR), + const onEvent = new OnEventFunction(this, 'OnEventHandler', { description: 'onEvent handler for EKS cluster resource provider', - runtime: lambda.Runtime.NODEJS_18_X, environment: { AWS_STS_REGIONAL_ENDPOINTS: 'regional', ...props.environment, }, - handler: 'index.onEvent', timeout: Duration.minutes(1), vpc: props.subnets ? props.vpc : undefined, vpcSubnets: props.subnets ? { subnets: props.subnets } : undefined, @@ -83,15 +78,12 @@ export class ClusterResourceProvider extends NestedStack { layers: props.onEventLayer ? [props.onEventLayer] : [nodeProxyAgentLayer], }); - const isComplete = new lambda.Function(this, 'IsCompleteHandler', { - code: lambda.Code.fromAsset(HANDLER_DIR), + const isComplete = new IsCompleteFunction(this, 'IsCompleteHandler', { description: 'isComplete handler for EKS cluster resource provider', - runtime: lambda.Runtime.NODEJS_18_X, environment: { AWS_STS_REGIONAL_ENDPOINTS: 'regional', ...props.environment, }, - handler: 'index.isComplete', timeout: Duration.minutes(1), vpc: props.subnets ? props.vpc : undefined, vpcSubnets: props.subnets ? { subnets: props.subnets } : undefined, diff --git a/packages/aws-cdk-lib/aws-eks/lib/kubectl-provider.ts b/packages/aws-cdk-lib/aws-eks/lib/kubectl-provider.ts index 3f2d473ce94b9..ceab1298df378 100644 --- a/packages/aws-cdk-lib/aws-eks/lib/kubectl-provider.ts +++ b/packages/aws-cdk-lib/aws-eks/lib/kubectl-provider.ts @@ -1,9 +1,8 @@ -import * as path from 'path'; import { Construct, IConstruct } from 'constructs'; import { ICluster, Cluster } from './cluster'; import * as iam from '../../aws-iam'; -import * as lambda from '../../aws-lambda'; import { Duration, Stack, NestedStack, Names, CfnCondition, Fn, Aws } from '../../core'; +import { KubectlFunction } from '../../custom-resource-handlers/dist/aws-eks/kubectl-provider.generated'; import * as cr from '../../custom-resources'; import { AwsCliLayer } from '../../lambda-layer-awscli'; import { KubectlLayer } from '../../lambda-layer-kubectl'; @@ -133,10 +132,7 @@ export class KubectlProvider extends NestedStack implements IKubectlProvider { const memorySize = cluster.kubectlMemory ? cluster.kubectlMemory.toMebibytes() : 1024; - const handler = new lambda.Function(this, 'Handler', { - code: lambda.Code.fromAsset(path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-eks', 'kubectl-handler')), - runtime: lambda.Runtime.PYTHON_3_10, - handler: 'index.handler', + const handler = new KubectlFunction(this, 'Handler', { timeout: Duration.minutes(15), description: 'onEvent handler for EKS kubectl resource provider', memorySize, diff --git a/packages/aws-cdk-lib/aws-lambda/lib/runtime.ts b/packages/aws-cdk-lib/aws-lambda/lib/runtime.ts index 07d5e00c8e0d0..0013d67ad2079 100644 --- a/packages/aws-cdk-lib/aws-lambda/lib/runtime.ts +++ b/packages/aws-cdk-lib/aws-lambda/lib/runtime.ts @@ -30,12 +30,6 @@ export interface LambdaRuntimeProps { * @default false */ readonly supportsSnapStart?: boolean; - - /** - * Whether this runtime is deprecated. - * @default false - */ - readonly isDeprecated?: boolean; } export enum RuntimeFamily { @@ -62,64 +56,43 @@ export class Runtime { * The NodeJS runtime (nodejs) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS runtime. */ - public static readonly NODEJS = new Runtime('nodejs', RuntimeFamily.NODEJS, { - supportsInlineCode: true, - isDeprecated: true, - }); + public static readonly NODEJS = new Runtime('nodejs', RuntimeFamily.NODEJS, { supportsInlineCode: true }); /** * The NodeJS 4.3 runtime (nodejs4.3) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS runtime. */ - public static readonly NODEJS_4_3 = new Runtime('nodejs4.3', RuntimeFamily.NODEJS, { - supportsInlineCode: true, - isDeprecated: true, - }); + public static readonly NODEJS_4_3 = new Runtime('nodejs4.3', RuntimeFamily.NODEJS, { supportsInlineCode: true }); /** * The NodeJS 6.10 runtime (nodejs6.10) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS runtime. */ - public static readonly NODEJS_6_10 = new Runtime('nodejs6.10', RuntimeFamily.NODEJS, { - supportsInlineCode: true, - isDeprecated: true, - }); + public static readonly NODEJS_6_10 = new Runtime('nodejs6.10', RuntimeFamily.NODEJS, { supportsInlineCode: true }); /** * The NodeJS 8.10 runtime (nodejs8.10) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS runtime. */ - public static readonly NODEJS_8_10 = new Runtime('nodejs8.10', RuntimeFamily.NODEJS, { - supportsInlineCode: true, - isDeprecated: true, - }); + public static readonly NODEJS_8_10 = new Runtime('nodejs8.10', RuntimeFamily.NODEJS, { supportsInlineCode: true }); /** * The NodeJS 10.x runtime (nodejs10.x) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS runtime. */ - public static readonly NODEJS_10_X = new Runtime('nodejs10.x', RuntimeFamily.NODEJS, { - supportsInlineCode: true, - isDeprecated: true, - }); + public static readonly NODEJS_10_X = new Runtime('nodejs10.x', RuntimeFamily.NODEJS, { supportsInlineCode: true }); /** * The NodeJS 12.x runtime (nodejs12.x) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS runtime. */ - public static readonly NODEJS_12_X = new Runtime('nodejs12.x', RuntimeFamily.NODEJS, { - supportsInlineCode: true, - isDeprecated: true, - }); + public static readonly NODEJS_12_X = new Runtime('nodejs12.x', RuntimeFamily.NODEJS, { supportsInlineCode: true }); /** * The NodeJS 14.x runtime (nodejs14.x) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS runtime. */ - public static readonly NODEJS_14_X = new Runtime('nodejs14.x', RuntimeFamily.NODEJS, { - supportsInlineCode: true, - isDeprecated: true, - }); + public static readonly NODEJS_14_X = new Runtime('nodejs14.x', RuntimeFamily.NODEJS, { supportsInlineCode: true }); /** * The NodeJS 16.x runtime (nodejs16.x) @@ -146,10 +119,7 @@ export class Runtime { * The Python 2.7 runtime (python2.7) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest Python runtime. */ - public static readonly PYTHON_2_7 = new Runtime('python2.7', RuntimeFamily.PYTHON, { - supportsInlineCode: true, - isDeprecated: true, - }); + public static readonly PYTHON_2_7 = new Runtime('python2.7', RuntimeFamily.PYTHON, { supportsInlineCode: true }); /** * The Python 3.6 runtime (python3.6) (not recommended) @@ -161,7 +131,6 @@ export class Runtime { public static readonly PYTHON_3_6 = new Runtime('python3.6', RuntimeFamily.PYTHON, { supportsInlineCode: true, supportsCodeGuruProfiling: true, - isDeprecated: true, }); /** @@ -259,49 +228,37 @@ export class Runtime { * The .NET Core 1.0 runtime (dotnetcore1.0) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest .NET Core runtime. */ - public static readonly DOTNET_CORE_1 = new Runtime('dotnetcore1.0', RuntimeFamily.DOTNET_CORE, { - isDeprecated: true, - }); + public static readonly DOTNET_CORE_1 = new Runtime('dotnetcore1.0', RuntimeFamily.DOTNET_CORE); /** * The .NET Core 2.0 runtime (dotnetcore2.0) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest .NET Core runtime. */ - public static readonly DOTNET_CORE_2 = new Runtime('dotnetcore2.0', RuntimeFamily.DOTNET_CORE, { - isDeprecated: true, - }); + public static readonly DOTNET_CORE_2 = new Runtime('dotnetcore2.0', RuntimeFamily.DOTNET_CORE); /** * The .NET Core 2.1 runtime (dotnetcore2.1) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest .NET Core runtime. */ - public static readonly DOTNET_CORE_2_1 = new Runtime('dotnetcore2.1', RuntimeFamily.DOTNET_CORE, { - isDeprecated: true, - }); + public static readonly DOTNET_CORE_2_1 = new Runtime('dotnetcore2.1', RuntimeFamily.DOTNET_CORE); /** * The .NET Core 3.1 runtime (dotnetcore3.1) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest .NET Core runtime. */ - public static readonly DOTNET_CORE_3_1 = new Runtime('dotnetcore3.1', RuntimeFamily.DOTNET_CORE, { - isDeprecated: true, - }); + public static readonly DOTNET_CORE_3_1 = new Runtime('dotnetcore3.1', RuntimeFamily.DOTNET_CORE); /** * The Go 1.x runtime (go1.x) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the PROVIDED_AL2023 runtime. */ - public static readonly GO_1_X = new Runtime('go1.x', RuntimeFamily.GO, { - isDeprecated: true, - }); + public static readonly GO_1_X = new Runtime('go1.x', RuntimeFamily.GO); /** * The Ruby 2.5 runtime (ruby2.5) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest Ruby runtime. */ - public static readonly RUBY_2_5 = new Runtime('ruby2.5', RuntimeFamily.RUBY, { - isDeprecated: true, - }); + public static readonly RUBY_2_5 = new Runtime('ruby2.5', RuntimeFamily.RUBY); /** * The Ruby 2.7 runtime (ruby2.7) @@ -317,9 +274,7 @@ export class Runtime { * The custom provided runtime (provided) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest provided.al2023 runtime. */ - public static readonly PROVIDED = new Runtime('provided', RuntimeFamily.OTHER, { - isDeprecated: true, - }); + public static readonly PROVIDED = new Runtime('provided', RuntimeFamily.OTHER); /** * The custom provided runtime with Amazon Linux 2 (provided.al2) @@ -378,17 +333,11 @@ export class Runtime { */ public readonly isVariable: boolean; - /** - * Whether the runtime is deprecated. - */ - public readonly isDeprecated: boolean; - constructor(name: string, family?: RuntimeFamily, props: LambdaRuntimeProps = {}) { this.name = name; this.supportsInlineCode = !!props.supportsInlineCode; this.family = family; this.isVariable = !!props.isVariable; - this.isDeprecated = props.isDeprecated ?? false; const imageName = props.bundlingDockerImage ?? `public.ecr.aws/sam/build-${name}`; this.bundlingDockerImage = DockerImage.fromRegistry(imageName); diff --git a/packages/aws-cdk-lib/aws-lambda/test/runtime.test.ts b/packages/aws-cdk-lib/aws-lambda/test/runtime.test.ts index 5a6a421a9cb80..f3976e70c4327 100644 --- a/packages/aws-cdk-lib/aws-lambda/test/runtime.test.ts +++ b/packages/aws-cdk-lib/aws-lambda/test/runtime.test.ts @@ -55,26 +55,3 @@ describe('runtime', () => { expect(runtime.bundlingDockerImage.image).toEqual('my-docker-image'); }); }); - -describe('deprecated runtimes', () => { - test.each([ - [lambda.Runtime.PYTHON_2_7], - [lambda.Runtime.PYTHON_3_6], - [lambda.Runtime.NODEJS], - [lambda.Runtime.NODEJS_4_3], - [lambda.Runtime.NODEJS_6_10], - [lambda.Runtime.NODEJS_8_10], - [lambda.Runtime.NODEJS_10_X], - [lambda.Runtime.NODEJS_12_X], - [lambda.Runtime.NODEJS_14_X], - [lambda.Runtime.DOTNET_CORE_1], - [lambda.Runtime.DOTNET_CORE_2], - [lambda.Runtime.DOTNET_CORE_2_1], - [lambda.Runtime.DOTNET_CORE_3_1], - [lambda.Runtime.GO_1_X], - [lambda.Runtime.RUBY_2_5], - [lambda.Runtime.PROVIDED], - ])('%s is deprecated', (runtime) => { - expect(runtime.isDeprecated).toEqual(true); - }); -}); diff --git a/packages/aws-cdk-lib/aws-route53/lib/record-set.ts b/packages/aws-cdk-lib/aws-route53/lib/record-set.ts index f9f34f60e7fcd..ca6830fae5d65 100644 --- a/packages/aws-cdk-lib/aws-route53/lib/record-set.ts +++ b/packages/aws-cdk-lib/aws-route53/lib/record-set.ts @@ -1,4 +1,3 @@ -import * as path from 'path'; import { Construct } from 'constructs'; import { IAliasRecordTarget } from './alias-record-target'; import { GeoLocation } from './geo-location'; @@ -6,7 +5,9 @@ import { IHostedZone } from './hosted-zone-ref'; import { CfnRecordSet } from './route53.generated'; import { determineFullyQualifiedDomainName } from './util'; import * as iam from '../../aws-iam'; -import { CustomResource, CustomResourceProvider, CustomResourceProviderRuntime, Duration, IResource, RemovalPolicy, Resource, Token } from '../../core'; +import { CustomResource, Duration, IResource, RemovalPolicy, Resource, Token } from '../../core'; +import { CrossAccountZoneDelegationProvider } from '../../custom-resource-handlers/dist/aws-route53/cross-account-zone-delegation-provider.generated'; +import { DeleteExistingRecordSetProvider } from '../../custom-resource-handlers/dist/aws-route53/delete-existing-record-set-provider.generated'; const CROSS_ACCOUNT_ZONE_DELEGATION_RESOURCE_TYPE = 'Custom::CrossAccountZoneDelegation'; const DELETE_EXISTING_RECORD_SET_RESOURCE_TYPE = 'Custom::DeleteExistingRecordSet'; @@ -268,16 +269,13 @@ export class RecordSet extends Resource implements IRecordSet { if (props.deleteExisting) { // Delete existing record before creating the new one - const provider = CustomResourceProvider.getOrCreateProvider(this, DELETE_EXISTING_RECORD_SET_RESOURCE_TYPE, { - codeDirectory: path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-route53', 'delete-existing-record-set-handler'), - runtime: CustomResourceProviderRuntime.NODEJS_18_X, + const provider = DeleteExistingRecordSetProvider.getOrCreateProvider(this, DELETE_EXISTING_RECORD_SET_RESOURCE_TYPE, { policyStatements: [{ // IAM permissions for all providers Effect: 'Allow', Action: 'route53:GetChange', Resource: '*', }], }); - // Add to the singleton policy for this specific provider provider.addToRolePolicy({ Effect: 'Allow', @@ -773,10 +771,7 @@ export class CrossAccountZoneDelegationRecord extends Construct { throw Error('Only one of parentHostedZoneName and parentHostedZoneId is supported'); } - const provider = CustomResourceProvider.getOrCreateProvider(this, CROSS_ACCOUNT_ZONE_DELEGATION_RESOURCE_TYPE, { - codeDirectory: path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-route53', 'cross-account-zone-delegation-handler'), - runtime: CustomResourceProviderRuntime.NODEJS_18_X, - }); + const provider = CrossAccountZoneDelegationProvider.getOrCreateProvider(this, CROSS_ACCOUNT_ZONE_DELEGATION_RESOURCE_TYPE, {}); const role = iam.Role.fromRoleArn(this, 'cross-account-zone-delegation-handler-role', provider.roleArn); diff --git a/packages/aws-cdk-lib/custom-resources/lib/aws-custom-resource/aws-custom-resource.ts b/packages/aws-cdk-lib/custom-resources/lib/aws-custom-resource/aws-custom-resource.ts index aae56b70556cc..f4f2cb6d1622a 100644 --- a/packages/aws-cdk-lib/custom-resources/lib/aws-custom-resource/aws-custom-resource.ts +++ b/packages/aws-cdk-lib/custom-resources/lib/aws-custom-resource/aws-custom-resource.ts @@ -1,11 +1,10 @@ -import * as path from 'path'; import { Construct } from 'constructs'; import * as ec2 from '../../../aws-ec2'; import * as iam from '../../../aws-iam'; -import * as lambda from '../../../aws-lambda'; import * as logs from '../../../aws-logs'; import * as cdk from '../../../core'; import { Annotations } from '../../../core'; +import { AwsCustomResourceFunction } from '../../../custom-resource-handlers/dist/custom-resources/aws-custom-resource-provider.generated'; import * as cxapi from '../../../cx-api'; import { awsSdkToIamAction } from '../helpers-internal/sdk-info'; @@ -446,10 +445,7 @@ export class AwsCustomResource extends Construct implements iam.IGrantable { this.props = props; - const provider = new lambda.SingletonFunction(this, 'Provider', { - code: lambda.Code.fromAsset(path.join(__dirname, '..', '..', '..', 'custom-resource-handlers', 'dist', 'custom-resources', 'aws-custom-resource-handler')), - runtime: lambda.Runtime.NODEJS_18_X, - handler: 'index.handler', + const provider = new AwsCustomResourceFunction(this, 'Provider', { uuid: AwsCustomResource.PROVIDER_FUNCTION_UUID, lambdaPurpose: 'AWS', timeout: props.timeout || cdk.Duration.minutes(2), diff --git a/packages/aws-cdk-lib/handler-framework/lib/cdk-handler.ts b/packages/aws-cdk-lib/handler-framework/lib/cdk-handler.ts deleted file mode 100644 index d82edcfe9bde2..0000000000000 --- a/packages/aws-cdk-lib/handler-framework/lib/cdk-handler.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Construct } from 'constructs'; -import { RuntimeDeterminer } from './utils/runtime-determiner'; -import { Code, Runtime } from '../../aws-lambda'; - -/** - * Properties used to initialize `CdkHandler`. - */ -export interface CdkHandlerProps { - /** - * A local file system directory with the provider's code. The code will be - * bundled into a zip asset and wired to the provider's AWS Lambda function. - */ - readonly codeDirectory: string; - - /** - * Runtimes that are compatible with the source code. - */ - readonly compatibleRuntimes: Runtime[]; -} - -/** - * Represents an instance of `CdkHandler`. - */ -export class CdkHandler extends Construct { - /** - * The latest nodejs runtime version available across all AWS regions - */ - private static readonly DEFAULT_RUNTIME = Runtime.NODEJS_LATEST; - - /** - * The local file system directory with the provider's code. - */ - public readonly codeDirectory: string; - - /** - * The source code of your Lambda function. - */ - public readonly code: Code; - - /** - * The latest runtime that is compatible with the source code. - */ - public readonly runtime: Runtime; - - public constructor(scope: Construct, id: string, props: CdkHandlerProps) { - super(scope, id); - this.codeDirectory = props.codeDirectory; - this.code = Code.fromAsset(props.codeDirectory); - this.runtime = RuntimeDeterminer.determineLatestRuntime(CdkHandler.DEFAULT_RUNTIME, props.compatibleRuntimes); - } -} diff --git a/packages/aws-cdk-lib/handler-framework/lib/runtime-determiner.ts b/packages/aws-cdk-lib/handler-framework/lib/runtime-determiner.ts new file mode 100644 index 0000000000000..d865573bc5834 --- /dev/null +++ b/packages/aws-cdk-lib/handler-framework/lib/runtime-determiner.ts @@ -0,0 +1,113 @@ +import { Runtime, RuntimeFamily } from '../../aws-lambda'; + +/** + * A utility class used to determine the latest runtime for a specific runtime family + */ +export class RuntimeDeterminer { + /** + * Determines the latest runtime from a list of runtimes. + * + * Note: runtimes must only be nodejs or python. Nodejs runtimes will be given preference over + * python runtimes. + * + * @param runtimes the list of runtimes to search in + * @returns the latest nodejs or python runtime found, otherwise undefined if no nodejs or python + * runtimes are specified + */ + public static determineLatestRuntime(runtimes: Runtime[]) { + if (runtimes.length === 0) { + throw new Error('You must specify at least one compatible runtime'); + } + + const nodeJsRuntimes = runtimes.filter(runtime => runtime.family === RuntimeFamily.NODEJS); + const latestNodeJsRuntime = RuntimeDeterminer.latestNodeJsRuntime(nodeJsRuntimes); + if (latestNodeJsRuntime !== undefined) { + if (latestNodeJsRuntime.isDeprecated) { + throw new Error(`Latest nodejs runtime ${latestNodeJsRuntime} is deprecated. You must upgrade to the latest code compatible nodejs runtime`); + } + return latestNodeJsRuntime; + } + + const pythonRuntimes = runtimes.filter(runtime => runtime.family === RuntimeFamily.PYTHON); + const latestPythonRuntime = RuntimeDeterminer.latestPythonRuntime(pythonRuntimes); + if (latestPythonRuntime !== undefined) { + if (latestPythonRuntime.isDeprecated) { + throw new Error(`Latest python runtime ${latestPythonRuntime} is deprecated. You must upgrade to the latest code compatible python runtime`); + } + return latestPythonRuntime; + } + + throw new Error('Compatible runtimes must contain only nodejs or python runtimes'); + } + + /** + * The latest nodejs runtime version available across all AWS regions + */ + private static readonly DEFAULT_RUNTIME = Runtime.NODEJS_LATEST; + + private static latestNodeJsRuntime(nodeJsRuntimes: Runtime[]) { + if (nodeJsRuntimes.length === 0) { + return undefined; + } + + if (nodeJsRuntimes.some(runtime => runtime.runtimeEquals(RuntimeDeterminer.DEFAULT_RUNTIME))) { + return RuntimeDeterminer.DEFAULT_RUNTIME; + } + + let latestRuntime = nodeJsRuntimes[0]; + for (let idx = 1; idx < nodeJsRuntimes.length; idx++) { + latestRuntime = RuntimeDeterminer.latestRuntime(latestRuntime, nodeJsRuntimes[idx], RuntimeFamily.NODEJS); + } + + return latestRuntime; + } + + private static latestPythonRuntime(pythonRuntimes: Runtime[]) { + if (pythonRuntimes.length === 0) { + return undefined; + } + + let latestRuntime = pythonRuntimes[0]; + for (let idx = 1; idx < pythonRuntimes.length; idx++) { + latestRuntime = RuntimeDeterminer.latestRuntime(latestRuntime, pythonRuntimes[idx], RuntimeFamily.PYTHON); + } + + return latestRuntime; + } + + private static latestRuntime(runtime1: Runtime, runtime2: Runtime, family: RuntimeFamily) { + let sliceStart: number; + switch (family) { + case RuntimeFamily.NODEJS: { + sliceStart = 'nodejs'.length; + break; + } + case RuntimeFamily.PYTHON: { + sliceStart = 'python'.length; + break; + } + default: { + sliceStart = 0; + break; + } + } + + const version1 = runtime1.name.slice(sliceStart).split('.'); + const version2 = runtime2.name.slice(sliceStart).split('.'); + + const versionLength = Math.min(version1.length, version2.length); + for (let idx = 0; idx < versionLength; idx++) { + if (parseInt(version1[idx]) > parseInt(version2[idx])) { + return runtime1; + } + + if (parseInt(version1[idx]) < parseInt(version2[idx])) { + return runtime2; + } + } + + return runtime1; + } + + private constructor() {} +} \ No newline at end of file diff --git a/packages/aws-cdk-lib/handler-framework/test/cdk-handler.test.ts b/packages/aws-cdk-lib/handler-framework/test/cdk-handler.test.ts deleted file mode 100644 index f3e712056a518..0000000000000 --- a/packages/aws-cdk-lib/handler-framework/test/cdk-handler.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import * as path from 'path'; -import { Runtime } from '../../aws-lambda'; -import { Stack } from '../../core'; -import { CdkHandler } from '../lib/cdk-handler'; - -describe('cdk handler', () => { - let codeDirectory: string; - beforeAll(() => { - codeDirectory = path.join(__dirname, 'test-handler'); - }); - - test('code directory property is correctly set', () => { - // GIVEN - const stack = new Stack(); - - // WHEN - const handler = new CdkHandler(stack, 'CdkHandler', { - codeDirectory, - compatibleRuntimes: [Runtime.NODEJS_16_X, Runtime.NODEJS_LATEST, Runtime.PYTHON_3_12], - }); - - // THEN - expect(handler.codeDirectory).toEqual(codeDirectory); - }); - - test('runtime property is correctly set', () => { - // GIVEN - const stack = new Stack(); - - // WHEN - const handler = new CdkHandler(stack, 'CdkHandler', { - codeDirectory, - compatibleRuntimes: [Runtime.NODEJS_16_X, Runtime.NODEJS_LATEST, Runtime.PYTHON_3_12], - }); - - // THEN - expect(handler.runtime.runtimeEquals(Runtime.NODEJS_LATEST)).toBe(true); - }); -}); diff --git a/packages/aws-cdk-lib/handler-framework/test/utils/runtime-determiner.test.ts b/packages/aws-cdk-lib/handler-framework/test/runtime-determiner.test.ts similarity index 81% rename from packages/aws-cdk-lib/handler-framework/test/utils/runtime-determiner.test.ts rename to packages/aws-cdk-lib/handler-framework/test/runtime-determiner.test.ts index ce8fca15ebd6d..8d00a0e435239 100644 --- a/packages/aws-cdk-lib/handler-framework/test/utils/runtime-determiner.test.ts +++ b/packages/aws-cdk-lib/handler-framework/test/runtime-determiner.test.ts @@ -1,5 +1,5 @@ -import { Runtime } from '../../../aws-lambda'; -import { RuntimeDeterminer } from '../../lib/utils/runtime-determiner'; +import { Runtime } from '../../aws-lambda'; +import { RuntimeDeterminer } from '../lib/runtime-determiner'; const DEFAULT_RUNTIME = Runtime.NODEJS_LATEST; @@ -9,7 +9,7 @@ describe('latest runtime', () => { const runtimes = [Runtime.NODEJS_16_X, Runtime.PYTHON_3_12, Runtime.NODEJS_18_X]; // WHEN - const latestRuntime = RuntimeDeterminer.determineLatestRuntime(DEFAULT_RUNTIME, runtimes); + const latestRuntime = RuntimeDeterminer.determineLatestRuntime(runtimes); // THEN expect(latestRuntime?.runtimeEquals(DEFAULT_RUNTIME)).toEqual(true); @@ -20,7 +20,7 @@ describe('latest runtime', () => { const runtimes = [Runtime.NODEJS_16_X, Runtime.PYTHON_3_12, Runtime.NODEJS_14_X, Runtime.PYTHON_3_11, Runtime.NODEJS_20_X]; // WHEN - const latestRuntime = RuntimeDeterminer.determineLatestRuntime(DEFAULT_RUNTIME, runtimes); + const latestRuntime = RuntimeDeterminer.determineLatestRuntime(runtimes); // THEN expect(latestRuntime?.runtimeEquals(Runtime.NODEJS_20_X)).toEqual(true); @@ -31,7 +31,7 @@ describe('latest runtime', () => { const runtimes = [Runtime.PYTHON_3_10, Runtime.PYTHON_3_11, Runtime.PYTHON_3_7]; // WHEN - const latestRuntime = RuntimeDeterminer.determineLatestRuntime(DEFAULT_RUNTIME, runtimes); + const latestRuntime = RuntimeDeterminer.determineLatestRuntime(runtimes); // THEN expect(latestRuntime?.runtimeEquals(Runtime.PYTHON_3_11)).toEqual(true); @@ -43,7 +43,7 @@ describe('latest runtime', () => { // WHEN / THEN expect(() => { - RuntimeDeterminer.determineLatestRuntime(DEFAULT_RUNTIME, runtimes); + RuntimeDeterminer.determineLatestRuntime(runtimes); }).toThrow('You must specify at least one compatible runtime'); }); @@ -53,7 +53,7 @@ describe('latest runtime', () => { // WHEN / THEN expect(() => { - RuntimeDeterminer.determineLatestRuntime(DEFAULT_RUNTIME, runtimes); + RuntimeDeterminer.determineLatestRuntime(runtimes); }).toThrow(`Latest nodejs runtime ${Runtime.NODEJS_14_X} is deprecated. You must upgrade to the latest code compatible nodejs runtime`); }); @@ -63,7 +63,7 @@ describe('latest runtime', () => { // WHEN / THEN expect(() => { - RuntimeDeterminer.determineLatestRuntime(DEFAULT_RUNTIME, runtimes); + RuntimeDeterminer.determineLatestRuntime(runtimes); }).toThrow(`Latest python runtime ${Runtime.PYTHON_3_6} is deprecated. You must upgrade to the latest code compatible python runtime`); }); @@ -73,7 +73,7 @@ describe('latest runtime', () => { // WHEN / THEN expect(() => { - RuntimeDeterminer.determineLatestRuntime(DEFAULT_RUNTIME, runtimes); + RuntimeDeterminer.determineLatestRuntime(runtimes); }).toThrow('Compatible runtimes must contain only nodejs or python runtimes'); }); }); From ca3be849905d78d0656e7cf38f0dd6a2113f3e0e Mon Sep 17 00:00:00 2001 From: Francis Date: Thu, 7 Dec 2023 17:55:34 -0800 Subject: [PATCH 30/73] renamed to custom resource framework Signed-off-by: Francis --- .../classes.ts | 8 +- .../config.ts | 0 .../constructors.ts | 2 +- .../framework.ts | 6 +- .../modules.ts | 9 -- .../runtime-determiner.ts | 0 .../runtime.ts | 0 .../scripts/generate.ts | 4 +- .../core/lib/private/cfn-utils-provider.ts | 8 +- .../aws-cdk-lib/handler-framework/README.md | 29 ----- .../lib/runtime-determiner.ts | 113 ------------------ .../test/mock-provider/.gitignore | 1 - .../test/mock-provider/index.js | 0 .../test/runtime-determiner.test.ts | 79 ------------ .../test/test-handler/index.ts | 3 - 15 files changed, 10 insertions(+), 252 deletions(-) rename packages/@aws-cdk/custom-resource-handlers/lib/{handler-framework => custom-resource-framework}/classes.ts (97%) rename packages/@aws-cdk/custom-resource-handlers/lib/{handler-framework => custom-resource-framework}/config.ts (100%) rename packages/@aws-cdk/custom-resource-handlers/lib/{handler-framework => custom-resource-framework}/constructors.ts (97%) rename packages/@aws-cdk/custom-resource-handlers/lib/{handler-framework => custom-resource-framework}/framework.ts (92%) rename packages/@aws-cdk/custom-resource-handlers/lib/{handler-framework => custom-resource-framework}/modules.ts (80%) rename packages/@aws-cdk/custom-resource-handlers/lib/{handler-framework => custom-resource-framework}/runtime-determiner.ts (100%) rename packages/@aws-cdk/custom-resource-handlers/lib/{handler-framework => custom-resource-framework}/runtime.ts (100%) delete mode 100644 packages/aws-cdk-lib/handler-framework/README.md delete mode 100644 packages/aws-cdk-lib/handler-framework/lib/runtime-determiner.ts delete mode 100644 packages/aws-cdk-lib/handler-framework/test/mock-provider/.gitignore delete mode 100644 packages/aws-cdk-lib/handler-framework/test/mock-provider/index.js delete mode 100644 packages/aws-cdk-lib/handler-framework/test/runtime-determiner.test.ts delete mode 100644 packages/aws-cdk-lib/handler-framework/test/test-handler/index.ts diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/classes.ts similarity index 97% rename from packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts rename to packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/classes.ts index eaf4b05aeb49f..72aad5cc04249 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/classes.ts @@ -2,7 +2,7 @@ import { ClassType, stmt, expr, Type, ExternalModule, PropertySpec, InterfaceSpec, InterfaceType } from '@cdklabs/typewriter'; import { CdkHandlerFrameworkConstructor } from './constructors'; import { CdkHandlerFrameworkModule } from './framework'; -import { HANDLER_FRAMEWORK_MODULE, CONSTRUCTS_MODULE, LAMBDA_MODULE, CORE_MODULE } from './modules'; +import { CONSTRUCTS_MODULE, LAMBDA_MODULE, CORE_MODULE } from './modules'; import { Runtime } from './runtime'; /** @@ -44,7 +44,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { public readonly runtime: Runtime; public readonly constructorPropsType = LAMBDA_MODULE.FunctionOptions; - protected readonly externalModules = [CONSTRUCTS_MODULE, LAMBDA_MODULE, HANDLER_FRAMEWORK_MODULE]; + protected readonly externalModules = [CONSTRUCTS_MODULE, LAMBDA_MODULE]; public constructor() { super(scope, { @@ -73,7 +73,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { public readonly runtime: Runtime; public readonly constructorPropsType: Type; - protected readonly externalModules = [CONSTRUCTS_MODULE, LAMBDA_MODULE, HANDLER_FRAMEWORK_MODULE]; + protected readonly externalModules = [CONSTRUCTS_MODULE, LAMBDA_MODULE]; public constructor() { super(scope, { @@ -130,7 +130,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { public readonly runtime: Runtime; public readonly constructorPropsType = CORE_MODULE.CustomResourceProviderOptions; - protected readonly externalModules = [CONSTRUCTS_MODULE, CORE_MODULE, HANDLER_FRAMEWORK_MODULE]; + protected readonly externalModules = [CONSTRUCTS_MODULE, CORE_MODULE]; public constructor() { super(scope, { diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/config.ts similarity index 100% rename from packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/config.ts rename to packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/config.ts diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/constructors.ts similarity index 97% rename from packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts rename to packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/constructors.ts index 24fa7d4938960..ff3a01a3f673d 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/constructors.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/constructors.ts @@ -31,7 +31,7 @@ export class CdkHandlerFrameworkConstructor { const superProps = new ObjectLiteral([ new Splat(expr.ident('props')), ['codeDirectory', expr.directCode(`path.join(__dirname, '${_class.codeDirectory}')`)], - ['runtimeName', expr.directCode(_class.runtime.name)], + ['runtimeName', expr.lit(_class.runtime.name)], ]); CdkHandlerFrameworkConstructor.forClass(_class, superProps, MemberVisibility.Private); } diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts similarity index 92% rename from packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts rename to packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts index 8406cdf6a54ff..bb37e6d16ae69 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/framework.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts @@ -3,7 +3,7 @@ import { ExternalModule, InterfaceType, Module, TypeScriptRenderer } from '@cdkl import * as fs from 'fs-extra'; import { CdkHandlerClassProps, CdkHandlerFrameworkClass } from './classes'; import { ComponentType, ConfigProps } from './config'; -import { HANDLER_FRAMEWORK_MODULE, CONSTRUCTS_MODULE, CORE_MODULE, LAMBDA_MODULE, PATH_MODULE } from './modules'; +import { CONSTRUCTS_MODULE, CORE_MODULE, LAMBDA_MODULE, PATH_MODULE } from './modules'; import { Runtime } from './runtime'; import { RuntimeDeterminer } from './runtime-determiner'; @@ -109,10 +109,6 @@ export class CdkHandlerFrameworkModule extends Module { LAMBDA_MODULE.import(this, 'lambda'); break; } - case HANDLER_FRAMEWORK_MODULE.fqn: { - HANDLER_FRAMEWORK_MODULE.importSelective(this, ['RuntimeDeterminer']); - break; - } } } } diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/modules.ts similarity index 80% rename from packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts rename to packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/modules.ts index bef49f96d02e6..a7ed871696d15 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/modules.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/modules.ts @@ -15,14 +15,6 @@ class ConstructsModule extends ExternalModule { } } -class HandlerFrameworkModule extends ExternalModule { - public readonly RuntimeDeterminer = Type.fromName(this, 'RuntimeDeterminer'); - - public constructor() { - super('../../../handler-framework/lib/runtime-determiner'); - } -} - class CoreModule extends ExternalModule { public readonly CustomResourceProviderBase = Type.fromName(this, 'CustomResourceProviderBase'); public readonly CustomResourceProviderOptions = Type.fromName(this, 'CustomResourceProviderOptions'); @@ -44,6 +36,5 @@ class LambdaModule extends ExternalModule { export const PATH_MODULE = new PathModule(); export const CONSTRUCTS_MODULE = new ConstructsModule(); -export const HANDLER_FRAMEWORK_MODULE = new HandlerFrameworkModule(); export const CORE_MODULE = new CoreModule(); export const LAMBDA_MODULE = new LambdaModule(); diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/runtime-determiner.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/runtime-determiner.ts similarity index 100% rename from packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/runtime-determiner.ts rename to packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/runtime-determiner.ts diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/runtime.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/runtime.ts similarity index 100% rename from packages/@aws-cdk/custom-resource-handlers/lib/handler-framework/runtime.ts rename to packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/runtime.ts diff --git a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts index 9773099abdd21..aa7b94c75d51e 100644 --- a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts +++ b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts @@ -1,8 +1,8 @@ import * as fs from 'fs'; import * as path from 'path'; import * as esbuild from 'esbuild'; -import { config, ConfigProps } from '../lib/handler-framework/config'; -import { CdkHandlerFrameworkModule } from '../lib/handler-framework/framework'; +import { config, ConfigProps } from '../lib/custom-resource-framework/config'; +import { CdkHandlerFrameworkModule } from '../lib/custom-resource-framework/framework'; const framework: { [renderLocation: string]: ConfigProps[] } = {}; diff --git a/packages/aws-cdk-lib/core/lib/private/cfn-utils-provider.ts b/packages/aws-cdk-lib/core/lib/private/cfn-utils-provider.ts index 16358347185d6..c41331c2702fe 100644 --- a/packages/aws-cdk-lib/core/lib/private/cfn-utils-provider.ts +++ b/packages/aws-cdk-lib/core/lib/private/cfn-utils-provider.ts @@ -1,18 +1,14 @@ -import * as path from 'path'; import { Construct } from 'constructs'; import { CfnUtilsResourceType } from './cfn-utils-provider/consts'; +import { CdkCfnUtilsProvider } from '../../../custom-resource-handlers/dist/core/cfn-utils-provider.generated'; import { CustomResource } from '../custom-resource'; -import { CustomResourceProvider, CustomResourceProviderRuntime } from '../custom-resource-provider'; /** * A custom resource provider for CFN utilities such as `CfnJson`. */ export class CfnUtilsProvider extends Construct { public static getOrCreate(scope: Construct) { - return CustomResourceProvider.getOrCreate(scope, 'AWSCDKCfnUtilsProvider', { - runtime: CustomResourceProviderRuntime.NODEJS_18_X, - codeDirectory: path.join(__dirname, '..', '..', '..', 'custom-resource-handlers', 'dist', 'core', 'cfn-utils-provider'), - }); + return CdkCfnUtilsProvider.getOrCreate(scope, 'AWSCDKCfnUtilsProvider', {}); } } diff --git a/packages/aws-cdk-lib/handler-framework/README.md b/packages/aws-cdk-lib/handler-framework/README.md deleted file mode 100644 index 6bc0a9d9e83c1..0000000000000 --- a/packages/aws-cdk-lib/handler-framework/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# AWS CDK Vended Handler Framework - -Note: This is framework intended for internal use only. - -The handler framework module is an internal framework used to establish best practices for vending Lambda handlers that are deployed to user accounts. Primarily, this framework includes a centralized definition of the default runtime version which is the latest version of NodeJs available across all AWS Regions. - -In addition to including a default runtime version, this framework forces the user to specify `compatibleRuntimes` for each Lambda handler being used. The framework first checks for the default runtime in the list of `compatibleRuntimes`. If found, the default runtime is used. If not found, the framework will look for the latest defined runtime in the list of `compatibleRuntimes`. If the latest runtime found is marked as deprecated, then the framework will force the build to fail. To continue, the user must specify a non-deprecated runtime version that the handler code is compatible with. - -## CDK Handler - -`CdkHandler` is a class that represents the source code that will be executed within a Lambda `Function` acting as a custom resource provider. Once constructed, this class contains four attributes: -1. `codeDirectory` - the local file system directory with the provider's code. This the code that will be bundled into a zip asset and wired to the provider's AWS Lambda function. -2. `code` - the source code that is loaded from a local disk path -3. `entrypoint` - the name of the method within your `code` that Lambda calls to execute your `Function`. Note that the default entrypoint is 'index.handler' -4. `compatibleRuntimes` - the runtimes that your `code` is compatible with - -Note that `compatibleRuntimes` can be any python or nodejs runtimes, but the nodejs runtime family is preferred. Python runtimes are supported to provide support for legacy handler code that was written using python. - -The following is an example of how to use `CdkHandler`: - -```ts -const stack = new Stack(); - -const handler = new CdkHandler(stack, 'Handler', { - codeDirectory: path.join(__dirname, 'my-handler'), - entrypoint: 'index.onEventHandler', - compatibleRuntimes: [Runtime.NODEJS_16_X, Runtime.NODEJS_18_X], -}); -``` diff --git a/packages/aws-cdk-lib/handler-framework/lib/runtime-determiner.ts b/packages/aws-cdk-lib/handler-framework/lib/runtime-determiner.ts deleted file mode 100644 index d865573bc5834..0000000000000 --- a/packages/aws-cdk-lib/handler-framework/lib/runtime-determiner.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { Runtime, RuntimeFamily } from '../../aws-lambda'; - -/** - * A utility class used to determine the latest runtime for a specific runtime family - */ -export class RuntimeDeterminer { - /** - * Determines the latest runtime from a list of runtimes. - * - * Note: runtimes must only be nodejs or python. Nodejs runtimes will be given preference over - * python runtimes. - * - * @param runtimes the list of runtimes to search in - * @returns the latest nodejs or python runtime found, otherwise undefined if no nodejs or python - * runtimes are specified - */ - public static determineLatestRuntime(runtimes: Runtime[]) { - if (runtimes.length === 0) { - throw new Error('You must specify at least one compatible runtime'); - } - - const nodeJsRuntimes = runtimes.filter(runtime => runtime.family === RuntimeFamily.NODEJS); - const latestNodeJsRuntime = RuntimeDeterminer.latestNodeJsRuntime(nodeJsRuntimes); - if (latestNodeJsRuntime !== undefined) { - if (latestNodeJsRuntime.isDeprecated) { - throw new Error(`Latest nodejs runtime ${latestNodeJsRuntime} is deprecated. You must upgrade to the latest code compatible nodejs runtime`); - } - return latestNodeJsRuntime; - } - - const pythonRuntimes = runtimes.filter(runtime => runtime.family === RuntimeFamily.PYTHON); - const latestPythonRuntime = RuntimeDeterminer.latestPythonRuntime(pythonRuntimes); - if (latestPythonRuntime !== undefined) { - if (latestPythonRuntime.isDeprecated) { - throw new Error(`Latest python runtime ${latestPythonRuntime} is deprecated. You must upgrade to the latest code compatible python runtime`); - } - return latestPythonRuntime; - } - - throw new Error('Compatible runtimes must contain only nodejs or python runtimes'); - } - - /** - * The latest nodejs runtime version available across all AWS regions - */ - private static readonly DEFAULT_RUNTIME = Runtime.NODEJS_LATEST; - - private static latestNodeJsRuntime(nodeJsRuntimes: Runtime[]) { - if (nodeJsRuntimes.length === 0) { - return undefined; - } - - if (nodeJsRuntimes.some(runtime => runtime.runtimeEquals(RuntimeDeterminer.DEFAULT_RUNTIME))) { - return RuntimeDeterminer.DEFAULT_RUNTIME; - } - - let latestRuntime = nodeJsRuntimes[0]; - for (let idx = 1; idx < nodeJsRuntimes.length; idx++) { - latestRuntime = RuntimeDeterminer.latestRuntime(latestRuntime, nodeJsRuntimes[idx], RuntimeFamily.NODEJS); - } - - return latestRuntime; - } - - private static latestPythonRuntime(pythonRuntimes: Runtime[]) { - if (pythonRuntimes.length === 0) { - return undefined; - } - - let latestRuntime = pythonRuntimes[0]; - for (let idx = 1; idx < pythonRuntimes.length; idx++) { - latestRuntime = RuntimeDeterminer.latestRuntime(latestRuntime, pythonRuntimes[idx], RuntimeFamily.PYTHON); - } - - return latestRuntime; - } - - private static latestRuntime(runtime1: Runtime, runtime2: Runtime, family: RuntimeFamily) { - let sliceStart: number; - switch (family) { - case RuntimeFamily.NODEJS: { - sliceStart = 'nodejs'.length; - break; - } - case RuntimeFamily.PYTHON: { - sliceStart = 'python'.length; - break; - } - default: { - sliceStart = 0; - break; - } - } - - const version1 = runtime1.name.slice(sliceStart).split('.'); - const version2 = runtime2.name.slice(sliceStart).split('.'); - - const versionLength = Math.min(version1.length, version2.length); - for (let idx = 0; idx < versionLength; idx++) { - if (parseInt(version1[idx]) > parseInt(version2[idx])) { - return runtime1; - } - - if (parseInt(version1[idx]) < parseInt(version2[idx])) { - return runtime2; - } - } - - return runtime1; - } - - private constructor() {} -} \ No newline at end of file diff --git a/packages/aws-cdk-lib/handler-framework/test/mock-provider/.gitignore b/packages/aws-cdk-lib/handler-framework/test/mock-provider/.gitignore deleted file mode 100644 index 033e6722bb6e0..0000000000000 --- a/packages/aws-cdk-lib/handler-framework/test/mock-provider/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!index.js diff --git a/packages/aws-cdk-lib/handler-framework/test/mock-provider/index.js b/packages/aws-cdk-lib/handler-framework/test/mock-provider/index.js deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/packages/aws-cdk-lib/handler-framework/test/runtime-determiner.test.ts b/packages/aws-cdk-lib/handler-framework/test/runtime-determiner.test.ts deleted file mode 100644 index 8d00a0e435239..0000000000000 --- a/packages/aws-cdk-lib/handler-framework/test/runtime-determiner.test.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { Runtime } from '../../aws-lambda'; -import { RuntimeDeterminer } from '../lib/runtime-determiner'; - -const DEFAULT_RUNTIME = Runtime.NODEJS_LATEST; - -describe('latest runtime', () => { - test('selects default runtime', () => { - // GIVEN - const runtimes = [Runtime.NODEJS_16_X, Runtime.PYTHON_3_12, Runtime.NODEJS_18_X]; - - // WHEN - const latestRuntime = RuntimeDeterminer.determineLatestRuntime(runtimes); - - // THEN - expect(latestRuntime?.runtimeEquals(DEFAULT_RUNTIME)).toEqual(true); - }); - - test('selects latest nodejs runtime', () => { - // GIVEN - const runtimes = [Runtime.NODEJS_16_X, Runtime.PYTHON_3_12, Runtime.NODEJS_14_X, Runtime.PYTHON_3_11, Runtime.NODEJS_20_X]; - - // WHEN - const latestRuntime = RuntimeDeterminer.determineLatestRuntime(runtimes); - - // THEN - expect(latestRuntime?.runtimeEquals(Runtime.NODEJS_20_X)).toEqual(true); - }); - - test('selects latest python runtime', () => { - // GIVEN - const runtimes = [Runtime.PYTHON_3_10, Runtime.PYTHON_3_11, Runtime.PYTHON_3_7]; - - // WHEN - const latestRuntime = RuntimeDeterminer.determineLatestRuntime(runtimes); - - // THEN - expect(latestRuntime?.runtimeEquals(Runtime.PYTHON_3_11)).toEqual(true); - }); - - test('throws if no runtimes are specified', () => { - // GIVEN - const runtimes = []; - - // WHEN / THEN - expect(() => { - RuntimeDeterminer.determineLatestRuntime(runtimes); - }).toThrow('You must specify at least one compatible runtime'); - }); - - test('throws if latest nodejs runtime is deprecated', () => { - // GIVEN - const runtimes = [Runtime.NODEJS_12_X, Runtime.NODEJS_14_X]; - - // WHEN / THEN - expect(() => { - RuntimeDeterminer.determineLatestRuntime(runtimes); - }).toThrow(`Latest nodejs runtime ${Runtime.NODEJS_14_X} is deprecated. You must upgrade to the latest code compatible nodejs runtime`); - }); - - test('throws if latest python runtime is deprecated', () => { - // GIVEN - const runtimes = [Runtime.PYTHON_2_7, Runtime.PYTHON_3_6]; - - // WHEN / THEN - expect(() => { - RuntimeDeterminer.determineLatestRuntime(runtimes); - }).toThrow(`Latest python runtime ${Runtime.PYTHON_3_6} is deprecated. You must upgrade to the latest code compatible python runtime`); - }); - - test('throws if runtimes are neither nodejs nor python', () => { - // GIVEN - const runtimes = [Runtime.JAVA_17, Runtime.RUBY_3_2]; - - // WHEN / THEN - expect(() => { - RuntimeDeterminer.determineLatestRuntime(runtimes); - }).toThrow('Compatible runtimes must contain only nodejs or python runtimes'); - }); -}); diff --git a/packages/aws-cdk-lib/handler-framework/test/test-handler/index.ts b/packages/aws-cdk-lib/handler-framework/test/test-handler/index.ts deleted file mode 100644 index 4c6a8a3937bf8..0000000000000 --- a/packages/aws-cdk-lib/handler-framework/test/test-handler/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export async function handler() { - return { message: 'Hello, world!' }; -} From 721c59f6f1e94f69820856186f59e7e4bea55bc7 Mon Sep 17 00:00:00 2001 From: Francis Date: Thu, 7 Dec 2023 18:21:19 -0800 Subject: [PATCH 31/73] migrate Signed-off-by: Francis --- packages/aws-cdk-lib/aws-iam/lib/oidc-provider.ts | 8 ++------ .../aws-cdk-lib/core/lib/private/cfn-utils-provider.ts | 8 ++++++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/aws-cdk-lib/aws-iam/lib/oidc-provider.ts b/packages/aws-cdk-lib/aws-iam/lib/oidc-provider.ts index c7456b5867463..b8c5494038a24 100644 --- a/packages/aws-cdk-lib/aws-iam/lib/oidc-provider.ts +++ b/packages/aws-cdk-lib/aws-iam/lib/oidc-provider.ts @@ -1,14 +1,12 @@ -import * as path from 'path'; import { Construct } from 'constructs'; import { Arn, CustomResource, - CustomResourceProvider, - CustomResourceProviderRuntime, IResource, Resource, Token, } from '../../core'; +import { OidcProvider } from '../../custom-resource-handlers/dist/aws-iam/oidc-provider.generated'; const RESOURCE_TYPE = 'Custom::AWSCDKOpenIdConnectProvider'; @@ -162,9 +160,7 @@ export class OpenIdConnectProvider extends Resource implements IOpenIdConnectPro } private getOrCreateProvider() { - return CustomResourceProvider.getOrCreateProvider(this, RESOURCE_TYPE, { - codeDirectory: path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-iam', 'oidc-handler'), - runtime: CustomResourceProviderRuntime.NODEJS_18_X, + return OidcProvider.getOrCreateProvider(this, RESOURCE_TYPE, { policyStatements: [ { Effect: 'Allow', diff --git a/packages/aws-cdk-lib/core/lib/private/cfn-utils-provider.ts b/packages/aws-cdk-lib/core/lib/private/cfn-utils-provider.ts index c41331c2702fe..16358347185d6 100644 --- a/packages/aws-cdk-lib/core/lib/private/cfn-utils-provider.ts +++ b/packages/aws-cdk-lib/core/lib/private/cfn-utils-provider.ts @@ -1,14 +1,18 @@ +import * as path from 'path'; import { Construct } from 'constructs'; import { CfnUtilsResourceType } from './cfn-utils-provider/consts'; -import { CdkCfnUtilsProvider } from '../../../custom-resource-handlers/dist/core/cfn-utils-provider.generated'; import { CustomResource } from '../custom-resource'; +import { CustomResourceProvider, CustomResourceProviderRuntime } from '../custom-resource-provider'; /** * A custom resource provider for CFN utilities such as `CfnJson`. */ export class CfnUtilsProvider extends Construct { public static getOrCreate(scope: Construct) { - return CdkCfnUtilsProvider.getOrCreate(scope, 'AWSCDKCfnUtilsProvider', {}); + return CustomResourceProvider.getOrCreate(scope, 'AWSCDKCfnUtilsProvider', { + runtime: CustomResourceProviderRuntime.NODEJS_18_X, + codeDirectory: path.join(__dirname, '..', '..', '..', 'custom-resource-handlers', 'dist', 'core', 'cfn-utils-provider'), + }); } } From f2504ae8749742d90f36743de86511de6b6996ce Mon Sep 17 00:00:00 2001 From: Francis Date: Thu, 7 Dec 2023 19:41:17 -0800 Subject: [PATCH 32/73] refactor Signed-off-by: Francis --- .../lib/custom-resource-framework/classes.ts | 153 ++++++++++++------ .../custom-resource-framework/constructors.ts | 59 ------- 2 files changed, 102 insertions(+), 110 deletions(-) delete mode 100644 packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/constructors.ts diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/classes.ts index 72aad5cc04249..71ad5d98da55d 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/classes.ts @@ -1,10 +1,52 @@ /* eslint-disable import/no-extraneous-dependencies */ -import { ClassType, stmt, expr, Type, ExternalModule, PropertySpec, InterfaceSpec, InterfaceType } from '@cdklabs/typewriter'; -import { CdkHandlerFrameworkConstructor } from './constructors'; +import { + ClassType, + stmt, + expr, + Type, + Splat, + ExternalModule, + PropertySpec, + InterfaceSpec, + InterfaceType, + ObjectLiteral, + MemberVisibility, + SuperInitializer, + Expression, +} from '@cdklabs/typewriter'; import { CdkHandlerFrameworkModule } from './framework'; import { CONSTRUCTS_MODULE, LAMBDA_MODULE, CORE_MODULE } from './modules'; import { Runtime } from './runtime'; +/** + * Initialization properties for a class constructor. + */ +interface ConstructorBuildProps { + /** + * The props type used to create an instance of this class. + */ + readonly constructorPropsType: Type; + + /** + * Properties to pass up to the parent class. + */ + readonly superProps: ObjectLiteral; + + /** + * Whether the class constructor props are optional. + * + * @default false + */ + readonly optionalConstructorProps?: boolean; + + /** + * Visbility for the constructor. + * + * @default MemberVisbility.Public + */ + readonly constructorVisbility?: MemberVisibility; +} + /** * Initialization properties used to build a `CdkHandlerFrameworkClass` instance. */ @@ -39,11 +81,6 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { */ public static buildCdkFunction(scope: CdkHandlerFrameworkModule, props: CdkHandlerClassProps): CdkHandlerFrameworkClass { return new (class CdkFunction extends CdkHandlerFrameworkClass { - public readonly codeDirectory: string; - public readonly entrypoint: string; - public readonly runtime: Runtime; - public readonly constructorPropsType = LAMBDA_MODULE.FunctionOptions; - protected readonly externalModules = [CONSTRUCTS_MODULE, LAMBDA_MODULE]; public constructor() { @@ -52,13 +89,21 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { extends: LAMBDA_MODULE.Function, export: true, }); - this.codeDirectory = props.codeDirectory; - this.entrypoint = props.entrypoint ?? 'index.handler'; - this.runtime = props.runtime; this.externalModules.forEach(module => scope.addExternalModule(module)); - CdkHandlerFrameworkConstructor.forCdkFunction(this); + const superProps = new ObjectLiteral([ + new Splat(expr.ident('props')), + ['code', expr.directCode(`lambda.Code.fromAsset(path.join(__dirname, '${props.codeDirectory}'))`)], + ['handler', expr.lit(props.entrypoint ?? 'index.handler')], + ['runtime', expr.directCode(props.runtime.toLambdaRuntime())], + ]); + this.buildConstructor({ + constructorPropsType: LAMBDA_MODULE.FunctionOptions, + superProps, + optionalConstructorProps: true, + constructorVisbility: MemberVisibility.Public, + }); } })(); } @@ -68,11 +113,6 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { */ public static buildCdkSingletonFunction(scope: CdkHandlerFrameworkModule, props: CdkHandlerClassProps): CdkHandlerFrameworkClass { return new (class CdkSingletonFunction extends CdkHandlerFrameworkClass { - public readonly codeDirectory: string; - public readonly entrypoint: string; - public readonly runtime: Runtime; - public readonly constructorPropsType: Type; - protected readonly externalModules = [CONSTRUCTS_MODULE, LAMBDA_MODULE]; public constructor() { @@ -81,9 +121,6 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { extends: LAMBDA_MODULE.SingletonFunction, export: true, }); - this.codeDirectory = props.codeDirectory; - this.entrypoint = props.entrypoint ?? 'index.handler'; - this.runtime = props.runtime; this.externalModules.forEach(module => scope.addExternalModule(module)); @@ -113,9 +150,18 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { extends: [LAMBDA_MODULE.FunctionOptions], properties: [uuid, lambdaPurpose], }); - this.constructorPropsType = _interface.type; - CdkHandlerFrameworkConstructor.forCdkFunction(this); + const superProps = new ObjectLiteral([ + new Splat(expr.ident('props')), + ['code', expr.directCode(`lambda.Code.fromAsset(path.join(__dirname, '${props.codeDirectory}'))`)], + ['handler', expr.lit(props.entrypoint ?? 'index.handler')], + ['runtime', expr.directCode(props.runtime.toLambdaRuntime())], + ]); + this.buildConstructor({ + constructorPropsType: _interface.type, + superProps, + constructorVisbility: MemberVisibility.Public, + }); } })(); } @@ -125,11 +171,6 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { */ public static buildCdkCustomResourceProvider(scope: CdkHandlerFrameworkModule, props: CdkHandlerClassProps): CdkHandlerFrameworkClass { return new (class CdkCustomResourceProvider extends CdkHandlerFrameworkClass { - public readonly codeDirectory: string; - public readonly entrypoint: string; - public readonly runtime: Runtime; - public readonly constructorPropsType = CORE_MODULE.CustomResourceProviderOptions; - protected readonly externalModules = [CONSTRUCTS_MODULE, CORE_MODULE]; public constructor() { @@ -138,9 +179,6 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { extends: CORE_MODULE.CustomResourceProviderBase, export: true, }); - this.codeDirectory = props.codeDirectory; - this.entrypoint = props.entrypoint ?? 'index.handler'; - this.runtime = props.runtime; this.externalModules.forEach(module => scope.addExternalModule(module)); @@ -163,6 +201,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { getOrCreateMethod.addParameter({ name: 'props', type: CORE_MODULE.CustomResourceProviderOptions, + optional: true, }); getOrCreateMethod.addBody( stmt.ret(expr.directCode('this.getOrCreateProvider(scope, uniqueid, props).serviceToken')), @@ -187,6 +226,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { getOrCreateProviderMethod.addParameter({ name: 'props', type: CORE_MODULE.CustomResourceProviderOptions, + optional: true, }); getOrCreateProviderMethod.addBody( stmt.constVar(expr.ident('id'), expr.directCode('`${uniqueid}CustomResourceProvider`')), @@ -195,32 +235,21 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { stmt.ret(expr.directCode(`existing ?? new ${this.name}(stack, id, props)`)), ); - CdkHandlerFrameworkConstructor.forCdkCustomResourceProvider(this); + const superProps = new ObjectLiteral([ + new Splat(expr.ident('props')), + ['codeDirectory', expr.directCode(`path.join(__dirname, '${props.codeDirectory}')`)], + ['runtimeName', expr.lit(props.runtime.name)], + ]); + this.buildConstructor({ + constructorPropsType: CORE_MODULE.CustomResourceProviderOptions, + superProps, + constructorVisbility: MemberVisibility.Private, + optionalConstructorProps: true, + }); } })(); } - /** - * A local file system directory with the provider's code. The code will be - * bundled into a zip asset and wired to the provider's AWS Lambda function. - */ - public abstract readonly codeDirectory: string; - - /** - * The name of the method within your code that Lambda calls to execute your function. - */ - public abstract readonly entrypoint: string; - - /** - * The runtime environment for the Lambda function. - */ - public abstract readonly runtime: Runtime; - - /** - * Properties used to initialize this class. - */ - public abstract readonly constructorPropsType: Type; - /** * External modules that this class depends on. */ @@ -236,4 +265,26 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { scope.registerInterface(_interface); return _interface; } + + private buildConstructor(props: ConstructorBuildProps) { + const init = this.addInitializer({ + visibility: props.constructorVisbility, + }); + const scope = init.addParameter({ + name: 'scope', + type: CONSTRUCTS_MODULE.Construct, + }); + const id = init.addParameter({ + name: 'id', + type: Type.STRING, + }); + init.addParameter({ + name: 'props', + type: props.constructorPropsType, + optional: props.optionalConstructorProps, + }); + + const superInitializerArgs: Expression[] = [scope, id, props.superProps]; + init.addBody(new SuperInitializer(...superInitializerArgs)); + } } diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/constructors.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/constructors.ts deleted file mode 100644 index ff3a01a3f673d..0000000000000 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/constructors.ts +++ /dev/null @@ -1,59 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ -import { Expression, Type, expr, SuperInitializer, ObjectLiteral, Splat, MemberVisibility } from '@cdklabs/typewriter'; -import { CdkHandlerFrameworkClass } from './classes'; -import { CONSTRUCTS_MODULE } from './modules'; - -export class CdkHandlerFrameworkConstructor { - /** - * Builds a constructor for a CdkFunction class. - */ - public static forCdkFunction(_class: CdkHandlerFrameworkClass) { - const superProps = new ObjectLiteral([ - new Splat(expr.ident('props')), - ['code', expr.directCode(`lambda.Code.fromAsset(path.join(__dirname, '${_class.codeDirectory}'))`)], - ['handler', expr.lit(_class.entrypoint)], - ['runtime', expr.directCode(_class.runtime.toLambdaRuntime())], - ]); - CdkHandlerFrameworkConstructor.forClass(_class, superProps, MemberVisibility.Public); - } - - /** - * Builds a constructor for a CdkSingletonFunction class. - */ - public static forCdkSingletonFunction(_class: CdkHandlerFrameworkClass) { - CdkHandlerFrameworkConstructor.forCdkFunction(_class); - } - - /** - * Builds a constructor for a CdkCustomResourceProvider class. - */ - public static forCdkCustomResourceProvider(_class: CdkHandlerFrameworkClass) { - const superProps = new ObjectLiteral([ - new Splat(expr.ident('props')), - ['codeDirectory', expr.directCode(`path.join(__dirname, '${_class.codeDirectory}')`)], - ['runtimeName', expr.lit(_class.runtime.name)], - ]); - CdkHandlerFrameworkConstructor.forClass(_class, superProps, MemberVisibility.Private); - } - - private static forClass(_class: CdkHandlerFrameworkClass, superProps: Expression, visibility: MemberVisibility) { - const init = _class.addInitializer({ visibility }); - const scope = init.addParameter({ - name: 'scope', - type: CONSTRUCTS_MODULE.Construct, - }); - const id = init.addParameter({ - name: 'id', - type: Type.STRING, - }); - init.addParameter({ - name: 'props', - type: _class.constructorPropsType, - }); - - const superInitializerArgs: Expression[] = [scope, id, superProps]; - init.addBody(new SuperInitializer(...superInitializerArgs)); - } - - private constructor() {} -} From 98cbe116b4c6ae65b9966c6af9070eb9296c44c0 Mon Sep 17 00:00:00 2001 From: Francis Date: Thu, 7 Dec 2023 19:41:31 -0800 Subject: [PATCH 33/73] migrate Signed-off-by: Francis --- packages/aws-cdk-lib/aws-ecr/lib/repository.ts | 8 ++------ packages/aws-cdk-lib/aws-s3/lib/bucket.ts | 8 ++------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/packages/aws-cdk-lib/aws-ecr/lib/repository.ts b/packages/aws-cdk-lib/aws-ecr/lib/repository.ts index ef4fa18d809c9..008753dbd567d 100644 --- a/packages/aws-cdk-lib/aws-ecr/lib/repository.ts +++ b/packages/aws-cdk-lib/aws-ecr/lib/repository.ts @@ -1,5 +1,4 @@ import { EOL } from 'os'; -import * as path from 'path'; import { IConstruct, Construct } from 'constructs'; import { CfnRepository } from './ecr.generated'; import { LifecycleRule, TagStatus } from './lifecycle'; @@ -18,10 +17,9 @@ import { Token, TokenComparison, CustomResource, - CustomResourceProvider, - CustomResourceProviderRuntime, Aws, } from '../../core'; +import { AutoDeleteImagesProvider } from '../../custom-resource-handlers/dist/aws-ecr/auto-delete-images-provider.generated'; const AUTO_DELETE_IMAGES_RESOURCE_TYPE = 'Custom::ECRAutoDeleteImages'; const AUTO_DELETE_IMAGES_TAG = 'aws-cdk:auto-delete-images'; @@ -863,10 +861,8 @@ export class Repository extends RepositoryBase { private enableAutoDeleteImages() { const firstTime = Stack.of(this).node.tryFindChild(`${AUTO_DELETE_IMAGES_RESOURCE_TYPE}CustomResourceProvider`) === undefined; - const provider = CustomResourceProvider.getOrCreateProvider(this, AUTO_DELETE_IMAGES_RESOURCE_TYPE, { - codeDirectory: path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-ecr', 'auto-delete-images-handler'), + const provider = AutoDeleteImagesProvider.getOrCreateProvider(this, AUTO_DELETE_IMAGES_RESOURCE_TYPE, { useCfnResponseWrapper: false, - runtime: CustomResourceProviderRuntime.NODEJS_18_X, description: `Lambda function for auto-deleting images in ${this.repositoryName} repository.`, }); diff --git a/packages/aws-cdk-lib/aws-s3/lib/bucket.ts b/packages/aws-cdk-lib/aws-s3/lib/bucket.ts index cb14f945fe69b..79e3fabddcbe8 100644 --- a/packages/aws-cdk-lib/aws-s3/lib/bucket.ts +++ b/packages/aws-cdk-lib/aws-s3/lib/bucket.ts @@ -1,5 +1,4 @@ import { EOL } from 'os'; -import * as path from 'path'; import { Construct } from 'constructs'; import { BucketPolicy } from './bucket-policy'; import { IBucketNotificationDestination } from './destination'; @@ -13,7 +12,6 @@ import * as iam from '../../aws-iam'; import * as kms from '../../aws-kms'; import { CustomResource, - CustomResourceProvider, Duration, FeatureFlags, Fn, @@ -27,9 +25,9 @@ import { Token, Tokenization, Annotations, - CustomResourceProviderRuntime, } from '../../core'; import { CfnReference } from '../../core/lib/private/cfn-reference'; +import { AutoDeleteObjectsProvider } from '../../custom-resource-handlers/dist/aws-s3/auto-delete-objects-provider.generated'; import * as cxapi from '../../cx-api'; import * as regionInformation from '../../region-info'; @@ -2466,10 +2464,8 @@ export class Bucket extends BucketBase { } private enableAutoDeleteObjects() { - const provider = CustomResourceProvider.getOrCreateProvider(this, AUTO_DELETE_OBJECTS_RESOURCE_TYPE, { - codeDirectory: path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-s3', 'auto-delete-objects-handler'), + const provider = AutoDeleteObjectsProvider.getOrCreateProvider(this, AUTO_DELETE_OBJECTS_RESOURCE_TYPE, { useCfnResponseWrapper: false, - runtime: CustomResourceProviderRuntime.NODEJS_18_X, description: `Lambda function for auto-deleting objects in ${this.bucketName} S3 bucket.`, }); From aa3c17628b226c4468ef44a87f69a50b9270bd75 Mon Sep 17 00:00:00 2001 From: Francis Date: Thu, 7 Dec 2023 20:55:10 -0800 Subject: [PATCH 34/73] migrate to codegen providers Signed-off-by: Francis --- .../lib/custom-resource-framework/config.ts | 61 ++++++++++++++++++- .../aws-cdk-lib/aws-route53/lib/record-set.ts | 2 +- .../lib/bucket-deployment.ts | 7 +-- .../aws-cdk-lib/aws-ses/lib/receipt-rule.ts | 8 +-- .../lib/emrcontainers/start-job-run.ts | 8 +-- .../lib/evaluate-expression.ts | 9 +-- .../aws-cdk-lib/aws-synthetics/lib/canary.ts | 6 +- .../lib/private/application-security-check.ts | 7 +-- packages/aws-cdk-lib/triggers/lib/trigger.ts | 9 +-- 9 files changed, 76 insertions(+), 41 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/config.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/config.ts index 7eb420fd90546..ee80be44cd8f4 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/config.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/config.ts @@ -254,13 +254,52 @@ export const config: HandlerFrameworkConfig = { 'bucket-deployment-provider': [ { type: ComponentType.CDK_SINGLETON_FUNCTION, - name: 'BucketDeploymentProvider', + name: 'BucketDeploymentFunction', sourceCode: path.resolve(__dirname, '..', 'aws-s3-deployment', 'bucket-deployment-handler', 'index.py'), compatibleRuntimes: [Runtime.PYTHON_3_9], disableBundleAndMinify: true, }, ], }, + 'aws-ses': { + 'drop-spam-provider': [ + { + type: ComponentType.CDK_SINGLETON_FUNCTION, + name: 'DropSpamFunction', + sourceCode: path.resolve(__dirname, '..', 'aws-ses', 'drop-spam-handler', 'index.ts'), + compatibleRuntimes: [Runtime.NODEJS_18_X], + }, + ], + }, + 'aws-stepfunctions-tasks': { + 'eval-nodejs-provider': [ + { + type: ComponentType.CDK_SINGLETON_FUNCTION, + name: 'EvalNodeJsFunction', + sourceCode: path.resolve(__dirname, '..', 'aws-stepfunctions-tasks', 'eval-nodejs-handler', 'index.ts'), + compatibleRuntimes: [Runtime.NODEJS_18_X], + }, + ], + 'role-policy-provider': [ + { + type: ComponentType.CDK_SINGLETON_FUNCTION, + name: 'RolePolicyFunction', + sourceCode: path.resolve(__dirname, '..', 'aws-stepfunctions-tasks', 'role-policy-handler', 'index.py'), + compatibleRuntimes: [Runtime.PYTHON_3_9], + disableBundleAndMinify: true, + }, + ], + }, + 'aws-synthetics': { + 'auto-delete-underlying-resources-provider': [ + { + type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + name: 'AutoDeleteUnderlyingResourcesProvider', + sourceCode: path.resolve(__dirname, '..', 'aws-synthetics', 'auto-delete-underlying-resources-handler', 'index.ts'), + compatibleRuntimes: [Runtime.NODEJS_18_X], + }, + ], + }, 'core': { 'cfn-utils-provider': [ { @@ -290,4 +329,24 @@ export const config: HandlerFrameworkConfig = { }, ], }, + 'pipelines': { + 'approve-lambda': [ + { + type: ComponentType.CDK_FUNCTION, + name: 'ApproveLambdaFunction', + sourceCode: path.resolve(__dirname, '..', 'pipelines', 'approve-lambda', 'index.ts'), + compatibleRuntimes: [Runtime.NODEJS_18_X], + }, + ], + }, + 'triggers': { + 'trigger-provider': [ + { + type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + name: 'TriggerProvider', + sourceCode: path.resolve(__dirname, '..', 'triggers', 'lambda', 'index.ts'), + compatibleRuntimes: [Runtime.NODEJS_18_X], + }, + ], + }, }; diff --git a/packages/aws-cdk-lib/aws-route53/lib/record-set.ts b/packages/aws-cdk-lib/aws-route53/lib/record-set.ts index ca6830fae5d65..15b49c7e36cd5 100644 --- a/packages/aws-cdk-lib/aws-route53/lib/record-set.ts +++ b/packages/aws-cdk-lib/aws-route53/lib/record-set.ts @@ -771,7 +771,7 @@ export class CrossAccountZoneDelegationRecord extends Construct { throw Error('Only one of parentHostedZoneName and parentHostedZoneId is supported'); } - const provider = CrossAccountZoneDelegationProvider.getOrCreateProvider(this, CROSS_ACCOUNT_ZONE_DELEGATION_RESOURCE_TYPE, {}); + const provider = CrossAccountZoneDelegationProvider.getOrCreateProvider(this, CROSS_ACCOUNT_ZONE_DELEGATION_RESOURCE_TYPE); const role = iam.Role.fromRoleArn(this, 'cross-account-zone-delegation-handler-role', provider.roleArn); diff --git a/packages/aws-cdk-lib/aws-s3-deployment/lib/bucket-deployment.ts b/packages/aws-cdk-lib/aws-s3-deployment/lib/bucket-deployment.ts index e9b3fa59039c4..d5ba1f195e15e 100644 --- a/packages/aws-cdk-lib/aws-s3-deployment/lib/bucket-deployment.ts +++ b/packages/aws-cdk-lib/aws-s3-deployment/lib/bucket-deployment.ts @@ -1,6 +1,5 @@ import * as fs from 'fs'; -import * as path from 'path'; import { kebab as toKebabCase } from 'case'; import { Construct } from 'constructs'; import { ISource, SourceConfig, Source } from './source'; @@ -12,6 +11,7 @@ import * as lambda from '../../aws-lambda'; import * as logs from '../../aws-logs'; import * as s3 from '../../aws-s3'; import * as cdk from '../../core'; +import { BucketDeploymentFunction } from '../../custom-resource-handlers/dist/aws-s3-deployment/bucket-deployment-provider.generated'; import { AwsCliLayer } from '../../lambda-layer-awscli'; // tag key has a limit of 128 characters @@ -316,18 +316,15 @@ export class BucketDeployment extends Construct { } const mountPath = `/mnt${accessPointPath}`; - const handler = new lambda.SingletonFunction(this, 'CustomResourceHandler', { + const handler = new BucketDeploymentFunction(this, 'CustomResourceHandler', { uuid: this.renderSingletonUuid(props.memoryLimit, props.ephemeralStorageSize, props.vpc), - code: lambda.Code.fromAsset(path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-s3-deployment', 'bucket-deployment-handler')), layers: [new AwsCliLayer(this, 'AwsCliLayer')], - runtime: lambda.Runtime.PYTHON_3_9, environment: { ...props.useEfs ? { MOUNT_PATH: mountPath } : undefined, // Override the built-in CA bundle from the AWS CLI with the Lambda-curated one // This is necessary to make the CLI work in ADC regions. AWS_CA_BUNDLE: '/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem', }, - handler: 'index.handler', lambdaPurpose: 'Custom::CDKBucketDeployment', timeout: cdk.Duration.minutes(15), role: props.role, diff --git a/packages/aws-cdk-lib/aws-ses/lib/receipt-rule.ts b/packages/aws-cdk-lib/aws-ses/lib/receipt-rule.ts index f0bf13f0008d1..2ae395cf0da63 100644 --- a/packages/aws-cdk-lib/aws-ses/lib/receipt-rule.ts +++ b/packages/aws-cdk-lib/aws-ses/lib/receipt-rule.ts @@ -1,11 +1,10 @@ -import * as path from 'path'; import { Construct } from 'constructs'; import { IReceiptRuleAction } from './receipt-rule-action'; import { IReceiptRuleSet } from './receipt-rule-set'; import { CfnReceiptRule } from './ses.generated'; import * as iam from '../../aws-iam'; -import * as lambda from '../../aws-lambda'; import { Aws, IResource, Lazy, Resource } from '../../core'; +import { DropSpamFunction } from '../../custom-resource-handlers/dist/aws-ses/drop-spam-provider.generated'; /** * A receipt rule. @@ -171,10 +170,7 @@ export class DropSpamReceiptRule extends Construct { constructor(scope: Construct, id: string, props: DropSpamReceiptRuleProps) { super(scope, id); - const fn = new lambda.SingletonFunction(this, 'Function', { - runtime: lambda.Runtime.NODEJS_18_X, - handler: 'index.handler', - code: lambda.Code.fromAsset(path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-ses', 'drop-spam-handler')), + const fn = new DropSpamFunction(this, 'Function', { uuid: '224e77f9-a32e-4b4d-ac32-983477abba16', }); diff --git a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/emrcontainers/start-job-run.ts b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/emrcontainers/start-job-run.ts index da544e8fd9e0e..71bfd53b388df 100644 --- a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/emrcontainers/start-job-run.ts +++ b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/emrcontainers/start-job-run.ts @@ -1,12 +1,11 @@ -import * as path from 'path'; import { Construct } from 'constructs'; import * as iam from '../../../aws-iam'; -import * as lambda from '../../../aws-lambda'; import * as logs from '../../../aws-logs'; import * as s3 from '../../../aws-s3'; import * as sfn from '../../../aws-stepfunctions'; import { TaskInput } from '../../../aws-stepfunctions'; import * as cdk from '../../../core'; +import { RolePolicyFunction } from '../../../custom-resource-handlers/dist/aws-stepfunctions-tasks/role-policy-provider.generated'; import * as cr from '../../../custom-resources'; import * as awscli from '../../../lambda-layer-awscli'; import { integrationResourceArn, validatePatternSupported } from '../private/task-utils'; @@ -350,11 +349,8 @@ export class EmrContainersStartJobRun extends sfn.TaskStateBase implements iam.I * Commands available through CLI: https://awscli.amazonaws.com/v2/documentation/api/latest/reference/emr-containers/index.html */ const cliLayer = new awscli.AwsCliLayer(this, 'awsclilayer'); - const shellCliLambda = new lambda.SingletonFunction(this, 'Call Update-Role-Trust-Policy', { + const shellCliLambda = new RolePolicyFunction(this, 'Call Update-Role-Trust-Policy', { uuid: '8693BB64-9689-44B6-9AAF-B0CC9EB8757C', - runtime: lambda.Runtime.PYTHON_3_9, - handler: 'index.handler', - code: lambda.Code.fromAsset(path.join(__dirname, '..', '..', '..', 'custom-resource-handlers', 'dist', 'aws-stepfunctions-tasks', 'role-policy-handler')), timeout: cdk.Duration.seconds(30), memorySize: 256, layers: [cliLayer], diff --git a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/evaluate-expression.ts b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/evaluate-expression.ts index ea3d55d711454..eef863729fea6 100644 --- a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/evaluate-expression.ts +++ b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/evaluate-expression.ts @@ -1,8 +1,8 @@ -import * as path from 'path'; import { Construct } from 'constructs'; import * as iam from '../../aws-iam'; import * as lambda from '../../aws-lambda'; import * as sfn from '../../aws-stepfunctions'; +import { EvalNodeJsFunction } from '../../custom-resource-handlers/dist/aws-stepfunctions-tasks/eval-nodejs-provider.generated'; /** * Properties for EvaluateExpression @@ -116,13 +116,8 @@ function createEvalFn(runtime: lambda.Runtime | undefined, scope: Construct) { throw new Error(`The runtime ${runtime?.name} is currently not supported.`); } - return new lambda.SingletonFunction(scope, 'EvalFunction', { - runtime: runtime ?? lambda.Runtime.NODEJS_18_X, + return new EvalNodeJsFunction(scope, 'EvalFunction', { uuid, - handler: 'index.handler', lambdaPurpose, - code: lambda.Code.fromAsset(path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-stepfunctions-tasks', 'eval-nodejs-handler'), { - exclude: ['*.ts'], - }), }); } diff --git a/packages/aws-cdk-lib/aws-synthetics/lib/canary.ts b/packages/aws-cdk-lib/aws-synthetics/lib/canary.ts index dd33e7b6f0002..2d8317782806b 100644 --- a/packages/aws-cdk-lib/aws-synthetics/lib/canary.ts +++ b/packages/aws-cdk-lib/aws-synthetics/lib/canary.ts @@ -1,5 +1,4 @@ import * as crypto from 'crypto'; -import * as path from 'path'; import { Construct } from 'constructs'; import { Code } from './code'; import { Runtime } from './runtime'; @@ -11,6 +10,7 @@ import * as ec2 from '../../aws-ec2'; import * as iam from '../../aws-iam'; import * as s3 from '../../aws-s3'; import * as cdk from '../../core'; +import { AutoDeleteUnderlyingResourcesProvider } from '../../custom-resource-handlers/dist/aws-synthetics/auto-delete-underlying-resources-provider.generated'; const AUTO_DELETE_UNDERLYING_RESOURCES_RESOURCE_TYPE = 'Custom::SyntheticsAutoDeleteUnderlyingResources'; const AUTO_DELETE_UNDERLYING_RESOURCES_TAG = 'aws-cdk:auto-delete-underlying-resources'; @@ -321,10 +321,8 @@ export class Canary extends cdk.Resource implements ec2.IConnectable { } private cleanupUnderlyingResources() { - const provider = cdk.CustomResourceProvider.getOrCreateProvider(this, AUTO_DELETE_UNDERLYING_RESOURCES_RESOURCE_TYPE, { - codeDirectory: path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-synthetics', 'auto-delete-underlying-resources-handler'), + const provider = AutoDeleteUnderlyingResourcesProvider.getOrCreateProvider(this, AUTO_DELETE_UNDERLYING_RESOURCES_RESOURCE_TYPE, { useCfnResponseWrapper: false, - runtime: cdk.CustomResourceProviderRuntime.NODEJS_18_X, description: `Lambda function for auto-deleting underlying resources created by ${this.canaryName}.`, policyStatements: [{ Effect: 'Allow', diff --git a/packages/aws-cdk-lib/pipelines/lib/private/application-security-check.ts b/packages/aws-cdk-lib/pipelines/lib/private/application-security-check.ts index 0308652adf868..b625862d9eea2 100644 --- a/packages/aws-cdk-lib/pipelines/lib/private/application-security-check.ts +++ b/packages/aws-cdk-lib/pipelines/lib/private/application-security-check.ts @@ -1,4 +1,3 @@ -import * as path from 'path'; import { Construct } from 'constructs'; import { CDKP_DEFAULT_CODEBUILD_IMAGE } from './default-codebuild-image'; import * as codebuild from '../../../aws-codebuild'; @@ -6,6 +5,7 @@ import * as cp from '../../../aws-codepipeline'; import * as iam from '../../../aws-iam'; import * as lambda from '../../../aws-lambda'; import { Duration, Tags } from '../../../core'; +import { ApproveLambdaFunction } from '../../../custom-resource-handlers/dist/pipelines/approve-lambda.generated'; /** * Properties for an ApplicationSecurityCheck @@ -57,10 +57,7 @@ export class ApplicationSecurityCheck extends Construct { includeResourceTypes: ['AWS::CodePipeline::Pipeline'], }); - this.preApproveLambda = new lambda.Function(this, 'CDKPipelinesAutoApprove', { - handler: 'index.handler', - runtime: lambda.Runtime.NODEJS_18_X, - code: lambda.Code.fromAsset(path.resolve(__dirname, '..', '..', '..', 'custom-resource-handlers', 'dist', 'pipelines', 'approve-lambda')), + this.preApproveLambda = new ApproveLambdaFunction(this, 'CDKPipelinesAutoApprove', { timeout: Duration.minutes(5), }); diff --git a/packages/aws-cdk-lib/triggers/lib/trigger.ts b/packages/aws-cdk-lib/triggers/lib/trigger.ts index 3bc03ef35ad07..d3b75e12559e1 100644 --- a/packages/aws-cdk-lib/triggers/lib/trigger.ts +++ b/packages/aws-cdk-lib/triggers/lib/trigger.ts @@ -1,7 +1,7 @@ -import { join } from 'path'; import { Construct, IConstruct, Node } from 'constructs'; import * as lambda from '../../aws-lambda'; -import { CustomResource, CustomResourceProvider, CustomResourceProviderRuntime, Duration } from '../../core'; +import { CustomResource, Duration } from '../../core'; +import { TriggerProvider } from '../../custom-resource-handlers/dist/triggers/trigger-provider.generated'; /** * Interface for triggers. @@ -114,10 +114,7 @@ export class Trigger extends Construct implements ITrigger { constructor(scope: Construct, id: string, props: TriggerProps) { super(scope, id); - const provider = CustomResourceProvider.getOrCreateProvider(this, 'AWSCDK.TriggerCustomResourceProvider', { - runtime: CustomResourceProviderRuntime.NODEJS_18_X, - codeDirectory: join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'triggers', 'lambda'), - }); + const provider = TriggerProvider.getOrCreateProvider(this, 'AWSCDK.TriggerCustomResourceProvider'); provider.addToRolePolicy({ Effect: 'Allow', From c89f470431734cfc737cab2b9ec07248a87ab14c Mon Sep 17 00:00:00 2001 From: Francis Date: Fri, 8 Dec 2023 00:26:37 -0800 Subject: [PATCH 35/73] airlift core to core Signed-off-by: Francis --- .../lib/custom-resource-framework/framework.ts | 4 ++-- .../custom-resource-handlers/scripts/generate.ts | 12 ++++++------ .../scripts/airlift-custom-resource-handlers.sh | 12 ++++++++++-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts index bb37e6d16ae69..481c05017be17 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts @@ -59,10 +59,10 @@ export class CdkHandlerFrameworkModule extends Module { /** * Render built framework into an output file. */ - public render(outputFileLocation: string) { + public render() { if (this.hasComponents) { this.importExternalModules(); - fs.outputFileSync(`dist/${outputFileLocation}.generated.ts`, this.renderer.render(this)); + fs.outputFileSync(`dist/${this.fqn}.generated.ts`, this.renderer.render(this)); } } diff --git a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts index aa7b94c75d51e..57740c0fa2b00 100644 --- a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts +++ b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts @@ -4,12 +4,12 @@ import * as esbuild from 'esbuild'; import { config, ConfigProps } from '../lib/custom-resource-framework/config'; import { CdkHandlerFrameworkModule } from '../lib/custom-resource-framework/framework'; -const framework: { [renderLocation: string]: ConfigProps[] } = {}; +const framework: { [fqn: string]: ConfigProps[] } = {}; async function main() { recurse(config, []); - for (const [renderLocation, components] of Object.entries(framework)) { - const module = new CdkHandlerFrameworkModule('cdk-handler-framework'); + for (const [fqn, components] of Object.entries(framework)) { + const module = new CdkHandlerFrameworkModule(fqn); for (const component of components) { const outfile = calculateOutfile(component.sourceCode); if (component.disableBundleAndMinify) { @@ -21,15 +21,15 @@ async function main() { const sourceCodeDirectory = path.dirname(outfile).split('/').pop(); module.build(component, `${sourceCodeDirectory}`); } - module.render(renderLocation); + module.render(); } function recurse(_config: any, _path: string[]) { // base case - this is a framework component array and we will build a module with // all defined components if (_config instanceof Array) { - const outputFileLocation = _path.join('/'); - framework[outputFileLocation] = _config; + const fqn = _path.join('/'); + framework[fqn] = _config; return; } diff --git a/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh b/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh index f92b47de5ffff..e4786e5119cbe 100755 --- a/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh +++ b/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh @@ -5,8 +5,16 @@ awscdklibdir=${scriptdir}/.. customresourcedir=$(node -p "path.dirname(require.resolve('@aws-cdk/custom-resource-handlers/package.json'))") function airlift() { - mkdir -p $awscdklibdir/custom-resource-handlers/$1 - cp $customresourcedir/$2 $awscdklibdir/custom-resource-handlers/$1 + echo $1 + # core needs to be airlifted directly to core to prevent circular dependencies + if [[ $1 = dist/core/* || $1 = dist/core ]]; + then + mkdir -p $awscdklibdir/core/$1 + cp $customresourcedir/$2 $awscdklibdir/core/$1 + else + mkdir -p $awscdklibdir/custom-resource-handlers/$1 + cp $customresourcedir/$2 $awscdklibdir/custom-resource-handlers/$1 + fi } function recurse() { From b0e9ba46ecf92a874e27b824bb0db56054f17e20 Mon Sep 17 00:00:00 2001 From: Francis Date: Fri, 8 Dec 2023 00:54:00 -0800 Subject: [PATCH 36/73] core internal Signed-off-by: Francis --- .../lib/custom-resource-framework/classes.ts | 33 +++++++++++++++---- .../custom-resource-framework/framework.ts | 16 ++++++++- .../lib/custom-resource-framework/modules.ts | 26 +++++++++++++++ .../scripts/generate.ts | 4 +-- .../airlift-custom-resource-handlers.sh | 1 - 5 files changed, 70 insertions(+), 10 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/classes.ts index 71ad5d98da55d..4ea6fb4c9d89d 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/classes.ts @@ -15,7 +15,14 @@ import { Expression, } from '@cdklabs/typewriter'; import { CdkHandlerFrameworkModule } from './framework'; -import { CONSTRUCTS_MODULE, LAMBDA_MODULE, CORE_MODULE } from './modules'; +import { + CONSTRUCTS_MODULE, + LAMBDA_MODULE, + CORE_MODULE, + STACK, + CUSTOM_RESOURCE_PROVIDER_BASE, + CUSTOM_RESOURCE_PROVIDER_OPTIONS, +} from './modules'; import { Runtime } from './runtime'; /** @@ -171,15 +178,23 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { */ public static buildCdkCustomResourceProvider(scope: CdkHandlerFrameworkModule, props: CdkHandlerClassProps): CdkHandlerFrameworkClass { return new (class CdkCustomResourceProvider extends CdkHandlerFrameworkClass { - protected readonly externalModules = [CONSTRUCTS_MODULE, CORE_MODULE]; + protected readonly externalModules: ExternalModule[] = [CONSTRUCTS_MODULE]; public constructor() { super(scope, { name: props.name, - extends: CORE_MODULE.CustomResourceProviderBase, + extends: scope.coreInternal + ? CUSTOM_RESOURCE_PROVIDER_BASE.CustomResourceProviderBase + : CORE_MODULE.CustomResourceProviderBase, export: true, }); + if (scope.coreInternal) { + this.externalModules.push(...[STACK, CUSTOM_RESOURCE_PROVIDER_BASE, CUSTOM_RESOURCE_PROVIDER_OPTIONS]); + } else { + this.externalModules.push(CORE_MODULE); + } + this.externalModules.forEach(module => scope.addExternalModule(module)); const getOrCreateMethod = this.addMethod({ @@ -200,7 +215,9 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { }); getOrCreateMethod.addParameter({ name: 'props', - type: CORE_MODULE.CustomResourceProviderOptions, + type: scope.coreInternal + ? CUSTOM_RESOURCE_PROVIDER_OPTIONS.CustomResourceProviderOptions + : CORE_MODULE.CustomResourceProviderOptions, optional: true, }); getOrCreateMethod.addBody( @@ -225,7 +242,9 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { }); getOrCreateProviderMethod.addParameter({ name: 'props', - type: CORE_MODULE.CustomResourceProviderOptions, + type: scope.coreInternal + ? CUSTOM_RESOURCE_PROVIDER_OPTIONS.CustomResourceProviderOptions + : CORE_MODULE.CustomResourceProviderOptions, optional: true, }); getOrCreateProviderMethod.addBody( @@ -241,7 +260,9 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { ['runtimeName', expr.lit(props.runtime.name)], ]); this.buildConstructor({ - constructorPropsType: CORE_MODULE.CustomResourceProviderOptions, + constructorPropsType: scope.coreInternal + ? CUSTOM_RESOURCE_PROVIDER_OPTIONS.CustomResourceProviderOptions + : CORE_MODULE.CustomResourceProviderOptions, superProps, constructorVisbility: MemberVisibility.Private, optionalConstructorProps: true, diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts index 481c05017be17..4cd7dbf3e5193 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts @@ -3,7 +3,7 @@ import { ExternalModule, InterfaceType, Module, TypeScriptRenderer } from '@cdkl import * as fs from 'fs-extra'; import { CdkHandlerClassProps, CdkHandlerFrameworkClass } from './classes'; import { ComponentType, ConfigProps } from './config'; -import { CONSTRUCTS_MODULE, CORE_MODULE, LAMBDA_MODULE, PATH_MODULE } from './modules'; +import { CONSTRUCTS_MODULE, CORE_MODULE, CUSTOM_RESOURCE_PROVIDER_BASE, CUSTOM_RESOURCE_PROVIDER_OPTIONS, LAMBDA_MODULE, PATH_MODULE, STACK } from './modules'; import { Runtime } from './runtime'; import { RuntimeDeterminer } from './runtime-determiner'; @@ -16,10 +16,12 @@ export class CdkHandlerFrameworkModule extends Module { private readonly renderer = new TypeScriptRenderer(); private readonly externalModules = new Map(); private readonly _interfaces = new Map(); + public readonly coreInternal: boolean; private hasComponents = false; public constructor(fqn: string) { super(fqn); + this.coreInternal = fqn.includes('core'); } /** @@ -105,6 +107,18 @@ export class CdkHandlerFrameworkModule extends Module { ]); break; } + case STACK.fqn: { + STACK.importSelective(this, ['Stack']); + break; + } + case CUSTOM_RESOURCE_PROVIDER_BASE.fqn: { + CUSTOM_RESOURCE_PROVIDER_BASE.importSelective(this, ['CustomResourceProviderBase']); + break; + } + case CUSTOM_RESOURCE_PROVIDER_OPTIONS.fqn: { + CUSTOM_RESOURCE_PROVIDER_OPTIONS.importSelective(this, ['CustomResourceProviderOptions']); + break; + } case LAMBDA_MODULE.fqn: { LAMBDA_MODULE.import(this, 'lambda'); break; diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/modules.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/modules.ts index a7ed871696d15..f53bbc4883da4 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/modules.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/modules.ts @@ -24,6 +24,28 @@ class CoreModule extends ExternalModule { } } +class Stack extends ExternalModule { + public constructor() { + super('../../lib/stack'); + } +} + +class CustomResourceProviderBase extends ExternalModule { + public readonly CustomResourceProviderBase = Type.fromName(this, 'CustomResourceProviderBase'); + + public constructor() { + super('../../lib/custom-resource-provider/custom-resource-provider-base'); + } +} + +class CustomResourceProviderOptions extends ExternalModule { + public readonly CustomResourceProviderOptions = Type.fromName(this, 'CustomResourceProviderOptions'); + + public constructor() { + super('../../lib/custom-resource-provider/shared'); + } +} + class LambdaModule extends ExternalModule { public readonly Function = Type.fromName(this, 'Function'); public readonly SingletonFunction = Type.fromName(this, 'SingletonFunction'); @@ -38,3 +60,7 @@ export const PATH_MODULE = new PathModule(); export const CONSTRUCTS_MODULE = new ConstructsModule(); export const CORE_MODULE = new CoreModule(); export const LAMBDA_MODULE = new LambdaModule(); + +export const STACK = new Stack(); +export const CUSTOM_RESOURCE_PROVIDER_BASE = new CustomResourceProviderBase(); +export const CUSTOM_RESOURCE_PROVIDER_OPTIONS = new CustomResourceProviderOptions(); diff --git a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts index 57740c0fa2b00..b7ac3e9ee6d20 100644 --- a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts +++ b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts @@ -16,7 +16,7 @@ async function main() { fs.mkdirSync(path.dirname(outfile), { recursive: true }); fs.copyFileSync(component.sourceCode, outfile); } else { - await bundleAndMinify(component.sourceCode, outfile); + await minifyAndBundle(component.sourceCode, outfile); } const sourceCodeDirectory = path.dirname(outfile).split('/').pop(); module.build(component, `${sourceCodeDirectory}`); @@ -43,7 +43,7 @@ async function main() { } } -async function bundleAndMinify(infile: string, outfile: string) { +async function minifyAndBundle(infile: string, outfile: string) { const result = await esbuild.build({ entryPoints: [infile], outfile, diff --git a/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh b/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh index e4786e5119cbe..63a6ae5091272 100755 --- a/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh +++ b/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh @@ -5,7 +5,6 @@ awscdklibdir=${scriptdir}/.. customresourcedir=$(node -p "path.dirname(require.resolve('@aws-cdk/custom-resource-handlers/package.json'))") function airlift() { - echo $1 # core needs to be airlifted directly to core to prevent circular dependencies if [[ $1 = dist/core/* || $1 = dist/core ]]; then From d6d5cbfa0db5eba1f98435c1c8dac79c3af67aa5 Mon Sep 17 00:00:00 2001 From: Francis Date: Fri, 8 Dec 2023 09:08:06 -0800 Subject: [PATCH 37/73] finished migrating core providers Signed-off-by: Francis --- .../lib/custom-resource-framework/config.ts | 48 +++++++++++++++---- .../scripts/generate.ts | 2 +- .../export-reader-provider.ts | 7 +-- .../export-writer-provider.ts | 16 +++---- .../core/lib/private/cfn-utils-provider.ts | 8 +--- .../airlift-custom-resource-handlers.sh | 2 +- 6 files changed, 50 insertions(+), 33 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/config.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/config.ts index ee80be44cd8f4..ad5c1d137b6c6 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/config.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/config.ts @@ -58,9 +58,12 @@ export interface ConfigProps { readonly entrypoint?: string; /** + * Prevents the source code from being minified and bundled. This is needed for python + * files or for source code with a `require` import. + * * @default false */ - readonly disableBundleAndMinify?: boolean; + readonly preventMinifyAndBundle?: boolean; } type HandlerFrameworkConfig = { [module: string]: { [identifier: string]: ConfigProps[] } }; @@ -133,7 +136,7 @@ export const config: HandlerFrameworkConfig = { sourceCode: path.resolve(__dirname, '..', 'aws-ecs', 'lambda-source', 'index.py'), compatibleRuntimes: [Runtime.PYTHON_3_9], entrypoint: 'index.lambda_handler', - disableBundleAndMinify: true, + preventMinifyAndBundle: true, }, ], }, @@ -160,35 +163,35 @@ export const config: HandlerFrameworkConfig = { name: 'KubectlFunction', sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'index.py'), compatibleRuntimes: [Runtime.PYTHON_3_10], - disableBundleAndMinify: true, + preventMinifyAndBundle: true, }, { type: ComponentType.CDK_NO_OP, name: 'ApplyHandler', sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'apply', '__init__.py'), compatibleRuntimes: [Runtime.PYTHON_3_10], - disableBundleAndMinify: true, + preventMinifyAndBundle: true, }, { type: ComponentType.CDK_NO_OP, name: 'GetHandler', sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'get', '__init__.py'), compatibleRuntimes: [Runtime.PYTHON_3_10], - disableBundleAndMinify: true, + preventMinifyAndBundle: true, }, { type: ComponentType.CDK_NO_OP, name: 'HelmHandler', sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'helm', '__init__.py'), compatibleRuntimes: [Runtime.PYTHON_3_10], - disableBundleAndMinify: true, + preventMinifyAndBundle: true, }, { type: ComponentType.CDK_NO_OP, name: 'PatchHandler', sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'patch', '__init__.py'), compatibleRuntimes: [Runtime.PYTHON_3_10], - disableBundleAndMinify: true, + preventMinifyAndBundle: true, }, ], }, @@ -249,6 +252,15 @@ export const config: HandlerFrameworkConfig = { compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], + 'notifications-resource-handler': [ + { + type: ComponentType.CDK_NO_OP, + name: 'NotificationsResourceHandler', + sourceCode: path.resolve(__dirname, '..', 'aws-s3', 'notifications-resource-handler', 'index.py'), + compatibleRuntimes: [Runtime.PYTHON_3_9], + preventMinifyAndBundle: true, + }, + ], }, 'aws-s3-deployment': { 'bucket-deployment-provider': [ @@ -257,7 +269,7 @@ export const config: HandlerFrameworkConfig = { name: 'BucketDeploymentFunction', sourceCode: path.resolve(__dirname, '..', 'aws-s3-deployment', 'bucket-deployment-handler', 'index.py'), compatibleRuntimes: [Runtime.PYTHON_3_9], - disableBundleAndMinify: true, + preventMinifyAndBundle: true, }, ], }, @@ -286,7 +298,7 @@ export const config: HandlerFrameworkConfig = { name: 'RolePolicyFunction', sourceCode: path.resolve(__dirname, '..', 'aws-stepfunctions-tasks', 'role-policy-handler', 'index.py'), compatibleRuntimes: [Runtime.PYTHON_3_9], - disableBundleAndMinify: true, + preventMinifyAndBundle: true, }, ], }, @@ -309,13 +321,29 @@ export const config: HandlerFrameworkConfig = { compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], + 'cross-region-ssm-writer-provider': [ + { + type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + name: 'CrossRegionWriterProvider', + sourceCode: path.resolve(__dirname, '..', 'core', 'cross-region-ssm-writer-handler', 'index.ts'), + compatibleRuntimes: [Runtime.NODEJS_18_X], + }, + ], + 'cross-region-ssm-reader-provider': [ + { + type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + name: 'CrossRegionReaderProvider', + sourceCode: path.resolve(__dirname, '..', 'core', 'cross-region-ssm-reader-handler', 'index.ts'), + compatibleRuntimes: [Runtime.NODEJS_18_X], + }, + ], 'nodejs-entrypoint': [ { type: ComponentType.CDK_NO_OP, name: 'NodejsEntrypointHandler', sourceCode: path.resolve(__dirname, '..', 'core', 'nodejs-entrypoint-handler', 'index.js'), compatibleRuntimes: [Runtime.NODEJS_18_X], - disableBundleAndMinify: true, + preventMinifyAndBundle: true, }, ], }, diff --git a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts index b7ac3e9ee6d20..f3a80606a84d5 100644 --- a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts +++ b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts @@ -12,7 +12,7 @@ async function main() { const module = new CdkHandlerFrameworkModule(fqn); for (const component of components) { const outfile = calculateOutfile(component.sourceCode); - if (component.disableBundleAndMinify) { + if (component.preventMinifyAndBundle) { fs.mkdirSync(path.dirname(outfile), { recursive: true }); fs.copyFileSync(component.sourceCode, outfile); } else { diff --git a/packages/aws-cdk-lib/core/lib/custom-resource-provider/cross-region-export-providers/export-reader-provider.ts b/packages/aws-cdk-lib/core/lib/custom-resource-provider/cross-region-export-providers/export-reader-provider.ts index 2d42125c928db..4962e48fb2756 100644 --- a/packages/aws-cdk-lib/core/lib/custom-resource-provider/cross-region-export-providers/export-reader-provider.ts +++ b/packages/aws-cdk-lib/core/lib/custom-resource-provider/cross-region-export-providers/export-reader-provider.ts @@ -1,12 +1,11 @@ -import * as path from 'path'; import { Construct } from 'constructs'; import { SSM_EXPORT_PATH_PREFIX, ExportReaderCRProps, CrossRegionExports } from './types'; +import { CrossRegionReaderProvider } from '../../../dist/core/cross-region-ssm-reader-provider.generated'; import { CfnResource } from '../../cfn-resource'; import { CustomResource } from '../../custom-resource'; import { Lazy } from '../../lazy'; import { Intrinsic } from '../../private/intrinsic'; import { Stack } from '../../stack'; -import { CustomResourceProvider, CustomResourceProviderRuntime } from '../custom-resource-provider'; /** * Properties for an ExportReader @@ -35,9 +34,7 @@ export class ExportReader extends Construct { const stack = Stack.of(this); const resourceType = 'Custom::CrossRegionExportReader'; - const serviceToken = CustomResourceProvider.getOrCreate(this, resourceType, { - codeDirectory: path.join(__dirname, '..', '..', '..', '..', 'custom-resource-handlers', 'dist', 'core', 'cross-region-ssm-reader-handler'), - runtime: CustomResourceProviderRuntime.NODEJS_18_X, + const serviceToken = CrossRegionReaderProvider.getOrCreate(this, resourceType, { policyStatements: [{ Effect: 'Allow', Resource: stack.formatArn({ diff --git a/packages/aws-cdk-lib/core/lib/custom-resource-provider/cross-region-export-providers/export-writer-provider.ts b/packages/aws-cdk-lib/core/lib/custom-resource-provider/cross-region-export-providers/export-writer-provider.ts index 0a55c1aeb8607..05f7bbe76abfe 100644 --- a/packages/aws-cdk-lib/core/lib/custom-resource-provider/cross-region-export-providers/export-writer-provider.ts +++ b/packages/aws-cdk-lib/core/lib/custom-resource-provider/cross-region-export-providers/export-writer-provider.ts @@ -1,7 +1,7 @@ -import * as path from 'path'; import { Construct } from 'constructs'; import { ExportReader } from './export-reader-provider'; import { CrossRegionExports, SSM_EXPORT_PATH_PREFIX, ExportWriterCRProps } from './types'; +import { CrossRegionWriterProvider } from '../../../dist/core/cross-region-ssm-writer-provider.generated'; import { CfnDynamicReference, CfnDynamicReferenceService } from '../../cfn-dynamic-reference'; import { CustomResource } from '../../custom-resource'; import { Lazy } from '../../lazy'; @@ -10,7 +10,7 @@ import { makeUniqueId } from '../../private/uniqueid'; import { Reference } from '../../reference'; import { Stack } from '../../stack'; import { Token } from '../../token'; -import { CustomResourceProviderRuntime, CustomResourceProvider, CustomResourceProviderProps } from '../custom-resource-provider'; +import { CustomResourceProviderOptions } from '../shared'; /** * Properties for an ExportWriter @@ -28,18 +28,17 @@ export interface ExportWriterProps { * Create our own CustomResourceProvider so that we can add a single policy * with a list of ARNs instead of having to create a separate policy statement per ARN. */ -class CRProvider extends CustomResourceProvider { - public static getOrCreateProvider(scope: Construct, uniqueid: string, props: CustomResourceProviderProps): CRProvider { +class CRProvider extends CrossRegionWriterProvider { + public static getOrCreateProvider(scope: Construct, uniqueid: string, props?: CustomResourceProviderOptions): CRProvider { const id = `${uniqueid}CustomResourceProvider`; const stack = Stack.of(scope); const provider = stack.node.tryFindChild(id) as CRProvider ?? new CRProvider(stack, id, props); - return provider; } private readonly resourceArns = new Set(); - constructor(scope: Construct, id: string, props: CustomResourceProviderProps) { + constructor(scope: Construct, id: string, props?: CustomResourceProviderOptions) { super(scope, id, props); this.addToRolePolicy({ Effect: 'Allow', @@ -99,10 +98,7 @@ export class ExportWriter extends Construct { const region = props.region ?? stack.region; const resourceType = 'Custom::CrossRegionExportWriter'; - this.provider = CRProvider.getOrCreateProvider(this, resourceType, { - codeDirectory: path.join(__dirname, '..', '..', '..', '..', 'custom-resource-handlers', 'dist', 'core', 'cross-region-ssm-writer-handler'), - runtime: CustomResourceProviderRuntime.NODEJS_18_X, - }); + this.provider = CRProvider.getOrCreateProvider(this, resourceType); this.addRegionToPolicy(region); diff --git a/packages/aws-cdk-lib/core/lib/private/cfn-utils-provider.ts b/packages/aws-cdk-lib/core/lib/private/cfn-utils-provider.ts index 16358347185d6..dd785612ac2d6 100644 --- a/packages/aws-cdk-lib/core/lib/private/cfn-utils-provider.ts +++ b/packages/aws-cdk-lib/core/lib/private/cfn-utils-provider.ts @@ -1,18 +1,14 @@ -import * as path from 'path'; import { Construct } from 'constructs'; import { CfnUtilsResourceType } from './cfn-utils-provider/consts'; +import { CdkCfnUtilsProvider } from '../../dist/core/cfn-utils-provider.generated'; import { CustomResource } from '../custom-resource'; -import { CustomResourceProvider, CustomResourceProviderRuntime } from '../custom-resource-provider'; /** * A custom resource provider for CFN utilities such as `CfnJson`. */ export class CfnUtilsProvider extends Construct { public static getOrCreate(scope: Construct) { - return CustomResourceProvider.getOrCreate(scope, 'AWSCDKCfnUtilsProvider', { - runtime: CustomResourceProviderRuntime.NODEJS_18_X, - codeDirectory: path.join(__dirname, '..', '..', '..', 'custom-resource-handlers', 'dist', 'core', 'cfn-utils-provider'), - }); + return CdkCfnUtilsProvider.getOrCreate(scope, 'AWSCDKCfnUtilsProvider'); } } diff --git a/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh b/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh index 63a6ae5091272..898ca81973dcb 100755 --- a/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh +++ b/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh @@ -6,7 +6,7 @@ customresourcedir=$(node -p "path.dirname(require.resolve('@aws-cdk/custom-resou function airlift() { # core needs to be airlifted directly to core to prevent circular dependencies - if [[ $1 = dist/core/* || $1 = dist/core ]]; + if [[ ($1 = dist/core/* || $1 = dist/core) && $1 != dist/core/nodejs-entrypoint-handler ]]; then mkdir -p $awscdklibdir/core/$1 cp $customresourcedir/$2 $awscdklibdir/core/$1 From ca0ad0ae030497b872324888f6d392b4b9b6828d Mon Sep 17 00:00:00 2001 From: Francis Date: Fri, 8 Dec 2023 10:08:43 -0800 Subject: [PATCH 38/73] reverted drain hook change Signed-off-by: Francis --- .../lib/custom-resource-framework/framework.ts | 6 +++++- .../aws-ecs/lib/drain-hook/instance-drain-hook.ts | 9 +++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts index 4cd7dbf3e5193..04dab7f885a67 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts @@ -16,9 +16,13 @@ export class CdkHandlerFrameworkModule extends Module { private readonly renderer = new TypeScriptRenderer(); private readonly externalModules = new Map(); private readonly _interfaces = new Map(); - public readonly coreInternal: boolean; private hasComponents = false; + /** + * Whether the module being generated will live inside of aws-cdk-lib/core + */ + public readonly coreInternal: boolean; + public constructor(fqn: string) { super(fqn); this.coreInternal = fqn.includes('core'); diff --git a/packages/aws-cdk-lib/aws-ecs/lib/drain-hook/instance-drain-hook.ts b/packages/aws-cdk-lib/aws-ecs/lib/drain-hook/instance-drain-hook.ts index 598ff6befdc8c..b3217011ebea7 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/drain-hook/instance-drain-hook.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/drain-hook/instance-drain-hook.ts @@ -1,10 +1,12 @@ +import * as fs from 'fs'; +import * as path from 'path'; import { Construct } from 'constructs'; import * as autoscaling from '../../../aws-autoscaling'; import * as hooks from '../../../aws-autoscaling-hooktargets'; import * as iam from '../../../aws-iam'; import * as kms from '../../../aws-kms'; +import * as lambda from '../../../aws-lambda'; import * as cdk from '../../../core'; -import { DrainHookFunction } from '../../../custom-resource-handlers/dist/aws-ecs/drain-hook-provider.generated'; import { ICluster } from '../cluster'; // Reference for the source in this package: @@ -58,7 +60,10 @@ export class InstanceDrainHook extends Construct { const drainTime = props.drainTime || cdk.Duration.minutes(5); // Invoke Lambda via SNS Topic - const fn = new DrainHookFunction(this, 'Function', { + const fn = new lambda.Function(this, 'Function', { + code: lambda.Code.inline(fs.readFileSync(path.join(__dirname, 'lambda-source', 'index.py'), { encoding: 'utf-8' })), + handler: 'index.lambda_handler', + runtime: lambda.Runtime.PYTHON_3_6, // Timeout: some extra margin for additional API calls made by the Lambda, // up to a maximum of 15 minutes. timeout: cdk.Duration.seconds(Math.min(drainTime.toSeconds() + 10, 900)), From 2ca1f3314a671e3b03e62e17f983264cd9c07495 Mon Sep 17 00:00:00 2001 From: Francis Date: Fri, 8 Dec 2023 11:25:09 -0800 Subject: [PATCH 39/73] use lambda runtime and auto generate component name Signed-off-by: Francis --- .../lib/custom-resource-framework/classes.ts | 24 +++++- .../lib/custom-resource-framework/config.ts | 3 +- .../custom-resource-framework/framework.ts | 17 +++- .../lib/custom-resource-framework/runtime.ts | 79 ------------------- .../{ => utils}/runtime-determiner.ts | 15 ++-- .../custom-resource-handlers/package.json | 1 + 6 files changed, 46 insertions(+), 93 deletions(-) delete mode 100644 packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/runtime.ts rename packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/{ => utils}/runtime-determiner.ts (85%) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/classes.ts index 4ea6fb4c9d89d..e86c00a541408 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/classes.ts @@ -14,6 +14,7 @@ import { SuperInitializer, Expression, } from '@cdklabs/typewriter'; +import { Runtime } from 'aws-cdk-lib/aws-lambda'; import { CdkHandlerFrameworkModule } from './framework'; import { CONSTRUCTS_MODULE, @@ -23,7 +24,6 @@ import { CUSTOM_RESOURCE_PROVIDER_BASE, CUSTOM_RESOURCE_PROVIDER_OPTIONS, } from './modules'; -import { Runtime } from './runtime'; /** * Initialization properties for a class constructor. @@ -103,7 +103,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { new Splat(expr.ident('props')), ['code', expr.directCode(`lambda.Code.fromAsset(path.join(__dirname, '${props.codeDirectory}'))`)], ['handler', expr.lit(props.entrypoint ?? 'index.handler')], - ['runtime', expr.directCode(props.runtime.toLambdaRuntime())], + ['runtime', expr.directCode(toLambdaRuntime(props.runtime))], ]); this.buildConstructor({ constructorPropsType: LAMBDA_MODULE.FunctionOptions, @@ -162,7 +162,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { new Splat(expr.ident('props')), ['code', expr.directCode(`lambda.Code.fromAsset(path.join(__dirname, '${props.codeDirectory}'))`)], ['handler', expr.lit(props.entrypoint ?? 'index.handler')], - ['runtime', expr.directCode(props.runtime.toLambdaRuntime())], + ['runtime', expr.directCode(toLambdaRuntime(props.runtime))], ]); this.buildConstructor({ constructorPropsType: _interface.type, @@ -309,3 +309,21 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { init.addBody(new SuperInitializer(...superInitializerArgs)); } } + +function toLambdaRuntime(runtime: Runtime) { + switch (runtime.name) { + case Runtime.NODEJS_16_X.name: { + return 'lambda.Runtime.NODEJS_16_X'; + } + case Runtime.NODEJS_18_X.name: { + return 'lambda.Runtime.NODEJS_18_X'; + } + case Runtime.PYTHON_3_9.name: { + return 'lambda.Runtime.PYTHON_3_9'; + } + case Runtime.PYTHON_3_10.name: { + return 'lambda.Runtime.PYTHON_3_10'; + } + } + throw new Error('Unable to convert runtime to lambda runtime'); +} diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/config.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/config.ts index ad5c1d137b6c6..1c62d448ff158 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/config.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/config.ts @@ -1,5 +1,6 @@ +/* eslint-disable import/no-extraneous-dependencies */ import * as path from 'path'; -import { Runtime } from './runtime'; +import { Runtime } from 'aws-cdk-lib/aws-lambda'; /** * Handler framework component types. diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts index 04dab7f885a67..ffdb007c0bd08 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts @@ -1,17 +1,17 @@ /* eslint-disable import/no-extraneous-dependencies */ import { ExternalModule, InterfaceType, Module, TypeScriptRenderer } from '@cdklabs/typewriter'; +import { Runtime } from 'aws-cdk-lib/aws-lambda'; import * as fs from 'fs-extra'; import { CdkHandlerClassProps, CdkHandlerFrameworkClass } from './classes'; import { ComponentType, ConfigProps } from './config'; import { CONSTRUCTS_MODULE, CORE_MODULE, CUSTOM_RESOURCE_PROVIDER_BASE, CUSTOM_RESOURCE_PROVIDER_OPTIONS, LAMBDA_MODULE, PATH_MODULE, STACK } from './modules'; -import { Runtime } from './runtime'; -import { RuntimeDeterminer } from './runtime-determiner'; +import { RuntimeDeterminer } from './utils/runtime-determiner'; export class CdkHandlerFrameworkModule extends Module { /** * The latest nodejs runtime version available across all AWS regions */ - private static readonly DEFAULT_RUNTIME = Runtime.NODEJS_18_X; + private static readonly DEFAULT_RUNTIME = Runtime.NODEJS_LATEST; private readonly renderer = new TypeScriptRenderer(); private readonly externalModules = new Map(); @@ -32,6 +32,9 @@ export class CdkHandlerFrameworkModule extends Module { * Build a framework component inside of this module. */ public build(component: ConfigProps, sourceCodeDirectory: string) { + const name = this.buildComponentName(); + /* eslint-disable no-console */ + console.log(name); const props: CdkHandlerClassProps = { codeDirectory: sourceCodeDirectory, name: component.name, @@ -95,6 +98,14 @@ export class CdkHandlerFrameworkModule extends Module { return this._interfaces.get(name); } + private buildComponentName() { + const id = this.fqn.split('/').at(-1); + const name = id?.replace( + /-([a-z])/g, (s) => { return s[1].toUpperCase(); }, + ) ?? 'provider'; + return name.charAt(0).toUpperCase() + name.slice(1); + } + private importExternalModules() { PATH_MODULE.import(this, 'path'); for (const fqn of this.externalModules.keys()) { diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/runtime.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/runtime.ts deleted file mode 100644 index c6966c734b4fd..0000000000000 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/runtime.ts +++ /dev/null @@ -1,79 +0,0 @@ -export enum RuntimeFamily { - NODEJS, - PYTHON, -} - -/** - * Properties used to initialize a framework runtime. - */ -interface RuntimeProps { - /** - * Whether or not this runtime is deprecated. - * - * @default false - */ - readonly isDeprecated?: boolean; -} - -/** - * Custom resource framework runtimes used for code generation. - */ -export class Runtime { - /** - * The NodeJS 16.x runtime (nodejs16.x) - */ - public static readonly NODEJS_16_X = new Runtime('nodejs16.x', RuntimeFamily.NODEJS); - - /** - * The NodeJS 18.x runtime (nodejs18.x) - */ - public static readonly NODEJS_18_X = new Runtime('nodejs18.x', RuntimeFamily.NODEJS); - - /** - * The NodeJS 20.x runtime (nodejs20.x) - */ - public static readonly NODEJS_20_X = new Runtime('nodejs20.x', RuntimeFamily.NODEJS); - - /** - * The Python 3.9 runtime (python3.9) - */ - public static readonly PYTHON_3_9 = new Runtime('python3.9', RuntimeFamily.PYTHON); - - /** - * The Python 3.10 runtime (python3.10) - */ - public static readonly PYTHON_3_10 = new Runtime('python3.10', RuntimeFamily.PYTHON); - - public readonly name: string; - public readonly family: RuntimeFamily; - public readonly isDeprecated: boolean; - - private constructor(name: string, family: RuntimeFamily, props: RuntimeProps = {}) { - this.name = name; - this.family = family; - this.isDeprecated = props.isDeprecated ?? false; - } - - public runtimeEquals(other: Runtime): boolean { - return other.name === this.name && other.family === this.family; - } - - public toLambdaRuntime() { - switch (this.name) { - case 'nodejs16.x': { - return 'lambda.Runtime.NODEJS_16_X'; - } - case 'nodejs18.x': { - return 'lambda.Runtime.NODEJS_18_X'; - } - case 'python3.9': { - return 'lambda.Runtime.PYTHON_3_9'; - } - case 'python3.10': { - return 'lambda.Runtime.PYTHON_3_10'; - } - } - throw new Error('Unable to convert runtime to lambda runtime'); - } -} - diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/runtime-determiner.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/utils/runtime-determiner.ts similarity index 85% rename from packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/runtime-determiner.ts rename to packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/utils/runtime-determiner.ts index 05bd84ed5f410..48f4488f7ade1 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/runtime-determiner.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/utils/runtime-determiner.ts @@ -1,4 +1,5 @@ -import { Runtime, RuntimeFamily } from './runtime'; +/* eslint-disable import/no-extraneous-dependencies */ +import { Runtime, RuntimeFamily } from 'aws-cdk-lib/aws-lambda'; /** * A utility class used to determine the latest runtime for a specific runtime family @@ -22,18 +23,18 @@ export class RuntimeDeterminer { const nodeJsRuntimes = runtimes.filter(runtime => runtime.family === RuntimeFamily.NODEJS); const latestNodeJsRuntime = RuntimeDeterminer.latestNodeJsRuntime(nodeJsRuntimes, defaultRuntime); if (latestNodeJsRuntime !== undefined) { - if (latestNodeJsRuntime.isDeprecated) { - throw new Error(`Latest nodejs runtime ${latestNodeJsRuntime} is deprecated. You must upgrade to the latest code compatible nodejs runtime`); - } + // if (latestNodeJsRuntime.isDeprecated) { + // throw new Error(`Latest nodejs runtime ${latestNodeJsRuntime} is deprecated. You must upgrade to the latest code compatible nodejs runtime`); + // } return latestNodeJsRuntime; } const pythonRuntimes = runtimes.filter(runtime => runtime.family === RuntimeFamily.PYTHON); const latestPythonRuntime = RuntimeDeterminer.latestPythonRuntime(pythonRuntimes); if (latestPythonRuntime !== undefined) { - if (latestPythonRuntime.isDeprecated) { - throw new Error(`Latest python runtime ${latestPythonRuntime} is deprecated. You must upgrade to the latest code compatible python runtime`); - } + // if (latestPythonRuntime.isDeprecated) { + // throw new Error(`Latest python runtime ${latestPythonRuntime} is deprecated. You must upgrade to the latest code compatible python runtime`); + // } return latestPythonRuntime; } diff --git a/packages/@aws-cdk/custom-resource-handlers/package.json b/packages/@aws-cdk/custom-resource-handlers/package.json index 7e0a748fdd879..b8290183d4c89 100644 --- a/packages/@aws-cdk/custom-resource-handlers/package.json +++ b/packages/@aws-cdk/custom-resource-handlers/package.json @@ -45,6 +45,7 @@ "@aws-sdk/client-eks": "3.421.0", "@aws-sdk/client-sts": "3.421.0", "@aws-sdk/node-http-handler": "^3.370.0", + "aws-cdk-lib": "0.0.0", "@smithy/util-stream": "^2.0.20", "@types/jest": "^29.5.8", "aws-sdk-client-mock": "^3.0.0", From e036649cb10af1839d2c78df95bcaa5378287733 Mon Sep 17 00:00:00 2001 From: Francis Date: Fri, 8 Dec 2023 11:34:42 -0800 Subject: [PATCH 40/73] lambda runtime isdeprecated flag Signed-off-by: Francis --- .../aws-cdk-lib/aws-lambda/lib/runtime.ts | 77 +++++++++++++++---- .../aws-lambda/test/runtime.test.ts | 21 +++++ 2 files changed, 84 insertions(+), 14 deletions(-) diff --git a/packages/aws-cdk-lib/aws-lambda/lib/runtime.ts b/packages/aws-cdk-lib/aws-lambda/lib/runtime.ts index 0013d67ad2079..49d18f8a7ebf8 100644 --- a/packages/aws-cdk-lib/aws-lambda/lib/runtime.ts +++ b/packages/aws-cdk-lib/aws-lambda/lib/runtime.ts @@ -30,6 +30,12 @@ export interface LambdaRuntimeProps { * @default false */ readonly supportsSnapStart?: boolean; + + /** + * Whether this runtime is deprecated. + * @default false + */ + readonly isDeprecated?: boolean; } export enum RuntimeFamily { @@ -56,43 +62,64 @@ export class Runtime { * The NodeJS runtime (nodejs) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS runtime. */ - public static readonly NODEJS = new Runtime('nodejs', RuntimeFamily.NODEJS, { supportsInlineCode: true }); + public static readonly NODEJS = new Runtime('nodejs', RuntimeFamily.NODEJS, { + supportsInlineCode: true, + isDeprecated: true, + }); /** * The NodeJS 4.3 runtime (nodejs4.3) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS runtime. */ - public static readonly NODEJS_4_3 = new Runtime('nodejs4.3', RuntimeFamily.NODEJS, { supportsInlineCode: true }); + public static readonly NODEJS_4_3 = new Runtime('nodejs4.3', RuntimeFamily.NODEJS, { + supportsInlineCode: true, + isDeprecated: true, + }); /** * The NodeJS 6.10 runtime (nodejs6.10) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS runtime. */ - public static readonly NODEJS_6_10 = new Runtime('nodejs6.10', RuntimeFamily.NODEJS, { supportsInlineCode: true }); + public static readonly NODEJS_6_10 = new Runtime('nodejs6.10', RuntimeFamily.NODEJS, { + supportsInlineCode: true, + isDeprecated: true, + }); /** * The NodeJS 8.10 runtime (nodejs8.10) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS runtime. */ - public static readonly NODEJS_8_10 = new Runtime('nodejs8.10', RuntimeFamily.NODEJS, { supportsInlineCode: true }); + public static readonly NODEJS_8_10 = new Runtime('nodejs8.10', RuntimeFamily.NODEJS, { + supportsInlineCode: true, + isDeprecated: true, + }); /** * The NodeJS 10.x runtime (nodejs10.x) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS runtime. */ - public static readonly NODEJS_10_X = new Runtime('nodejs10.x', RuntimeFamily.NODEJS, { supportsInlineCode: true }); + public static readonly NODEJS_10_X = new Runtime('nodejs10.x', RuntimeFamily.NODEJS, { + supportsInlineCode: true, + isDeprecated: true, + }); /** * The NodeJS 12.x runtime (nodejs12.x) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS runtime. */ - public static readonly NODEJS_12_X = new Runtime('nodejs12.x', RuntimeFamily.NODEJS, { supportsInlineCode: true }); + public static readonly NODEJS_12_X = new Runtime('nodejs12.x', RuntimeFamily.NODEJS, { + supportsInlineCode: true, + isDeprecated: true, + }); /** * The NodeJS 14.x runtime (nodejs14.x) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS runtime. */ - public static readonly NODEJS_14_X = new Runtime('nodejs14.x', RuntimeFamily.NODEJS, { supportsInlineCode: true }); + public static readonly NODEJS_14_X = new Runtime('nodejs14.x', RuntimeFamily.NODEJS, { + supportsInlineCode: true, + isDeprecated: true, + }); /** * The NodeJS 16.x runtime (nodejs16.x) @@ -119,7 +146,10 @@ export class Runtime { * The Python 2.7 runtime (python2.7) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest Python runtime. */ - public static readonly PYTHON_2_7 = new Runtime('python2.7', RuntimeFamily.PYTHON, { supportsInlineCode: true }); + public static readonly PYTHON_2_7 = new Runtime('python2.7', RuntimeFamily.PYTHON, { + supportsInlineCode: true, + isDeprecated: true, + }); /** * The Python 3.6 runtime (python3.6) (not recommended) @@ -131,6 +161,7 @@ export class Runtime { public static readonly PYTHON_3_6 = new Runtime('python3.6', RuntimeFamily.PYTHON, { supportsInlineCode: true, supportsCodeGuruProfiling: true, + isDeprecated: true, }); /** @@ -228,37 +259,49 @@ export class Runtime { * The .NET Core 1.0 runtime (dotnetcore1.0) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest .NET Core runtime. */ - public static readonly DOTNET_CORE_1 = new Runtime('dotnetcore1.0', RuntimeFamily.DOTNET_CORE); + public static readonly DOTNET_CORE_1 = new Runtime('dotnetcore1.0', RuntimeFamily.DOTNET_CORE, { + isDeprecated: true, + }); /** * The .NET Core 2.0 runtime (dotnetcore2.0) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest .NET Core runtime. */ - public static readonly DOTNET_CORE_2 = new Runtime('dotnetcore2.0', RuntimeFamily.DOTNET_CORE); + public static readonly DOTNET_CORE_2 = new Runtime('dotnetcore2.0', RuntimeFamily.DOTNET_CORE, { + isDeprecated: true, + }); /** * The .NET Core 2.1 runtime (dotnetcore2.1) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest .NET Core runtime. */ - public static readonly DOTNET_CORE_2_1 = new Runtime('dotnetcore2.1', RuntimeFamily.DOTNET_CORE); + public static readonly DOTNET_CORE_2_1 = new Runtime('dotnetcore2.1', RuntimeFamily.DOTNET_CORE, { + isDeprecated: true, + }); /** * The .NET Core 3.1 runtime (dotnetcore3.1) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest .NET Core runtime. */ - public static readonly DOTNET_CORE_3_1 = new Runtime('dotnetcore3.1', RuntimeFamily.DOTNET_CORE); + public static readonly DOTNET_CORE_3_1 = new Runtime('dotnetcore3.1', RuntimeFamily.DOTNET_CORE, { + isDeprecated: true, + }); /** * The Go 1.x runtime (go1.x) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the PROVIDED_AL2023 runtime. */ - public static readonly GO_1_X = new Runtime('go1.x', RuntimeFamily.GO); + public static readonly GO_1_X = new Runtime('go1.x', RuntimeFamily.GO, { + isDeprecated: true, + }); /** * The Ruby 2.5 runtime (ruby2.5) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest Ruby runtime. */ - public static readonly RUBY_2_5 = new Runtime('ruby2.5', RuntimeFamily.RUBY); + public static readonly RUBY_2_5 = new Runtime('ruby2.5', RuntimeFamily.RUBY, { + isDeprecated: true, + }); /** * The Ruby 2.7 runtime (ruby2.7) @@ -333,11 +376,17 @@ export class Runtime { */ public readonly isVariable: boolean; + /** + * Whether or not this runtime is deprecated. + */ + public readonly isDeprecated: boolean; + constructor(name: string, family?: RuntimeFamily, props: LambdaRuntimeProps = {}) { this.name = name; this.supportsInlineCode = !!props.supportsInlineCode; this.family = family; this.isVariable = !!props.isVariable; + this.isDeprecated = props.isDeprecated ?? false; const imageName = props.bundlingDockerImage ?? `public.ecr.aws/sam/build-${name}`; this.bundlingDockerImage = DockerImage.fromRegistry(imageName); diff --git a/packages/aws-cdk-lib/aws-lambda/test/runtime.test.ts b/packages/aws-cdk-lib/aws-lambda/test/runtime.test.ts index f3976e70c4327..d1d89cbbd5b6a 100644 --- a/packages/aws-cdk-lib/aws-lambda/test/runtime.test.ts +++ b/packages/aws-cdk-lib/aws-lambda/test/runtime.test.ts @@ -54,4 +54,25 @@ describe('runtime', () => { // THEN expect(runtime.bundlingDockerImage.image).toEqual('my-docker-image'); }); + + test.each([ + [lambda.Runtime.PYTHON_2_7], + [lambda.Runtime.PYTHON_3_6], + [lambda.Runtime.NODEJS], + [lambda.Runtime.NODEJS_4_3], + [lambda.Runtime.NODEJS_6_10], + [lambda.Runtime.NODEJS_8_10], + [lambda.Runtime.NODEJS_10_X], + [lambda.Runtime.NODEJS_12_X], + [lambda.Runtime.NODEJS_14_X], + [lambda.Runtime.DOTNET_CORE_1], + [lambda.Runtime.DOTNET_CORE_2], + [lambda.Runtime.DOTNET_CORE_2_1], + [lambda.Runtime.DOTNET_CORE_3_1], + [lambda.Runtime.GO_1_X], + [lambda.Runtime.RUBY_2_5], + [lambda.Runtime.PROVIDED], + ])('%s is deprecated', (runtime) => { + expect(runtime.isDeprecated).toEqual(true); + }); }); From b8da22df9abf882b6d69dc530dc9bad3f5fae6c8 Mon Sep 17 00:00:00 2001 From: Francis Date: Fri, 8 Dec 2023 12:18:58 -0800 Subject: [PATCH 41/73] generate names better Signed-off-by: Francis --- .../lib/custom-resource-framework/classes.ts | 8 ++-- .../lib/custom-resource-framework/config.ts | 38 ------------------- .../custom-resource-framework/framework.ts | 28 ++++++++------ .../utils/runtime-determiner.ts | 12 +++--- 4 files changed, 25 insertions(+), 61 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/classes.ts index e86c00a541408..5da6323b95507 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/classes.ts @@ -76,10 +76,8 @@ export interface CdkHandlerClassProps { /** * The name of the method within your code that Lambda calls to execute your function. - * - * @default 'index.handler' */ - readonly entrypoint?: string; + readonly entrypoint: string; } export abstract class CdkHandlerFrameworkClass extends ClassType { @@ -102,7 +100,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { const superProps = new ObjectLiteral([ new Splat(expr.ident('props')), ['code', expr.directCode(`lambda.Code.fromAsset(path.join(__dirname, '${props.codeDirectory}'))`)], - ['handler', expr.lit(props.entrypoint ?? 'index.handler')], + ['handler', expr.lit(props.entrypoint)], ['runtime', expr.directCode(toLambdaRuntime(props.runtime))], ]); this.buildConstructor({ @@ -161,7 +159,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { const superProps = new ObjectLiteral([ new Splat(expr.ident('props')), ['code', expr.directCode(`lambda.Code.fromAsset(path.join(__dirname, '${props.codeDirectory}'))`)], - ['handler', expr.lit(props.entrypoint ?? 'index.handler')], + ['handler', expr.lit(props.entrypoint)], ['runtime', expr.directCode(toLambdaRuntime(props.runtime))], ]); this.buildConstructor({ diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/config.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/config.ts index 1c62d448ff158..46e604649275a 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/config.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/config.ts @@ -41,11 +41,6 @@ export interface ConfigProps { */ readonly sourceCode: string; - /** - * The name of the component class to generate, i.e., `MyCdkFunction`. - */ - readonly name: string; - /** * Runtimes that are compatible with the source code. */ @@ -74,7 +69,6 @@ export const config: HandlerFrameworkConfig = { 'certificate-request-provider': [ { type: ComponentType.CDK_FUNCTION, - name: 'CertificateRequestFunction', sourceCode: path.resolve(__dirname, '..', 'aws-certificatemanager', 'dns-validated-certificate-handler', 'index.js'), compatibleRuntimes: [Runtime.NODEJS_18_X], entrypoint: 'index.certificateRequestHandler', @@ -85,7 +79,6 @@ export const config: HandlerFrameworkConfig = { 'cross-region-string-param-reader-provider': [ { type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, - name: 'CrossRegionStringParamReaderProvider', sourceCode: path.resolve(__dirname, '..', 'aws-cloudfront', 'edge-function', 'index.js'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -95,14 +88,12 @@ export const config: HandlerFrameworkConfig = { 'replica-provider': [ { type: ComponentType.CDK_FUNCTION, - name: 'OnEventFunction', sourceCode: path.resolve(__dirname, '..', 'aws-dynamodb', 'replica-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], entrypoint: 'index.onEventHandler', }, { type: ComponentType.CDK_FUNCTION, - name: 'IsCompleteFunction', sourceCode: path.resolve(__dirname, '..', 'aws-dynamodb', 'replica-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], entrypoint: 'index.isCompleteHandler', @@ -113,7 +104,6 @@ export const config: HandlerFrameworkConfig = { 'restrict-default-sg-provider': [ { type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, - name: 'RestrictDefaultSgProvider', sourceCode: path.resolve(__dirname, '..', 'aws-ec2', 'restrict-default-security-group-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -123,7 +113,6 @@ export const config: HandlerFrameworkConfig = { 'auto-delete-images-provider': [ { type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, - name: 'AutoDeleteImagesProvider', sourceCode: path.resolve(__dirname, '..', 'aws-ecr', 'auto-delete-images-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -133,7 +122,6 @@ export const config: HandlerFrameworkConfig = { 'drain-hook-provider': [ { type: ComponentType.CDK_FUNCTION, - name: 'DrainHookFunction', sourceCode: path.resolve(__dirname, '..', 'aws-ecs', 'lambda-source', 'index.py'), compatibleRuntimes: [Runtime.PYTHON_3_9], entrypoint: 'index.lambda_handler', @@ -145,14 +133,12 @@ export const config: HandlerFrameworkConfig = { 'cluster-resource-provider': [ { type: ComponentType.CDK_FUNCTION, - name: 'OnEventFunction', sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'cluster-resource-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], entrypoint: 'index.onEvent', }, { type: ComponentType.CDK_FUNCTION, - name: 'IsCompleteFunction', sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'cluster-resource-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], entrypoint: 'index.isComplete', @@ -161,35 +147,30 @@ export const config: HandlerFrameworkConfig = { 'kubectl-provider': [ { type: ComponentType.CDK_FUNCTION, - name: 'KubectlFunction', sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'index.py'), compatibleRuntimes: [Runtime.PYTHON_3_10], preventMinifyAndBundle: true, }, { type: ComponentType.CDK_NO_OP, - name: 'ApplyHandler', sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'apply', '__init__.py'), compatibleRuntimes: [Runtime.PYTHON_3_10], preventMinifyAndBundle: true, }, { type: ComponentType.CDK_NO_OP, - name: 'GetHandler', sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'get', '__init__.py'), compatibleRuntimes: [Runtime.PYTHON_3_10], preventMinifyAndBundle: true, }, { type: ComponentType.CDK_NO_OP, - name: 'HelmHandler', sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'helm', '__init__.py'), compatibleRuntimes: [Runtime.PYTHON_3_10], preventMinifyAndBundle: true, }, { type: ComponentType.CDK_NO_OP, - name: 'PatchHandler', sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'patch', '__init__.py'), compatibleRuntimes: [Runtime.PYTHON_3_10], preventMinifyAndBundle: true, @@ -200,7 +181,6 @@ export const config: HandlerFrameworkConfig = { 'aws-api-provider': [ { type: ComponentType.CDK_SINGLETON_FUNCTION, - name: 'AwsApiFunction', sourceCode: path.resolve(__dirname, '..', 'aws-events-targets', 'aws-api-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -210,7 +190,6 @@ export const config: HandlerFrameworkConfig = { 'oidc-provider': [ { type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, - name: 'OidcProvider', sourceCode: path.resolve(__dirname, '..', 'aws-iam', 'oidc-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -220,7 +199,6 @@ export const config: HandlerFrameworkConfig = { 'log-retention': [ { type: ComponentType.CDK_NO_OP, - name: 'LogRetentionHandler', sourceCode: path.resolve(__dirname, '..', 'aws-logs', 'log-retention-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -230,7 +208,6 @@ export const config: HandlerFrameworkConfig = { 'cross-account-zone-delegation-provider': [ { type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, - name: 'CrossAccountZoneDelegationProvider', sourceCode: path.resolve(__dirname, '..', 'aws-route53', 'cross-account-zone-delegation-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -238,7 +215,6 @@ export const config: HandlerFrameworkConfig = { 'delete-existing-record-set-provider': [ { type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, - name: 'DeleteExistingRecordSetProvider', sourceCode: path.resolve(__dirname, '..', 'aws-route53', 'delete-existing-record-set-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -248,7 +224,6 @@ export const config: HandlerFrameworkConfig = { 'auto-delete-objects-provider': [ { type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, - name: 'AutoDeleteObjectsProvider', sourceCode: path.resolve(__dirname, '..', 'aws-s3', 'auto-delete-objects-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -256,7 +231,6 @@ export const config: HandlerFrameworkConfig = { 'notifications-resource-handler': [ { type: ComponentType.CDK_NO_OP, - name: 'NotificationsResourceHandler', sourceCode: path.resolve(__dirname, '..', 'aws-s3', 'notifications-resource-handler', 'index.py'), compatibleRuntimes: [Runtime.PYTHON_3_9], preventMinifyAndBundle: true, @@ -267,7 +241,6 @@ export const config: HandlerFrameworkConfig = { 'bucket-deployment-provider': [ { type: ComponentType.CDK_SINGLETON_FUNCTION, - name: 'BucketDeploymentFunction', sourceCode: path.resolve(__dirname, '..', 'aws-s3-deployment', 'bucket-deployment-handler', 'index.py'), compatibleRuntimes: [Runtime.PYTHON_3_9], preventMinifyAndBundle: true, @@ -278,7 +251,6 @@ export const config: HandlerFrameworkConfig = { 'drop-spam-provider': [ { type: ComponentType.CDK_SINGLETON_FUNCTION, - name: 'DropSpamFunction', sourceCode: path.resolve(__dirname, '..', 'aws-ses', 'drop-spam-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -288,7 +260,6 @@ export const config: HandlerFrameworkConfig = { 'eval-nodejs-provider': [ { type: ComponentType.CDK_SINGLETON_FUNCTION, - name: 'EvalNodeJsFunction', sourceCode: path.resolve(__dirname, '..', 'aws-stepfunctions-tasks', 'eval-nodejs-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -296,7 +267,6 @@ export const config: HandlerFrameworkConfig = { 'role-policy-provider': [ { type: ComponentType.CDK_SINGLETON_FUNCTION, - name: 'RolePolicyFunction', sourceCode: path.resolve(__dirname, '..', 'aws-stepfunctions-tasks', 'role-policy-handler', 'index.py'), compatibleRuntimes: [Runtime.PYTHON_3_9], preventMinifyAndBundle: true, @@ -307,7 +277,6 @@ export const config: HandlerFrameworkConfig = { 'auto-delete-underlying-resources-provider': [ { type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, - name: 'AutoDeleteUnderlyingResourcesProvider', sourceCode: path.resolve(__dirname, '..', 'aws-synthetics', 'auto-delete-underlying-resources-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -317,7 +286,6 @@ export const config: HandlerFrameworkConfig = { 'cfn-utils-provider': [ { type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, - name: 'CdkCfnUtilsProvider', sourceCode: path.resolve(__dirname, '..', 'core', 'cfn-utils-provider', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -325,7 +293,6 @@ export const config: HandlerFrameworkConfig = { 'cross-region-ssm-writer-provider': [ { type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, - name: 'CrossRegionWriterProvider', sourceCode: path.resolve(__dirname, '..', 'core', 'cross-region-ssm-writer-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -333,7 +300,6 @@ export const config: HandlerFrameworkConfig = { 'cross-region-ssm-reader-provider': [ { type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, - name: 'CrossRegionReaderProvider', sourceCode: path.resolve(__dirname, '..', 'core', 'cross-region-ssm-reader-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -341,7 +307,6 @@ export const config: HandlerFrameworkConfig = { 'nodejs-entrypoint': [ { type: ComponentType.CDK_NO_OP, - name: 'NodejsEntrypointHandler', sourceCode: path.resolve(__dirname, '..', 'core', 'nodejs-entrypoint-handler', 'index.js'), compatibleRuntimes: [Runtime.NODEJS_18_X], preventMinifyAndBundle: true, @@ -352,7 +317,6 @@ export const config: HandlerFrameworkConfig = { 'aws-custom-resource-provider': [ { type: ComponentType.CDK_SINGLETON_FUNCTION, - name: 'AwsCustomResourceFunction', sourceCode: path.resolve(__dirname, '..', 'custom-resources', 'aws-custom-resource-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -362,7 +326,6 @@ export const config: HandlerFrameworkConfig = { 'approve-lambda': [ { type: ComponentType.CDK_FUNCTION, - name: 'ApproveLambdaFunction', sourceCode: path.resolve(__dirname, '..', 'pipelines', 'approve-lambda', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -372,7 +335,6 @@ export const config: HandlerFrameworkConfig = { 'trigger-provider': [ { type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, - name: 'TriggerProvider', sourceCode: path.resolve(__dirname, '..', 'triggers', 'lambda', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts index ffdb007c0bd08..96e308d824bde 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts @@ -32,23 +32,26 @@ export class CdkHandlerFrameworkModule extends Module { * Build a framework component inside of this module. */ public build(component: ConfigProps, sourceCodeDirectory: string) { - const name = this.buildComponentName(); + if (component.type === ComponentType.CDK_NO_OP) { + return; + } + + this.hasComponents = true; + + const entrypoint = component.entrypoint ?? 'index.handler'; + const name = this.buildComponentName(entrypoint); /* eslint-disable no-console */ console.log(name); const props: CdkHandlerClassProps = { codeDirectory: sourceCodeDirectory, - name: component.name, + name: this.buildComponentName(entrypoint), runtime: RuntimeDeterminer.determineLatestRuntime( component.compatibleRuntimes, CdkHandlerFrameworkModule.DEFAULT_RUNTIME, ), - entrypoint: component.entrypoint, + entrypoint, }; - if (!this.hasComponents && component.type !== ComponentType.CDK_NO_OP) { - this.hasComponents = true; - } - switch (component.type) { case ComponentType.CDK_FUNCTION: { CdkHandlerFrameworkClass.buildCdkFunction(this, props); @@ -98,12 +101,13 @@ export class CdkHandlerFrameworkModule extends Module { return this._interfaces.get(name); } - private buildComponentName() { - const id = this.fqn.split('/').at(-1); - const name = id?.replace( + private buildComponentName(entrypoint: string) { + const id = this.fqn.split('/').at(-1)?.replace('-provider', '') ?? ''; + const handler = entrypoint.split('.').at(-1)?.replace(/[_Hh]andler/g, '') ?? ''; + const name = (id.replace( /-([a-z])/g, (s) => { return s[1].toUpperCase(); }, - ) ?? 'provider'; - return name.charAt(0).toUpperCase() + name.slice(1); + )) + (handler.charAt(0).toUpperCase() + handler.slice(1)); + return name.charAt(0).toUpperCase() + name.slice(1) + 'Provider'; } private importExternalModules() { diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/utils/runtime-determiner.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/utils/runtime-determiner.ts index 48f4488f7ade1..c370b3a26f382 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/utils/runtime-determiner.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/utils/runtime-determiner.ts @@ -23,18 +23,18 @@ export class RuntimeDeterminer { const nodeJsRuntimes = runtimes.filter(runtime => runtime.family === RuntimeFamily.NODEJS); const latestNodeJsRuntime = RuntimeDeterminer.latestNodeJsRuntime(nodeJsRuntimes, defaultRuntime); if (latestNodeJsRuntime !== undefined) { - // if (latestNodeJsRuntime.isDeprecated) { - // throw new Error(`Latest nodejs runtime ${latestNodeJsRuntime} is deprecated. You must upgrade to the latest code compatible nodejs runtime`); - // } + if (latestNodeJsRuntime.isDeprecated) { + throw new Error(`Latest nodejs runtime ${latestNodeJsRuntime} is deprecated. You must upgrade to the latest code compatible nodejs runtime`); + } return latestNodeJsRuntime; } const pythonRuntimes = runtimes.filter(runtime => runtime.family === RuntimeFamily.PYTHON); const latestPythonRuntime = RuntimeDeterminer.latestPythonRuntime(pythonRuntimes); if (latestPythonRuntime !== undefined) { - // if (latestPythonRuntime.isDeprecated) { - // throw new Error(`Latest python runtime ${latestPythonRuntime} is deprecated. You must upgrade to the latest code compatible python runtime`); - // } + if (latestPythonRuntime.isDeprecated) { + throw new Error(`Latest python runtime ${latestPythonRuntime} is deprecated. You must upgrade to the latest code compatible python runtime`); + } return latestPythonRuntime; } From 13a0b4828d9db07c76b12c940b670828b34c340e Mon Sep 17 00:00:00 2001 From: Francis Date: Fri, 8 Dec 2023 12:22:00 -0800 Subject: [PATCH 42/73] formatting Signed-off-by: Francis --- .../lib/custom-resource-framework/framework.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts index 96e308d824bde..9735485553ef2 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts @@ -40,16 +40,15 @@ export class CdkHandlerFrameworkModule extends Module { const entrypoint = component.entrypoint ?? 'index.handler'; const name = this.buildComponentName(entrypoint); - /* eslint-disable no-console */ - console.log(name); + const props: CdkHandlerClassProps = { + name, + entrypoint, codeDirectory: sourceCodeDirectory, - name: this.buildComponentName(entrypoint), runtime: RuntimeDeterminer.determineLatestRuntime( component.compatibleRuntimes, CdkHandlerFrameworkModule.DEFAULT_RUNTIME, ), - entrypoint, }; switch (component.type) { From 1496b559f56800bfe5037fcfecb8f2127fd15842 Mon Sep 17 00:00:00 2001 From: Francis Date: Fri, 8 Dec 2023 18:18:36 -0800 Subject: [PATCH 43/73] update custom resources to use auto generated names Signed-off-by: Francis --- .../lib/custom-resource-framework/classes.ts | 40 +++++----- .../lib/custom-resource-framework/config.ts | 2 +- .../custom-resource-framework/framework.ts | 7 +- .../lib/custom-resource-framework/runtime.ts | 78 +++++++++++++++++++ .../utils/runtime-determiner.ts | 2 +- .../custom-resource-handlers/package.json | 1 - .../lib/dns-validated-certificate.ts | 4 +- .../aws-dynamodb/lib/replica-provider.ts | 14 +--- packages/aws-cdk-lib/aws-ec2/lib/vpc.ts | 8 +- .../aws-eks/lib/cluster-resource-provider.ts | 6 +- .../aws-eks/lib/kubectl-provider.ts | 4 +- .../aws-events-targets/lib/aws-api.ts | 4 +- .../lib/bucket-deployment.ts | 4 +- .../aws-cdk-lib/aws-ses/lib/receipt-rule.ts | 4 +- .../lib/emrcontainers/start-job-run.ts | 4 +- .../lib/evaluate-expression.ts | 4 +- .../export-reader-provider.ts | 4 +- .../export-writer-provider.ts | 4 +- .../core/lib/private/cfn-utils-provider.ts | 4 +- .../aws-custom-resource.ts | 4 +- .../lib/private/application-security-check.ts | 4 +- 21 files changed, 138 insertions(+), 68 deletions(-) create mode 100644 packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/runtime.ts diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/classes.ts index 5da6323b95507..5020ca804bf81 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/classes.ts @@ -14,7 +14,6 @@ import { SuperInitializer, Expression, } from '@cdklabs/typewriter'; -import { Runtime } from 'aws-cdk-lib/aws-lambda'; import { CdkHandlerFrameworkModule } from './framework'; import { CONSTRUCTS_MODULE, @@ -24,6 +23,7 @@ import { CUSTOM_RESOURCE_PROVIDER_BASE, CUSTOM_RESOURCE_PROVIDER_OPTIONS, } from './modules'; +import { Runtime } from './runtime'; /** * Initialization properties for a class constructor. @@ -101,7 +101,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { new Splat(expr.ident('props')), ['code', expr.directCode(`lambda.Code.fromAsset(path.join(__dirname, '${props.codeDirectory}'))`)], ['handler', expr.lit(props.entrypoint)], - ['runtime', expr.directCode(toLambdaRuntime(props.runtime))], + ['runtime', expr.directCode(props.runtime.toLambdaRuntime())], ]); this.buildConstructor({ constructorPropsType: LAMBDA_MODULE.FunctionOptions, @@ -160,7 +160,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { new Splat(expr.ident('props')), ['code', expr.directCode(`lambda.Code.fromAsset(path.join(__dirname, '${props.codeDirectory}'))`)], ['handler', expr.lit(props.entrypoint)], - ['runtime', expr.directCode(toLambdaRuntime(props.runtime))], + ['runtime', expr.directCode(props.runtime.toLambdaRuntime())], ]); this.buildConstructor({ constructorPropsType: _interface.type, @@ -308,20 +308,20 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { } } -function toLambdaRuntime(runtime: Runtime) { - switch (runtime.name) { - case Runtime.NODEJS_16_X.name: { - return 'lambda.Runtime.NODEJS_16_X'; - } - case Runtime.NODEJS_18_X.name: { - return 'lambda.Runtime.NODEJS_18_X'; - } - case Runtime.PYTHON_3_9.name: { - return 'lambda.Runtime.PYTHON_3_9'; - } - case Runtime.PYTHON_3_10.name: { - return 'lambda.Runtime.PYTHON_3_10'; - } - } - throw new Error('Unable to convert runtime to lambda runtime'); -} +// function toLambdaRuntime(runtime: Runtime) { +// switch (runtime.name) { +// case Runtime.NODEJS_16_X.name: { +// return 'lambda.Runtime.NODEJS_16_X'; +// } +// case Runtime.NODEJS_18_X.name: { +// return 'lambda.Runtime.NODEJS_18_X'; +// } +// case Runtime.PYTHON_3_9.name: { +// return 'lambda.Runtime.PYTHON_3_9'; +// } +// case Runtime.PYTHON_3_10.name: { +// return 'lambda.Runtime.PYTHON_3_10'; +// } +// } +// throw new Error('Unable to convert runtime to lambda runtime'); +// } diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/config.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/config.ts index 46e604649275a..b72cceda0e8ef 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/config.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/config.ts @@ -1,6 +1,6 @@ /* eslint-disable import/no-extraneous-dependencies */ import * as path from 'path'; -import { Runtime } from 'aws-cdk-lib/aws-lambda'; +import { Runtime } from './runtime'; /** * Handler framework component types. diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts index 9735485553ef2..8e886b08dc335 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts @@ -1,17 +1,17 @@ /* eslint-disable import/no-extraneous-dependencies */ import { ExternalModule, InterfaceType, Module, TypeScriptRenderer } from '@cdklabs/typewriter'; -import { Runtime } from 'aws-cdk-lib/aws-lambda'; import * as fs from 'fs-extra'; import { CdkHandlerClassProps, CdkHandlerFrameworkClass } from './classes'; import { ComponentType, ConfigProps } from './config'; import { CONSTRUCTS_MODULE, CORE_MODULE, CUSTOM_RESOURCE_PROVIDER_BASE, CUSTOM_RESOURCE_PROVIDER_OPTIONS, LAMBDA_MODULE, PATH_MODULE, STACK } from './modules'; +import { Runtime } from './runtime'; import { RuntimeDeterminer } from './utils/runtime-determiner'; export class CdkHandlerFrameworkModule extends Module { /** * The latest nodejs runtime version available across all AWS regions */ - private static readonly DEFAULT_RUNTIME = Runtime.NODEJS_LATEST; + private static readonly DEFAULT_RUNTIME = Runtime.NODEJS_18_X; private readonly renderer = new TypeScriptRenderer(); private readonly externalModules = new Map(); @@ -41,6 +41,9 @@ export class CdkHandlerFrameworkModule extends Module { const entrypoint = component.entrypoint ?? 'index.handler'; const name = this.buildComponentName(entrypoint); + /* eslint-disable */ + console.log(name); + const props: CdkHandlerClassProps = { name, entrypoint, diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/runtime.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/runtime.ts new file mode 100644 index 0000000000000..ff6e1262d65d9 --- /dev/null +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/runtime.ts @@ -0,0 +1,78 @@ +export enum RuntimeFamily { + NODEJS, + PYTHON, +} + +/** + * Properties used to initialize a framework runtime. + */ +interface RuntimeProps { + /** + * Whether or not this runtime is deprecated. + * + * @default false + */ + readonly isDeprecated?: boolean; +} + +/** + * Custom resource framework runtimes used for code generation. + */ +export class Runtime { + /** + * The NodeJS 16.x runtime (nodejs16.x) + */ + public static readonly NODEJS_16_X = new Runtime('nodejs16.x', RuntimeFamily.NODEJS); + + /** + * The NodeJS 18.x runtime (nodejs18.x) + */ + public static readonly NODEJS_18_X = new Runtime('nodejs18.x', RuntimeFamily.NODEJS); + + /** + * The NodeJS 20.x runtime (nodejs20.x) + */ + public static readonly NODEJS_20_X = new Runtime('nodejs20.x', RuntimeFamily.NODEJS); + + /** + * The Python 3.9 runtime (python3.9) + */ + public static readonly PYTHON_3_9 = new Runtime('python3.9', RuntimeFamily.PYTHON); + + /** + * The Python 3.10 runtime (python3.10) + */ + public static readonly PYTHON_3_10 = new Runtime('python3.10', RuntimeFamily.PYTHON); + + public readonly name: string; + public readonly family: RuntimeFamily; + public readonly isDeprecated: boolean; + + private constructor(name: string, family: RuntimeFamily, props: RuntimeProps = {}) { + this.name = name; + this.family = family; + this.isDeprecated = props.isDeprecated ?? false; + } + + public runtimeEquals(other: Runtime): boolean { + return other.name === this.name && other.family === this.family; + } + + public toLambdaRuntime() { + switch (this.name) { + case 'nodejs16.x': { + return 'lambda.Runtime.NODEJS_16_X'; + } + case 'nodejs18.x': { + return 'lambda.Runtime.NODEJS_18_X'; + } + case 'python3.9': { + return 'lambda.Runtime.PYTHON_3_9'; + } + case 'python3.10': { + return 'lambda.Runtime.PYTHON_3_10'; + } + } + throw new Error('Unable to convert runtime to lambda runtime'); + } +} diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/utils/runtime-determiner.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/utils/runtime-determiner.ts index c370b3a26f382..6de061c928e6e 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/utils/runtime-determiner.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/utils/runtime-determiner.ts @@ -1,5 +1,5 @@ /* eslint-disable import/no-extraneous-dependencies */ -import { Runtime, RuntimeFamily } from 'aws-cdk-lib/aws-lambda'; +import { Runtime, RuntimeFamily } from '../runtime'; /** * A utility class used to determine the latest runtime for a specific runtime family diff --git a/packages/@aws-cdk/custom-resource-handlers/package.json b/packages/@aws-cdk/custom-resource-handlers/package.json index b8290183d4c89..7e0a748fdd879 100644 --- a/packages/@aws-cdk/custom-resource-handlers/package.json +++ b/packages/@aws-cdk/custom-resource-handlers/package.json @@ -45,7 +45,6 @@ "@aws-sdk/client-eks": "3.421.0", "@aws-sdk/client-sts": "3.421.0", "@aws-sdk/node-http-handler": "^3.370.0", - "aws-cdk-lib": "0.0.0", "@smithy/util-stream": "^2.0.20", "@types/jest": "^29.5.8", "aws-sdk-client-mock": "^3.0.0", diff --git a/packages/aws-cdk-lib/aws-certificatemanager/lib/dns-validated-certificate.ts b/packages/aws-cdk-lib/aws-certificatemanager/lib/dns-validated-certificate.ts index 7f730a28eab42..0ea76195a261d 100644 --- a/packages/aws-cdk-lib/aws-certificatemanager/lib/dns-validated-certificate.ts +++ b/packages/aws-cdk-lib/aws-certificatemanager/lib/dns-validated-certificate.ts @@ -5,7 +5,7 @@ import * as iam from '../../aws-iam'; import * as route53 from '../../aws-route53'; import * as cdk from '../../core'; import { Token } from '../../core'; -import { CertificateRequestFunction } from '../../custom-resource-handlers/dist/aws-certificatemanager/certificate-request-provider.generated'; +import { CertificateRequestCertificateRequestProvider } from '../../custom-resource-handlers/dist/aws-certificatemanager/certificate-request-provider.generated'; /** * Properties to create a DNS validated certificate managed by AWS Certificate Manager @@ -105,7 +105,7 @@ export class DnsValidatedCertificate extends CertificateBase implements ICertifi certificateTransparencyLoggingPreference = props.transparencyLoggingEnabled ? 'ENABLED' : 'DISABLED'; } - const requestorFunction = new CertificateRequestFunction(this, 'CertificateRequestorFunction', { + const requestorFunction = new CertificateRequestCertificateRequestProvider(this, 'CertificateRequestorFunction', { timeout: cdk.Duration.minutes(15), role: props.customResourceRole, }); diff --git a/packages/aws-cdk-lib/aws-dynamodb/lib/replica-provider.ts b/packages/aws-cdk-lib/aws-dynamodb/lib/replica-provider.ts index fc3329ad1c998..023a14145666d 100644 --- a/packages/aws-cdk-lib/aws-dynamodb/lib/replica-provider.ts +++ b/packages/aws-cdk-lib/aws-dynamodb/lib/replica-provider.ts @@ -1,8 +1,8 @@ -import * as path from 'path'; import { Construct } from 'constructs'; import * as iam from '../../aws-iam'; import * as lambda from '../../aws-lambda'; import { Aws, Duration, NestedStack, Stack } from '../../core'; +import { ReplicaOnEventProvider, ReplicaIsCompleteProvider } from '../../custom-resource-handlers/dist/aws-dynamodb/replica-provider.generated'; import * as cr from '../../custom-resources'; /** @@ -55,21 +55,13 @@ export class ReplicaProvider extends NestedStack { private constructor(scope: Construct, id: string, props: ReplicaProviderProps) { super(scope, id); - const code = lambda.Code.fromAsset(path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-dynamodb', 'replica-handler')); - // Issues UpdateTable API calls - this.onEventHandler = new lambda.Function(this, 'OnEventHandler', { - code, - runtime: lambda.Runtime.NODEJS_18_X, - handler: 'index.onEventHandler', + this.onEventHandler = new ReplicaOnEventProvider(this, 'OnEventHandler', { timeout: Duration.minutes(5), }); // Checks if table is back to `ACTIVE` state - this.isCompleteHandler = new lambda.Function(this, 'IsCompleteHandler', { - code, - runtime: lambda.Runtime.NODEJS_18_X, - handler: 'index.isCompleteHandler', + this.isCompleteHandler = new ReplicaIsCompleteProvider(this, 'IsCompleteHandler', { timeout: Duration.seconds(30), }); diff --git a/packages/aws-cdk-lib/aws-ec2/lib/vpc.ts b/packages/aws-cdk-lib/aws-ec2/lib/vpc.ts index 536a9fb8fd5aa..c5f03e5f21df6 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/vpc.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/vpc.ts @@ -1,4 +1,3 @@ -import * as path from 'path'; import { Construct, Dependable, DependencyGroup, IConstruct, IDependable, Node } from 'constructs'; import { ClientVpnEndpoint, ClientVpnEndpointOptions } from './client-vpn-endpoint'; import { @@ -17,8 +16,9 @@ import { EnableVpnGatewayOptions, VpnConnection, VpnConnectionOptions, VpnConnec import * as cxschema from '../../cloud-assembly-schema'; import { Arn, Annotations, ContextProvider, - IResource, Lazy, Resource, Stack, Token, Tags, Names, CustomResourceProvider, CustomResourceProviderRuntime, CustomResource, FeatureFlags, + IResource, Lazy, Resource, Stack, Token, Tags, Names, CustomResource, FeatureFlags, } from '../../core'; +import { RestrictDefaultSgProvider } from '../../custom-resource-handlers/dist/aws-ec2/restrict-default-sg-provider.generated'; import * as cxapi from '../../cx-api'; import { EC2_RESTRICT_DEFAULT_SECURITY_GROUP } from '../../cx-api'; @@ -1698,9 +1698,7 @@ export class Vpc extends VpcBase { private restrictDefaultSecurityGroup(): void { const id = 'Custom::VpcRestrictDefaultSG'; - const provider = CustomResourceProvider.getOrCreateProvider(this, id, { - codeDirectory: path.join(__dirname, '..', '..', 'custom-resource-handlers', 'dist', 'aws-ec2', 'restrict-default-security-group-handler'), - runtime: CustomResourceProviderRuntime.NODEJS_18_X, + const provider = RestrictDefaultSgProvider.getOrCreateProvider(this, id, { description: 'Lambda function for removing all inbound/outbound rules from the VPC default security group', }); provider.addToRolePolicy({ diff --git a/packages/aws-cdk-lib/aws-eks/lib/cluster-resource-provider.ts b/packages/aws-cdk-lib/aws-eks/lib/cluster-resource-provider.ts index eab87cae1aa21..2d2ac695f1ad4 100644 --- a/packages/aws-cdk-lib/aws-eks/lib/cluster-resource-provider.ts +++ b/packages/aws-cdk-lib/aws-eks/lib/cluster-resource-provider.ts @@ -2,7 +2,7 @@ import { Construct } from 'constructs'; import * as ec2 from '../../aws-ec2'; import * as lambda from '../../aws-lambda'; import { Duration, NestedStack, Stack } from '../../core'; -import { OnEventFunction, IsCompleteFunction } from '../../custom-resource-handlers/dist/aws-eks/cluster-resource-provider.generated'; +import { ClusterResourceOnEventProvider, ClusterResourceIsCompleteProvider } from '../../custom-resource-handlers/dist/aws-eks/cluster-resource-provider.generated'; import * as cr from '../../custom-resources'; import { NodeProxyAgentLayer } from '../../lambda-layer-node-proxy-agent'; @@ -64,7 +64,7 @@ export class ClusterResourceProvider extends NestedStack { // The NPM dependency proxy-agent is required in order to support proxy routing with the AWS JS SDK. const nodeProxyAgentLayer = new NodeProxyAgentLayer(this, 'NodeProxyAgentLayer'); - const onEvent = new OnEventFunction(this, 'OnEventHandler', { + const onEvent = new ClusterResourceOnEventProvider(this, 'OnEventHandler', { description: 'onEvent handler for EKS cluster resource provider', environment: { AWS_STS_REGIONAL_ENDPOINTS: 'regional', @@ -78,7 +78,7 @@ export class ClusterResourceProvider extends NestedStack { layers: props.onEventLayer ? [props.onEventLayer] : [nodeProxyAgentLayer], }); - const isComplete = new IsCompleteFunction(this, 'IsCompleteHandler', { + const isComplete = new ClusterResourceIsCompleteProvider(this, 'IsCompleteHandler', { description: 'isComplete handler for EKS cluster resource provider', environment: { AWS_STS_REGIONAL_ENDPOINTS: 'regional', diff --git a/packages/aws-cdk-lib/aws-eks/lib/kubectl-provider.ts b/packages/aws-cdk-lib/aws-eks/lib/kubectl-provider.ts index ceab1298df378..50b414b43a983 100644 --- a/packages/aws-cdk-lib/aws-eks/lib/kubectl-provider.ts +++ b/packages/aws-cdk-lib/aws-eks/lib/kubectl-provider.ts @@ -2,7 +2,7 @@ import { Construct, IConstruct } from 'constructs'; import { ICluster, Cluster } from './cluster'; import * as iam from '../../aws-iam'; import { Duration, Stack, NestedStack, Names, CfnCondition, Fn, Aws } from '../../core'; -import { KubectlFunction } from '../../custom-resource-handlers/dist/aws-eks/kubectl-provider.generated'; +import { KubectlProvider as _KubectlProvider } from '../../custom-resource-handlers/dist/aws-eks/kubectl-provider.generated'; import * as cr from '../../custom-resources'; import { AwsCliLayer } from '../../lambda-layer-awscli'; import { KubectlLayer } from '../../lambda-layer-kubectl'; @@ -132,7 +132,7 @@ export class KubectlProvider extends NestedStack implements IKubectlProvider { const memorySize = cluster.kubectlMemory ? cluster.kubectlMemory.toMebibytes() : 1024; - const handler = new KubectlFunction(this, 'Handler', { + const handler = new _KubectlProvider(this, 'Handler', { timeout: Duration.minutes(15), description: 'onEvent handler for EKS kubectl resource provider', memorySize, diff --git a/packages/aws-cdk-lib/aws-events-targets/lib/aws-api.ts b/packages/aws-cdk-lib/aws-events-targets/lib/aws-api.ts index a515a2feeb602..f4eadb3365d17 100644 --- a/packages/aws-cdk-lib/aws-events-targets/lib/aws-api.ts +++ b/packages/aws-cdk-lib/aws-events-targets/lib/aws-api.ts @@ -4,7 +4,7 @@ import * as events from '../../aws-events'; import * as iam from '../../aws-iam'; import * as lambda from '../../aws-lambda'; import { Annotations, Duration } from '../../core'; -import { AwsApiFunction } from '../../custom-resource-handlers/dist/aws-events-targets/aws-api-provider.generated'; +import { AwsApiProvider } from '../../custom-resource-handlers/dist/aws-events-targets/aws-api-provider.generated'; /** * AWS SDK service metadata. @@ -81,7 +81,7 @@ export class AwsApi implements events.IRuleTarget { * result from an EventBridge event. */ public bind(rule: events.IRule, id?: string): events.RuleTargetConfig { - const handler = new AwsApiFunction(rule as events.Rule, `${rule.node.id}${id}Handler`, { + const handler = new AwsApiProvider(rule as events.Rule, `${rule.node.id}${id}Handler`, { timeout: Duration.seconds(60), memorySize: 256, uuid: 'b4cf1abd-4e4f-4bc6-9944-1af7ccd9ec37', diff --git a/packages/aws-cdk-lib/aws-s3-deployment/lib/bucket-deployment.ts b/packages/aws-cdk-lib/aws-s3-deployment/lib/bucket-deployment.ts index d5ba1f195e15e..86a5005b69900 100644 --- a/packages/aws-cdk-lib/aws-s3-deployment/lib/bucket-deployment.ts +++ b/packages/aws-cdk-lib/aws-s3-deployment/lib/bucket-deployment.ts @@ -11,7 +11,7 @@ import * as lambda from '../../aws-lambda'; import * as logs from '../../aws-logs'; import * as s3 from '../../aws-s3'; import * as cdk from '../../core'; -import { BucketDeploymentFunction } from '../../custom-resource-handlers/dist/aws-s3-deployment/bucket-deployment-provider.generated'; +import { BucketDeploymentProvider } from '../../custom-resource-handlers/dist/aws-s3-deployment/bucket-deployment-provider.generated'; import { AwsCliLayer } from '../../lambda-layer-awscli'; // tag key has a limit of 128 characters @@ -316,7 +316,7 @@ export class BucketDeployment extends Construct { } const mountPath = `/mnt${accessPointPath}`; - const handler = new BucketDeploymentFunction(this, 'CustomResourceHandler', { + const handler = new BucketDeploymentProvider(this, 'CustomResourceHandler', { uuid: this.renderSingletonUuid(props.memoryLimit, props.ephemeralStorageSize, props.vpc), layers: [new AwsCliLayer(this, 'AwsCliLayer')], environment: { diff --git a/packages/aws-cdk-lib/aws-ses/lib/receipt-rule.ts b/packages/aws-cdk-lib/aws-ses/lib/receipt-rule.ts index 2ae395cf0da63..b15cd902efc96 100644 --- a/packages/aws-cdk-lib/aws-ses/lib/receipt-rule.ts +++ b/packages/aws-cdk-lib/aws-ses/lib/receipt-rule.ts @@ -4,7 +4,7 @@ import { IReceiptRuleSet } from './receipt-rule-set'; import { CfnReceiptRule } from './ses.generated'; import * as iam from '../../aws-iam'; import { Aws, IResource, Lazy, Resource } from '../../core'; -import { DropSpamFunction } from '../../custom-resource-handlers/dist/aws-ses/drop-spam-provider.generated'; +import { DropSpamProvider } from '../../custom-resource-handlers/dist/aws-ses/drop-spam-provider.generated'; /** * A receipt rule. @@ -170,7 +170,7 @@ export class DropSpamReceiptRule extends Construct { constructor(scope: Construct, id: string, props: DropSpamReceiptRuleProps) { super(scope, id); - const fn = new DropSpamFunction(this, 'Function', { + const fn = new DropSpamProvider(this, 'Function', { uuid: '224e77f9-a32e-4b4d-ac32-983477abba16', }); diff --git a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/emrcontainers/start-job-run.ts b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/emrcontainers/start-job-run.ts index 71bfd53b388df..9ef5938ad72b6 100644 --- a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/emrcontainers/start-job-run.ts +++ b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/emrcontainers/start-job-run.ts @@ -5,7 +5,7 @@ import * as s3 from '../../../aws-s3'; import * as sfn from '../../../aws-stepfunctions'; import { TaskInput } from '../../../aws-stepfunctions'; import * as cdk from '../../../core'; -import { RolePolicyFunction } from '../../../custom-resource-handlers/dist/aws-stepfunctions-tasks/role-policy-provider.generated'; +import { RolePolicyProvider } from '../../../custom-resource-handlers/dist/aws-stepfunctions-tasks/role-policy-provider.generated'; import * as cr from '../../../custom-resources'; import * as awscli from '../../../lambda-layer-awscli'; import { integrationResourceArn, validatePatternSupported } from '../private/task-utils'; @@ -349,7 +349,7 @@ export class EmrContainersStartJobRun extends sfn.TaskStateBase implements iam.I * Commands available through CLI: https://awscli.amazonaws.com/v2/documentation/api/latest/reference/emr-containers/index.html */ const cliLayer = new awscli.AwsCliLayer(this, 'awsclilayer'); - const shellCliLambda = new RolePolicyFunction(this, 'Call Update-Role-Trust-Policy', { + const shellCliLambda = new RolePolicyProvider(this, 'Call Update-Role-Trust-Policy', { uuid: '8693BB64-9689-44B6-9AAF-B0CC9EB8757C', timeout: cdk.Duration.seconds(30), memorySize: 256, diff --git a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/evaluate-expression.ts b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/evaluate-expression.ts index eef863729fea6..1edf51f51117b 100644 --- a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/evaluate-expression.ts +++ b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/evaluate-expression.ts @@ -2,7 +2,7 @@ import { Construct } from 'constructs'; import * as iam from '../../aws-iam'; import * as lambda from '../../aws-lambda'; import * as sfn from '../../aws-stepfunctions'; -import { EvalNodeJsFunction } from '../../custom-resource-handlers/dist/aws-stepfunctions-tasks/eval-nodejs-provider.generated'; +import { EvalNodejsProvider } from '../../custom-resource-handlers/dist/aws-stepfunctions-tasks/eval-nodejs-provider.generated'; /** * Properties for EvaluateExpression @@ -116,7 +116,7 @@ function createEvalFn(runtime: lambda.Runtime | undefined, scope: Construct) { throw new Error(`The runtime ${runtime?.name} is currently not supported.`); } - return new EvalNodeJsFunction(scope, 'EvalFunction', { + return new EvalNodejsProvider(scope, 'EvalFunction', { uuid, lambdaPurpose, }); diff --git a/packages/aws-cdk-lib/core/lib/custom-resource-provider/cross-region-export-providers/export-reader-provider.ts b/packages/aws-cdk-lib/core/lib/custom-resource-provider/cross-region-export-providers/export-reader-provider.ts index 4962e48fb2756..44f54beb574ee 100644 --- a/packages/aws-cdk-lib/core/lib/custom-resource-provider/cross-region-export-providers/export-reader-provider.ts +++ b/packages/aws-cdk-lib/core/lib/custom-resource-provider/cross-region-export-providers/export-reader-provider.ts @@ -1,6 +1,6 @@ import { Construct } from 'constructs'; import { SSM_EXPORT_PATH_PREFIX, ExportReaderCRProps, CrossRegionExports } from './types'; -import { CrossRegionReaderProvider } from '../../../dist/core/cross-region-ssm-reader-provider.generated'; +import { CrossRegionSsmReaderProvider } from '../../../dist/core/cross-region-ssm-reader-provider.generated'; import { CfnResource } from '../../cfn-resource'; import { CustomResource } from '../../custom-resource'; import { Lazy } from '../../lazy'; @@ -34,7 +34,7 @@ export class ExportReader extends Construct { const stack = Stack.of(this); const resourceType = 'Custom::CrossRegionExportReader'; - const serviceToken = CrossRegionReaderProvider.getOrCreate(this, resourceType, { + const serviceToken = CrossRegionSsmReaderProvider.getOrCreate(this, resourceType, { policyStatements: [{ Effect: 'Allow', Resource: stack.formatArn({ diff --git a/packages/aws-cdk-lib/core/lib/custom-resource-provider/cross-region-export-providers/export-writer-provider.ts b/packages/aws-cdk-lib/core/lib/custom-resource-provider/cross-region-export-providers/export-writer-provider.ts index 05f7bbe76abfe..50bdd0b8f0fd2 100644 --- a/packages/aws-cdk-lib/core/lib/custom-resource-provider/cross-region-export-providers/export-writer-provider.ts +++ b/packages/aws-cdk-lib/core/lib/custom-resource-provider/cross-region-export-providers/export-writer-provider.ts @@ -1,7 +1,7 @@ import { Construct } from 'constructs'; import { ExportReader } from './export-reader-provider'; import { CrossRegionExports, SSM_EXPORT_PATH_PREFIX, ExportWriterCRProps } from './types'; -import { CrossRegionWriterProvider } from '../../../dist/core/cross-region-ssm-writer-provider.generated'; +import { CrossRegionSsmWriterProvider } from '../../../dist/core/cross-region-ssm-writer-provider.generated'; import { CfnDynamicReference, CfnDynamicReferenceService } from '../../cfn-dynamic-reference'; import { CustomResource } from '../../custom-resource'; import { Lazy } from '../../lazy'; @@ -28,7 +28,7 @@ export interface ExportWriterProps { * Create our own CustomResourceProvider so that we can add a single policy * with a list of ARNs instead of having to create a separate policy statement per ARN. */ -class CRProvider extends CrossRegionWriterProvider { +class CRProvider extends CrossRegionSsmWriterProvider { public static getOrCreateProvider(scope: Construct, uniqueid: string, props?: CustomResourceProviderOptions): CRProvider { const id = `${uniqueid}CustomResourceProvider`; const stack = Stack.of(scope); diff --git a/packages/aws-cdk-lib/core/lib/private/cfn-utils-provider.ts b/packages/aws-cdk-lib/core/lib/private/cfn-utils-provider.ts index dd785612ac2d6..f838c8576cf32 100644 --- a/packages/aws-cdk-lib/core/lib/private/cfn-utils-provider.ts +++ b/packages/aws-cdk-lib/core/lib/private/cfn-utils-provider.ts @@ -1,6 +1,6 @@ import { Construct } from 'constructs'; import { CfnUtilsResourceType } from './cfn-utils-provider/consts'; -import { CdkCfnUtilsProvider } from '../../dist/core/cfn-utils-provider.generated'; +import { CfnUtilsProvider as _CfnUtilsProvider } from '../../dist/core/cfn-utils-provider.generated'; import { CustomResource } from '../custom-resource'; /** @@ -8,7 +8,7 @@ import { CustomResource } from '../custom-resource'; */ export class CfnUtilsProvider extends Construct { public static getOrCreate(scope: Construct) { - return CdkCfnUtilsProvider.getOrCreate(scope, 'AWSCDKCfnUtilsProvider'); + return _CfnUtilsProvider.getOrCreate(scope, 'AWSCDKCfnUtilsProvider'); } } diff --git a/packages/aws-cdk-lib/custom-resources/lib/aws-custom-resource/aws-custom-resource.ts b/packages/aws-cdk-lib/custom-resources/lib/aws-custom-resource/aws-custom-resource.ts index f4f2cb6d1622a..0744bf629a1ce 100644 --- a/packages/aws-cdk-lib/custom-resources/lib/aws-custom-resource/aws-custom-resource.ts +++ b/packages/aws-cdk-lib/custom-resources/lib/aws-custom-resource/aws-custom-resource.ts @@ -4,7 +4,7 @@ import * as iam from '../../../aws-iam'; import * as logs from '../../../aws-logs'; import * as cdk from '../../../core'; import { Annotations } from '../../../core'; -import { AwsCustomResourceFunction } from '../../../custom-resource-handlers/dist/custom-resources/aws-custom-resource-provider.generated'; +import { AwsCustomResourceProvider } from '../../../custom-resource-handlers/dist/custom-resources/aws-custom-resource-provider.generated'; import * as cxapi from '../../../cx-api'; import { awsSdkToIamAction } from '../helpers-internal/sdk-info'; @@ -445,7 +445,7 @@ export class AwsCustomResource extends Construct implements iam.IGrantable { this.props = props; - const provider = new AwsCustomResourceFunction(this, 'Provider', { + const provider = new AwsCustomResourceProvider(this, 'Provider', { uuid: AwsCustomResource.PROVIDER_FUNCTION_UUID, lambdaPurpose: 'AWS', timeout: props.timeout || cdk.Duration.minutes(2), diff --git a/packages/aws-cdk-lib/pipelines/lib/private/application-security-check.ts b/packages/aws-cdk-lib/pipelines/lib/private/application-security-check.ts index b625862d9eea2..fa38ed6fb768a 100644 --- a/packages/aws-cdk-lib/pipelines/lib/private/application-security-check.ts +++ b/packages/aws-cdk-lib/pipelines/lib/private/application-security-check.ts @@ -5,7 +5,7 @@ import * as cp from '../../../aws-codepipeline'; import * as iam from '../../../aws-iam'; import * as lambda from '../../../aws-lambda'; import { Duration, Tags } from '../../../core'; -import { ApproveLambdaFunction } from '../../../custom-resource-handlers/dist/pipelines/approve-lambda.generated'; +import { ApproveLambdaProvider } from '../../../custom-resource-handlers/dist/pipelines/approve-lambda.generated'; /** * Properties for an ApplicationSecurityCheck @@ -57,7 +57,7 @@ export class ApplicationSecurityCheck extends Construct { includeResourceTypes: ['AWS::CodePipeline::Pipeline'], }); - this.preApproveLambda = new ApproveLambdaFunction(this, 'CDKPipelinesAutoApprove', { + this.preApproveLambda = new ApproveLambdaProvider(this, 'CDKPipelinesAutoApprove', { timeout: Duration.minutes(5), }); From 972836b7088d990488a40bed659ee5e55c84b537 Mon Sep 17 00:00:00 2001 From: Francis Date: Fri, 8 Dec 2023 18:20:40 -0800 Subject: [PATCH 44/73] remove console log Signed-off-by: Francis --- .../lib/custom-resource-framework/framework.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts index 8e886b08dc335..0db14d14b3212 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts @@ -41,9 +41,6 @@ export class CdkHandlerFrameworkModule extends Module { const entrypoint = component.entrypoint ?? 'index.handler'; const name = this.buildComponentName(entrypoint); - /* eslint-disable */ - console.log(name); - const props: CdkHandlerClassProps = { name, entrypoint, From 7b8723104b5578be78e259e515bdad48238dbe0f Mon Sep 17 00:00:00 2001 From: Francis Date: Fri, 8 Dec 2023 18:24:25 -0800 Subject: [PATCH 45/73] drain hook revert Signed-off-by: Francis --- .../aws-cdk-lib/aws-ecs/lib/drain-hook/instance-drain-hook.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/aws-cdk-lib/aws-ecs/lib/drain-hook/instance-drain-hook.ts b/packages/aws-cdk-lib/aws-ecs/lib/drain-hook/instance-drain-hook.ts index b3217011ebea7..3390e47001cc0 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/drain-hook/instance-drain-hook.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/drain-hook/instance-drain-hook.ts @@ -61,9 +61,9 @@ export class InstanceDrainHook extends Construct { // Invoke Lambda via SNS Topic const fn = new lambda.Function(this, 'Function', { - code: lambda.Code.inline(fs.readFileSync(path.join(__dirname, 'lambda-source', 'index.py'), { encoding: 'utf-8' })), + code: lambda.Code.fromInline(fs.readFileSync(path.join(__dirname, '..', '..', '..', 'custom-resource-handlers', 'dist', 'aws-ecs', 'lambda-source', 'index.py'), { encoding: 'utf-8' })), handler: 'index.lambda_handler', - runtime: lambda.Runtime.PYTHON_3_6, + runtime: lambda.Runtime.PYTHON_3_9, // Timeout: some extra margin for additional API calls made by the Lambda, // up to a maximum of 15 minutes. timeout: cdk.Duration.seconds(Math.min(drainTime.toSeconds() + 10, 900)), From c4a20ba963a5958b56626a716d70c58bec06dcd3 Mon Sep 17 00:00:00 2001 From: Francis Date: Sun, 10 Dec 2023 14:27:54 -0800 Subject: [PATCH 46/73] runtimes determiner testing Signed-off-by: Francis --- .../classes.ts | 0 .../config.ts | 0 .../framework.ts | 0 .../modules.ts | 0 .../runtime.ts | 55 +++++++++++++ .../utils/runtime-determiner.ts | 0 .../scripts/generate.ts | 4 +- .../utils/runtime-determiner.test.ts | 79 +++++++++++++++++++ 8 files changed, 136 insertions(+), 2 deletions(-) rename packages/@aws-cdk/custom-resource-handlers/lib/{custom-resource-framework => custom-resources-framework}/classes.ts (100%) rename packages/@aws-cdk/custom-resource-handlers/lib/{custom-resource-framework => custom-resources-framework}/config.ts (100%) rename packages/@aws-cdk/custom-resource-handlers/lib/{custom-resource-framework => custom-resources-framework}/framework.ts (100%) rename packages/@aws-cdk/custom-resource-handlers/lib/{custom-resource-framework => custom-resources-framework}/modules.ts (100%) rename packages/@aws-cdk/custom-resource-handlers/lib/{custom-resource-framework => custom-resources-framework}/runtime.ts (58%) rename packages/@aws-cdk/custom-resource-handlers/lib/{custom-resource-framework => custom-resources-framework}/utils/runtime-determiner.ts (100%) create mode 100644 packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/utils/runtime-determiner.test.ts diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts similarity index 100% rename from packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/classes.ts rename to packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/config.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/config.ts similarity index 100% rename from packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/config.ts rename to packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/config.ts diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/framework.ts similarity index 100% rename from packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/framework.ts rename to packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/framework.ts diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/modules.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/modules.ts similarity index 100% rename from packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/modules.ts rename to packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/modules.ts diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/runtime.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/runtime.ts similarity index 58% rename from packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/runtime.ts rename to packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/runtime.ts index ff6e1262d65d9..eecd73d989724 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/runtime.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/runtime.ts @@ -1,6 +1,8 @@ export enum RuntimeFamily { NODEJS, PYTHON, + JAVA, + RUBY, } /** @@ -19,6 +21,20 @@ interface RuntimeProps { * Custom resource framework runtimes used for code generation. */ export class Runtime { + /** + * The NodeJs 14.x runtime (nodejs14.x) + */ + public static readonly NODEJS_12_X = new Runtime('nodejs12.x', RuntimeFamily.NODEJS, { + isDeprecated: true, + }); + + /** + * The NodeJs 14.x runtime (nodejs14.x) + */ + public static readonly NODEJS_14_X = new Runtime('nodejs14.x', RuntimeFamily.NODEJS, { + isDeprecated: true, + }); + /** * The NodeJS 16.x runtime (nodejs16.x) */ @@ -34,6 +50,25 @@ export class Runtime { */ public static readonly NODEJS_20_X = new Runtime('nodejs20.x', RuntimeFamily.NODEJS); + /** + * The Python 2.7 runtime (python2.7) + */ + public static readonly PYTHON_2_7 = new Runtime('python2.7', RuntimeFamily.PYTHON, { + isDeprecated: true, + }); + + /** + * The Python 3.6 runtime (python3.6) + */ + public static readonly PYTHON_3_6 = new Runtime('python3.6', RuntimeFamily.PYTHON, { + isDeprecated: true, + }); + + /** + * The Python 3.7 runtime (python3.7) + */ + public static readonly PYTHON_3_7 = new Runtime('python3.7', RuntimeFamily.PYTHON); + /** * The Python 3.9 runtime (python3.9) */ @@ -44,6 +79,26 @@ export class Runtime { */ public static readonly PYTHON_3_10 = new Runtime('python3.10', RuntimeFamily.PYTHON); + /** + * The Python 3.11 runtime (python3.11) + */ + public static readonly PYTHON_3_11 = new Runtime('python3.11', RuntimeFamily.PYTHON); + + /** + * The Python 3.12 runtime (python3.12) + */ + public static readonly PYTHON_3_12 = new Runtime('python3.12', RuntimeFamily.PYTHON); + + /** + * The Java 17 runtime (java17) + */ + public static readonly JAVA_17 = new Runtime('java17', RuntimeFamily.JAVA); + + /** + * The Ruby 3.2 runtime (ruby3.2) + */ + public static readonly RUBY_3_2 = new Runtime('ruby3.2', RuntimeFamily.RUBY); + public readonly name: string; public readonly family: RuntimeFamily; public readonly isDeprecated: boolean; diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/utils/runtime-determiner.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/utils/runtime-determiner.ts similarity index 100% rename from packages/@aws-cdk/custom-resource-handlers/lib/custom-resource-framework/utils/runtime-determiner.ts rename to packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/utils/runtime-determiner.ts diff --git a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts index f3a80606a84d5..6777087d1a83c 100644 --- a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts +++ b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts @@ -1,8 +1,8 @@ import * as fs from 'fs'; import * as path from 'path'; import * as esbuild from 'esbuild'; -import { config, ConfigProps } from '../lib/custom-resource-framework/config'; -import { CdkHandlerFrameworkModule } from '../lib/custom-resource-framework/framework'; +import { config, ConfigProps } from '../lib/custom-resources-framework/config'; +import { CdkHandlerFrameworkModule } from '../lib/custom-resources-framework/framework'; const framework: { [fqn: string]: ConfigProps[] } = {}; diff --git a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/utils/runtime-determiner.test.ts b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/utils/runtime-determiner.test.ts new file mode 100644 index 0000000000000..11dd6f23d1b64 --- /dev/null +++ b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/utils/runtime-determiner.test.ts @@ -0,0 +1,79 @@ +import { Runtime } from '../../../lib/custom-resources-framework/runtime'; +import { RuntimeDeterminer } from '../../../lib/custom-resources-framework/utils/runtime-determiner'; + +const DEFAULT_RUNTIME = Runtime.NODEJS_18_X; + +describe('latest runtime', () => { + test('selects default runtime', () => { + // GIVEN + const runtimes = [Runtime.NODEJS_16_X, Runtime.PYTHON_3_12, Runtime.NODEJS_18_X]; + + // WHEN + const latestRuntime = RuntimeDeterminer.determineLatestRuntime(runtimes, DEFAULT_RUNTIME); + + // THEN + expect(latestRuntime?.runtimeEquals(DEFAULT_RUNTIME)).toEqual(true); + }); + + test('selects latest nodejs runtime', () => { + // GIVEN + const runtimes = [Runtime.NODEJS_16_X, Runtime.PYTHON_3_12, Runtime.NODEJS_14_X, Runtime.PYTHON_3_11, Runtime.NODEJS_20_X]; + + // WHEN + const latestRuntime = RuntimeDeterminer.determineLatestRuntime(runtimes, DEFAULT_RUNTIME); + + // THEN + expect(latestRuntime?.runtimeEquals(Runtime.NODEJS_20_X)).toEqual(true); + }); + + test('selects latest python runtime', () => { + // GIVEN + const runtimes = [Runtime.PYTHON_3_10, Runtime.PYTHON_3_11, Runtime.PYTHON_3_7]; + + // WHEN + const latestRuntime = RuntimeDeterminer.determineLatestRuntime(runtimes, DEFAULT_RUNTIME); + + // THEN + expect(latestRuntime?.runtimeEquals(Runtime.PYTHON_3_11)).toEqual(true); + }); + + test('throws if no runtimes are specified', () => { + // GIVEN + const runtimes = []; + + // WHEN / THEN + expect(() => { + RuntimeDeterminer.determineLatestRuntime(runtimes, DEFAULT_RUNTIME); + }).toThrow('You must specify at least one compatible runtime'); + }); + + test('throws if latest nodejs runtime is deprecated', () => { + // GIVEN + const runtimes = [Runtime.NODEJS_12_X, Runtime.NODEJS_14_X]; + + // WHEN / THEN + expect(() => { + RuntimeDeterminer.determineLatestRuntime(runtimes, DEFAULT_RUNTIME); + }).toThrow(`Latest nodejs runtime ${Runtime.NODEJS_14_X} is deprecated. You must upgrade to the latest code compatible nodejs runtime`); + }); + + test('throws if latest python runtime is deprecated', () => { + // GIVEN + const runtimes = [Runtime.PYTHON_2_7, Runtime.PYTHON_3_6]; + + // WHEN / THEN + expect(() => { + RuntimeDeterminer.determineLatestRuntime(runtimes, DEFAULT_RUNTIME); + }).toThrow(`Latest python runtime ${Runtime.PYTHON_3_6} is deprecated. You must upgrade to the latest code compatible python runtime`); + }); + + test('throws if runtimes are neither nodejs nor python', () => { + // GIVEN + const runtimes = [Runtime.JAVA_17, Runtime.RUBY_3_2]; + + // WHEN / THEN + expect(() => { + RuntimeDeterminer.determineLatestRuntime(runtimes, DEFAULT_RUNTIME); + }).toThrow('Compatible runtimes must contain only nodejs or python runtimes'); + }); +}); \ No newline at end of file From 510bcaa92c2d5dabfc0e5297dce15b75aab539ee Mon Sep 17 00:00:00 2001 From: Francis Date: Sun, 10 Dec 2023 16:39:56 -0800 Subject: [PATCH 47/73] testing and empty readme Signed-off-by: Francis --- .../lib/custom-resources-framework/README.md | 0 .../lib/custom-resources-framework/config.ts | 2 +- .../custom-resources-framework/framework.ts | 23 +++-- .../scripts/generate.ts | 11 ++- .../cdk-custom-resource-provider-core.ts | 33 +++++++ .../expected/cdk-custom-resource-provider.ts | 31 ++++++ .../expected/cdk-function.ts | 15 +++ .../expected/cdk-singleton-function.ts | 36 +++++++ .../framework.test.ts | 98 +++++++++++++++++++ .../my-handler/index.ts | 0 10 files changed, 235 insertions(+), 14 deletions(-) create mode 100644 packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/README.md create mode 100644 packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-custom-resource-provider-core.ts create mode 100644 packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-custom-resource-provider.ts create mode 100644 packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-function.ts create mode 100644 packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-singleton-function.ts create mode 100644 packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/framework.test.ts create mode 100644 packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/my-handler/index.ts diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/README.md b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/README.md new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/config.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/config.ts index b72cceda0e8ef..5d80f76d1ae17 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/config.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/config.ts @@ -62,7 +62,7 @@ export interface ConfigProps { readonly preventMinifyAndBundle?: boolean; } -type HandlerFrameworkConfig = { [module: string]: { [identifier: string]: ConfigProps[] } }; +export type HandlerFrameworkConfig = { [module: string]: { [identifier: string]: ConfigProps[] } }; export const config: HandlerFrameworkConfig = { 'aws-certificatemanager': { diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/framework.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/framework.ts index 0db14d14b3212..3c52a69b4aabc 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/framework.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/framework.ts @@ -16,13 +16,20 @@ export class CdkHandlerFrameworkModule extends Module { private readonly renderer = new TypeScriptRenderer(); private readonly externalModules = new Map(); private readonly _interfaces = new Map(); - private hasComponents = false; + private _hasComponents = false; /** * Whether the module being generated will live inside of aws-cdk-lib/core */ public readonly coreInternal: boolean; + /** + * Whether the module contains framework components + */ + public get hasComponents() { + return this._hasComponents; + } + public constructor(fqn: string) { super(fqn); this.coreInternal = fqn.includes('core'); @@ -31,12 +38,12 @@ export class CdkHandlerFrameworkModule extends Module { /** * Build a framework component inside of this module. */ - public build(component: ConfigProps, sourceCodeDirectory: string) { + public build(component: ConfigProps, codeDirectory: string) { if (component.type === ComponentType.CDK_NO_OP) { return; } - this.hasComponents = true; + this._hasComponents = true; const entrypoint = component.entrypoint ?? 'index.handler'; const name = this.buildComponentName(entrypoint); @@ -44,7 +51,7 @@ export class CdkHandlerFrameworkModule extends Module { const props: CdkHandlerClassProps = { name, entrypoint, - codeDirectory: sourceCodeDirectory, + codeDirectory, runtime: RuntimeDeterminer.determineLatestRuntime( component.compatibleRuntimes, CdkHandlerFrameworkModule.DEFAULT_RUNTIME, @@ -70,11 +77,9 @@ export class CdkHandlerFrameworkModule extends Module { /** * Render built framework into an output file. */ - public render() { - if (this.hasComponents) { - this.importExternalModules(); - fs.outputFileSync(`dist/${this.fqn}.generated.ts`, this.renderer.render(this)); - } + public render(file: string) { + this.importExternalModules(); + fs.outputFileSync(file, this.renderer.render(this)); } /** diff --git a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts index 6777087d1a83c..5991715384467 100644 --- a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts +++ b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts @@ -18,10 +18,13 @@ async function main() { } else { await minifyAndBundle(component.sourceCode, outfile); } - const sourceCodeDirectory = path.dirname(outfile).split('/').pop(); - module.build(component, `${sourceCodeDirectory}`); + const codeDirectory = path.dirname(outfile).split('/').pop(); + module.build(component, `${codeDirectory}`); + } + + if (module.hasComponents) { + module.render(`dist/${fqn}.generated.ts`); } - module.render(); } function recurse(_config: any, _path: string[]) { @@ -87,7 +90,7 @@ async function minifyAndBundle(infile: string, outfile: string) { } } -function calculateOutfile(file: string) { +export function calculateOutfile(file: string) { // turn ts extension into js extension if (file.includes('index.ts')) { file = path.join(path.dirname(file), path.basename(file, path.extname(file)) + '.js'); diff --git a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-custom-resource-provider-core.ts b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-custom-resource-provider-core.ts new file mode 100644 index 0000000000000..dce7069cfec4d --- /dev/null +++ b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-custom-resource-provider-core.ts @@ -0,0 +1,33 @@ +/* eslint-disable prettier/prettier,max-len */ +import * as path from "path"; +import { Construct } from "constructs"; +import { Stack } from "../../lib/stack"; +import { CustomResourceProviderBase } from "../../lib/custom-resource-provider/custom-resource-provider-base"; +import { CustomResourceProviderOptions } from "../../lib/custom-resource-provider/shared"; + +export class TestProvider extends CustomResourceProviderBase { + /** + * Returns a stack-level singleton ARN (service token) for the custom resource provider. + */ + public static getOrCreate(scope: Construct, uniqueid: string, props?: CustomResourceProviderOptions): string { + return this.getOrCreateProvider(scope, uniqueid, props).serviceToken; + } + + /** + * Returns a stack-level singleton for the custom resource provider. + */ + public static getOrCreateProvider(scope: Construct, uniqueid: string, props?: CustomResourceProviderOptions): TestProvider { + const id = `${uniqueid}CustomResourceProvider`; + const stack = Stack.of(scope); + const existing = stack.node.tryFindChild(id) as TestProvider; + return existing ?? new TestProvider(stack, id, props); + } + + public constructor(scope: Construct, id: string, props?: CustomResourceProviderOptions) { + super(scope, id, { + ...props, + "codeDirectory": path.join(__dirname, 'my-handler'), + "runtimeName": "nodejs18.x" + }); + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-custom-resource-provider.ts b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-custom-resource-provider.ts new file mode 100644 index 0000000000000..dfecaf43427eb --- /dev/null +++ b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-custom-resource-provider.ts @@ -0,0 +1,31 @@ +/* eslint-disable prettier/prettier,max-len */ +import * as path from "path"; +import { Construct } from "constructs"; +import { Stack, CustomResourceProviderBase, CustomResourceProviderOptions } from "../../../core"; + +export class TestProvider extends CustomResourceProviderBase { + /** + * Returns a stack-level singleton ARN (service token) for the custom resource provider. + */ + public static getOrCreate(scope: Construct, uniqueid: string, props?: CustomResourceProviderOptions): string { + return this.getOrCreateProvider(scope, uniqueid, props).serviceToken; + } + + /** + * Returns a stack-level singleton for the custom resource provider. + */ + public static getOrCreateProvider(scope: Construct, uniqueid: string, props?: CustomResourceProviderOptions): TestProvider { + const id = `${uniqueid}CustomResourceProvider`; + const stack = Stack.of(scope); + const existing = stack.node.tryFindChild(id) as TestProvider; + return existing ?? new TestProvider(stack, id, props); + } + + public constructor(scope: Construct, id: string, props?: CustomResourceProviderOptions) { + super(scope, id, { + ...props, + "codeDirectory": path.join(__dirname, 'my-handler'), + "runtimeName": "nodejs18.x" + }); + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-function.ts b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-function.ts new file mode 100644 index 0000000000000..960b75644da3a --- /dev/null +++ b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-function.ts @@ -0,0 +1,15 @@ +/* eslint-disable prettier/prettier,max-len */ +import * as path from "path"; +import { Construct } from "constructs"; +import * as lambda from "../../../aws-lambda"; + +export class TestProvider extends lambda.Function { + public constructor(scope: Construct, id: string, props?: lambda.FunctionOptions) { + super(scope, id, { + ...props, + "code": lambda.Code.fromAsset(path.join(__dirname, 'my-handler')), + "handler": "index.handler", + "runtime": lambda.Runtime.NODEJS_18_X + }); + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-singleton-function.ts b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-singleton-function.ts new file mode 100644 index 0000000000000..ee592bbe6c0a2 --- /dev/null +++ b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-singleton-function.ts @@ -0,0 +1,36 @@ +/* eslint-disable prettier/prettier,max-len */ +import * as path from "path"; +import { Construct } from "constructs"; +import * as lambda from "../../../aws-lambda"; + +export class TestProvider extends lambda.SingletonFunction { + public constructor(scope: Construct, id: string, props: CdkSingletonFunctionProps) { + super(scope, id, { + ...props, + "code": lambda.Code.fromAsset(path.join(__dirname, 'my-handler')), + "handler": "index.handler", + "runtime": lambda.Runtime.NODEJS_18_X + }); + } +} + +export interface CdkSingletonFunctionProps extends lambda.FunctionOptions { + /** + * A unique identifier to identify this Lambda. + * + * The identifier should be unique across all custom resource providers. + * We recommend generating a UUID per provider. + */ + readonly uuid: string; + + /** + * A descriptive name for the purpose of this Lambda. + * + * If the Lambda does not have a physical name, this string will be + * reflected in its generated name. The combination of lambdaPurpose + * and uuid must be unique. + * + * @default SingletonLambda + */ + readonly lambdaPurpose?: string; +} \ No newline at end of file diff --git a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/framework.test.ts b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/framework.test.ts new file mode 100644 index 0000000000000..3c5633099284f --- /dev/null +++ b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/framework.test.ts @@ -0,0 +1,98 @@ +import * as os from 'os'; +import * as path from 'path'; +import * as fs from 'fs-extra'; +import { ComponentType, ConfigProps } from '../../lib/custom-resources-framework/config'; +import { CdkHandlerFrameworkModule } from '../../lib/custom-resources-framework/framework'; +import { Runtime } from '../../lib/custom-resources-framework/runtime'; +import { calculateOutfile } from '../../scripts/generate'; + +/* eslint-disable no-console */ + +const sourceCode = path.resolve(__dirname, 'my-handler', 'index.ts'); + +describe('framework', () => { + let tmpDir: string; + beforeEach(() => { + tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cdk-test')); + }); + + test('can codegen cdk function', () => { + // GIVEN + const module = new CdkHandlerFrameworkModule('cdk-testing/test-provider'); + const component: ConfigProps = { + type: ComponentType.CDK_FUNCTION, + sourceCode, + compatibleRuntimes: [Runtime.NODEJS_18_X], + }; + const outfile = calculateOutfile(sourceCode); + module.build(component, path.dirname(outfile).split('/').pop() ?? 'dist'); + + // WHEN + module.render(`${tmpDir}/result.ts`); + + // THEN + const result = fs.readFileSync(path.resolve(tmpDir, 'result.ts'), 'utf-8'); + const expected = fs.readFileSync(path.resolve(__dirname, 'expected', 'cdk-function.ts'), 'utf-8'); + expect(result).toContain(expected); + }); + + test('can codegen cdk singleton function', () => { + // GIVEN + const module = new CdkHandlerFrameworkModule('cdk-testing/test-provider'); + const component: ConfigProps = { + type: ComponentType.CDK_SINGLETON_FUNCTION, + sourceCode, + compatibleRuntimes: [Runtime.NODEJS_18_X], + }; + const outfile = calculateOutfile(sourceCode); + module.build(component, path.dirname(outfile).split('/').pop() ?? 'dist'); + + // WHEN + module.render(`${tmpDir}/result.ts`); + + // THEN + const result = fs.readFileSync(path.resolve(tmpDir, 'result.ts'), 'utf-8'); + const expected = fs.readFileSync(path.resolve(__dirname, 'expected', 'cdk-singleton-function.ts'), 'utf-8'); + expect(result).toContain(expected); + }); + + test('can codegen cdk custom resource provider for core internal', () => { + // GIVEN + const module = new CdkHandlerFrameworkModule('cdk-testing/test-provider'); + const component: ConfigProps = { + type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + sourceCode, + compatibleRuntimes: [Runtime.NODEJS_18_X], + }; + const outfile = calculateOutfile(sourceCode); + module.build(component, path.dirname(outfile).split('/').pop() ?? 'dist'); + + // WHEN + module.render(`${tmpDir}/result.ts`); + + // THEN + const result = fs.readFileSync(path.resolve(tmpDir, 'result.ts'), 'utf-8'); + const expected = fs.readFileSync(path.resolve(__dirname, 'expected', 'cdk-custom-resource-provider.ts'), 'utf-8'); + expect(result).toContain(expected); + }); + + test('can codegen cdk custom resource provider for core internal', () => { + // GIVEN + const module = new CdkHandlerFrameworkModule('core/test-provider'); + const component: ConfigProps = { + type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + sourceCode, + compatibleRuntimes: [Runtime.NODEJS_18_X], + }; + const outfile = calculateOutfile(sourceCode); + module.build(component, path.dirname(outfile).split('/').pop() ?? 'dist'); + + // WHEN + module.render(`${tmpDir}/result.ts`); + + // THEN + const result = fs.readFileSync(path.resolve(tmpDir, 'result.ts'), 'utf-8'); + const expected = fs.readFileSync(path.resolve(__dirname, 'expected', 'cdk-custom-resource-provider-core.ts'), 'utf-8'); + expect(result).toContain(expected); + }); +}); diff --git a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/my-handler/index.ts b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/my-handler/index.ts new file mode 100644 index 0000000000000..e69de29bb2d1d From a7b74e1f13ca39359df8a176798a9d1606516efb Mon Sep 17 00:00:00 2001 From: Francis Date: Sun, 10 Dec 2023 18:00:00 -0800 Subject: [PATCH 48/73] alpha Signed-off-by: Francis --- .../lib/custom-resources-framework/config.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/config.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/config.ts index 5d80f76d1ae17..b6ebd575b8a5d 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/config.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/config.ts @@ -65,6 +65,15 @@ export interface ConfigProps { export type HandlerFrameworkConfig = { [module: string]: { [identifier: string]: ConfigProps[] } }; export const config: HandlerFrameworkConfig = { + 'aws-amplify-alpha': { + 'asset-deployment-handler': [ + { + type: ComponentType.CDK_NO_OP, + sourceCode: path.resolve(__dirname, '..', 'aws-amplify-alpha', 'asset-deployment-handler', 'index.ts'), + compatibleRuntimes: [Runtime.NODEJS_18_X], + }, + ], + }, 'aws-certificatemanager': { 'certificate-request-provider': [ { @@ -204,6 +213,15 @@ export const config: HandlerFrameworkConfig = { }, ], }, + 'aws-redshift-alpha': { + 'cluster-reboot-provider': [ + { + type: ComponentType.CDK_SINGLETON_FUNCTION, + sourceCode: path.resolve(__dirname, '..', 'aws-redshift-alpha', 'cluster-parameter-change-reboot-handler', 'index.ts'), + compatibleRuntimes: [Runtime.NODEJS_18_X], + }, + ], + }, 'aws-route53': { 'cross-account-zone-delegation-provider': [ { From 228aafd434abfc8aa27051256b9804b4b88c0194 Mon Sep 17 00:00:00 2001 From: Francis Date: Sun, 10 Dec 2023 18:28:43 -0800 Subject: [PATCH 49/73] runtime deprecated Signed-off-by: Francis --- packages/aws-cdk-lib/aws-lambda/lib/runtime.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/aws-lambda/lib/runtime.ts b/packages/aws-cdk-lib/aws-lambda/lib/runtime.ts index 49d18f8a7ebf8..f21fbd0b57083 100644 --- a/packages/aws-cdk-lib/aws-lambda/lib/runtime.ts +++ b/packages/aws-cdk-lib/aws-lambda/lib/runtime.ts @@ -317,7 +317,7 @@ export class Runtime { * The custom provided runtime (provided) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest provided.al2023 runtime. */ - public static readonly PROVIDED = new Runtime('provided', RuntimeFamily.OTHER); + public static readonly PROVIDED = new Runtime('provided', RuntimeFamily.OTHER, { isDeprecated: true }); /** * The custom provided runtime with Amazon Linux 2 (provided.al2) From c3fdb61db151da8f9ee10ccfbf37ab5bb13c6a0a Mon Sep 17 00:00:00 2001 From: Francis Date: Mon, 11 Dec 2023 07:41:48 -0800 Subject: [PATCH 50/73] naming Signed-off-by: Francis --- .../lib/custom-resources-framework/classes.ts | 20 +++++++++---------- .../custom-resources-framework/framework.ts | 14 ++++++------- .../scripts/generate.ts | 4 ++-- .../framework.test.ts | 10 +++++----- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts index 5020ca804bf81..031cd9fb1e2e4 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts @@ -14,7 +14,7 @@ import { SuperInitializer, Expression, } from '@cdklabs/typewriter'; -import { CdkHandlerFrameworkModule } from './framework'; +import { CdkCustomResourceModule } from './framework'; import { CONSTRUCTS_MODULE, LAMBDA_MODULE, @@ -57,7 +57,7 @@ interface ConstructorBuildProps { /** * Initialization properties used to build a `CdkHandlerFrameworkClass` instance. */ -export interface CdkHandlerClassProps { +export interface CdkCustomResourceClassProps { /** * The name of the component class. */ @@ -80,12 +80,12 @@ export interface CdkHandlerClassProps { readonly entrypoint: string; } -export abstract class CdkHandlerFrameworkClass extends ClassType { +export abstract class CdkCustomResourceClass extends ClassType { /** * Builds a `CdkFunction` class. */ - public static buildCdkFunction(scope: CdkHandlerFrameworkModule, props: CdkHandlerClassProps): CdkHandlerFrameworkClass { - return new (class CdkFunction extends CdkHandlerFrameworkClass { + public static buildCdkFunction(scope: CdkCustomResourceModule, props: CdkCustomResourceClassProps): CdkCustomResourceClass { + return new (class CdkFunction extends CdkCustomResourceClass { protected readonly externalModules = [CONSTRUCTS_MODULE, LAMBDA_MODULE]; public constructor() { @@ -116,8 +116,8 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { /** * Builds a `CdkSingletonFunction` class. */ - public static buildCdkSingletonFunction(scope: CdkHandlerFrameworkModule, props: CdkHandlerClassProps): CdkHandlerFrameworkClass { - return new (class CdkSingletonFunction extends CdkHandlerFrameworkClass { + public static buildCdkSingletonFunction(scope: CdkCustomResourceModule, props: CdkCustomResourceClassProps): CdkCustomResourceClass { + return new (class CdkSingletonFunction extends CdkCustomResourceClass { protected readonly externalModules = [CONSTRUCTS_MODULE, LAMBDA_MODULE]; public constructor() { @@ -174,8 +174,8 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { /** * Builds a `CdkCustomResourceProvider` class. */ - public static buildCdkCustomResourceProvider(scope: CdkHandlerFrameworkModule, props: CdkHandlerClassProps): CdkHandlerFrameworkClass { - return new (class CdkCustomResourceProvider extends CdkHandlerFrameworkClass { + public static buildCdkCustomResourceProvider(scope: CdkCustomResourceModule, props: CdkCustomResourceClassProps): CdkCustomResourceClass { + return new (class CdkCustomResourceProvider extends CdkCustomResourceClass { protected readonly externalModules: ExternalModule[] = [CONSTRUCTS_MODULE]; public constructor() { @@ -274,7 +274,7 @@ export abstract class CdkHandlerFrameworkClass extends ClassType { */ protected abstract readonly externalModules: ExternalModule[]; - private getOrCreateInterface(scope: CdkHandlerFrameworkModule, spec: InterfaceSpec) { + private getOrCreateInterface(scope: CdkCustomResourceModule, spec: InterfaceSpec) { const existing = scope.getInterface(spec.name); if (existing) { return existing; diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/framework.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/framework.ts index 3c52a69b4aabc..5adf20fafca93 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/framework.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/framework.ts @@ -1,13 +1,13 @@ /* eslint-disable import/no-extraneous-dependencies */ import { ExternalModule, InterfaceType, Module, TypeScriptRenderer } from '@cdklabs/typewriter'; import * as fs from 'fs-extra'; -import { CdkHandlerClassProps, CdkHandlerFrameworkClass } from './classes'; +import { CdkCustomResourceClass, CdkCustomResourceClassProps } from './classes'; import { ComponentType, ConfigProps } from './config'; import { CONSTRUCTS_MODULE, CORE_MODULE, CUSTOM_RESOURCE_PROVIDER_BASE, CUSTOM_RESOURCE_PROVIDER_OPTIONS, LAMBDA_MODULE, PATH_MODULE, STACK } from './modules'; import { Runtime } from './runtime'; import { RuntimeDeterminer } from './utils/runtime-determiner'; -export class CdkHandlerFrameworkModule extends Module { +export class CdkCustomResourceModule extends Module { /** * The latest nodejs runtime version available across all AWS regions */ @@ -48,27 +48,27 @@ export class CdkHandlerFrameworkModule extends Module { const entrypoint = component.entrypoint ?? 'index.handler'; const name = this.buildComponentName(entrypoint); - const props: CdkHandlerClassProps = { + const props: CdkCustomResourceClassProps = { name, entrypoint, codeDirectory, runtime: RuntimeDeterminer.determineLatestRuntime( component.compatibleRuntimes, - CdkHandlerFrameworkModule.DEFAULT_RUNTIME, + CdkCustomResourceModule.DEFAULT_RUNTIME, ), }; switch (component.type) { case ComponentType.CDK_FUNCTION: { - CdkHandlerFrameworkClass.buildCdkFunction(this, props); + CdkCustomResourceClass.buildCdkFunction(this, props); break; } case ComponentType.CDK_SINGLETON_FUNCTION: { - CdkHandlerFrameworkClass.buildCdkSingletonFunction(this, props); + CdkCustomResourceClass.buildCdkSingletonFunction(this, props); break; } case ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER: { - CdkHandlerFrameworkClass.buildCdkCustomResourceProvider(this, props); + CdkCustomResourceClass.buildCdkCustomResourceProvider(this, props); break; } } diff --git a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts index 5991715384467..ea97f9a044472 100644 --- a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts +++ b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts @@ -2,14 +2,14 @@ import * as fs from 'fs'; import * as path from 'path'; import * as esbuild from 'esbuild'; import { config, ConfigProps } from '../lib/custom-resources-framework/config'; -import { CdkHandlerFrameworkModule } from '../lib/custom-resources-framework/framework'; +import { CdkCustomResourceModule } from '../lib/custom-resources-framework/framework'; const framework: { [fqn: string]: ConfigProps[] } = {}; async function main() { recurse(config, []); for (const [fqn, components] of Object.entries(framework)) { - const module = new CdkHandlerFrameworkModule(fqn); + const module = new CdkCustomResourceModule(fqn); for (const component of components) { const outfile = calculateOutfile(component.sourceCode); if (component.preventMinifyAndBundle) { diff --git a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/framework.test.ts b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/framework.test.ts index 3c5633099284f..53e213fd65267 100644 --- a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/framework.test.ts +++ b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/framework.test.ts @@ -2,7 +2,7 @@ import * as os from 'os'; import * as path from 'path'; import * as fs from 'fs-extra'; import { ComponentType, ConfigProps } from '../../lib/custom-resources-framework/config'; -import { CdkHandlerFrameworkModule } from '../../lib/custom-resources-framework/framework'; +import { CdkCustomResourceModule } from '../../lib/custom-resources-framework/framework'; import { Runtime } from '../../lib/custom-resources-framework/runtime'; import { calculateOutfile } from '../../scripts/generate'; @@ -18,7 +18,7 @@ describe('framework', () => { test('can codegen cdk function', () => { // GIVEN - const module = new CdkHandlerFrameworkModule('cdk-testing/test-provider'); + const module = new CdkCustomResourceModule('cdk-testing/test-provider'); const component: ConfigProps = { type: ComponentType.CDK_FUNCTION, sourceCode, @@ -38,7 +38,7 @@ describe('framework', () => { test('can codegen cdk singleton function', () => { // GIVEN - const module = new CdkHandlerFrameworkModule('cdk-testing/test-provider'); + const module = new CdkCustomResourceModule('cdk-testing/test-provider'); const component: ConfigProps = { type: ComponentType.CDK_SINGLETON_FUNCTION, sourceCode, @@ -58,7 +58,7 @@ describe('framework', () => { test('can codegen cdk custom resource provider for core internal', () => { // GIVEN - const module = new CdkHandlerFrameworkModule('cdk-testing/test-provider'); + const module = new CdkCustomResourceModule('cdk-testing/test-provider'); const component: ConfigProps = { type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, sourceCode, @@ -78,7 +78,7 @@ describe('framework', () => { test('can codegen cdk custom resource provider for core internal', () => { // GIVEN - const module = new CdkHandlerFrameworkModule('core/test-provider'); + const module = new CdkCustomResourceModule('core/test-provider'); const component: ConfigProps = { type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, sourceCode, From 187c7dfd795a3e76c066a49afb4ed5c8555ca7e1 Mon Sep 17 00:00:00 2001 From: Francis Date: Mon, 11 Dec 2023 10:53:18 -0800 Subject: [PATCH 51/73] refactor and naming update Signed-off-by: Francis --- .../lib/custom-resources-framework/classes.ts | 74 ++++++++++++++----- .../custom-resources-framework/framework.ts | 47 ++---------- .../lib/custom-resources-framework/modules.ts | 24 ++---- 3 files changed, 68 insertions(+), 77 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts index 031cd9fb1e2e4..5d1c04f88b4db 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts @@ -19,9 +19,8 @@ import { CONSTRUCTS_MODULE, LAMBDA_MODULE, CORE_MODULE, - STACK, - CUSTOM_RESOURCE_PROVIDER_BASE, - CUSTOM_RESOURCE_PROVIDER_OPTIONS, + CORE_INTERNAL, + PATH_MODULE, } from './modules'; import { Runtime } from './runtime'; @@ -86,7 +85,7 @@ export abstract class CdkCustomResourceClass extends ClassType { */ public static buildCdkFunction(scope: CdkCustomResourceModule, props: CdkCustomResourceClassProps): CdkCustomResourceClass { return new (class CdkFunction extends CdkCustomResourceClass { - protected readonly externalModules = [CONSTRUCTS_MODULE, LAMBDA_MODULE]; + protected readonly externalModules = [PATH_MODULE, CONSTRUCTS_MODULE, LAMBDA_MODULE]; public constructor() { super(scope, { @@ -95,7 +94,7 @@ export abstract class CdkCustomResourceClass extends ClassType { export: true, }); - this.externalModules.forEach(module => scope.addExternalModule(module)); + this.importExternalModulesInto(scope); const superProps = new ObjectLiteral([ new Splat(expr.ident('props')), @@ -118,7 +117,7 @@ export abstract class CdkCustomResourceClass extends ClassType { */ public static buildCdkSingletonFunction(scope: CdkCustomResourceModule, props: CdkCustomResourceClassProps): CdkCustomResourceClass { return new (class CdkSingletonFunction extends CdkCustomResourceClass { - protected readonly externalModules = [CONSTRUCTS_MODULE, LAMBDA_MODULE]; + protected readonly externalModules = [PATH_MODULE, CONSTRUCTS_MODULE, LAMBDA_MODULE]; public constructor() { super(scope, { @@ -127,7 +126,7 @@ export abstract class CdkCustomResourceClass extends ClassType { export: true, }); - this.externalModules.forEach(module => scope.addExternalModule(module)); + this.importExternalModulesInto(scope); const uuid: PropertySpec = { name: 'uuid', @@ -176,24 +175,19 @@ export abstract class CdkCustomResourceClass extends ClassType { */ public static buildCdkCustomResourceProvider(scope: CdkCustomResourceModule, props: CdkCustomResourceClassProps): CdkCustomResourceClass { return new (class CdkCustomResourceProvider extends CdkCustomResourceClass { - protected readonly externalModules: ExternalModule[] = [CONSTRUCTS_MODULE]; + protected readonly externalModules: ExternalModule[] = [PATH_MODULE, CONSTRUCTS_MODULE]; public constructor() { super(scope, { name: props.name, extends: scope.coreInternal - ? CUSTOM_RESOURCE_PROVIDER_BASE.CustomResourceProviderBase + ? CORE_INTERNAL.CustomResourceProviderBase : CORE_MODULE.CustomResourceProviderBase, export: true, }); - if (scope.coreInternal) { - this.externalModules.push(...[STACK, CUSTOM_RESOURCE_PROVIDER_BASE, CUSTOM_RESOURCE_PROVIDER_OPTIONS]); - } else { - this.externalModules.push(CORE_MODULE); - } - - this.externalModules.forEach(module => scope.addExternalModule(module)); + this.externalModules.push(scope.coreInternal ? CORE_INTERNAL : CORE_MODULE); + this.importExternalModulesInto(scope); const getOrCreateMethod = this.addMethod({ name: 'getOrCreate', @@ -214,7 +208,7 @@ export abstract class CdkCustomResourceClass extends ClassType { getOrCreateMethod.addParameter({ name: 'props', type: scope.coreInternal - ? CUSTOM_RESOURCE_PROVIDER_OPTIONS.CustomResourceProviderOptions + ? CORE_INTERNAL.CustomResourceProviderOptions : CORE_MODULE.CustomResourceProviderOptions, optional: true, }); @@ -241,7 +235,7 @@ export abstract class CdkCustomResourceClass extends ClassType { getOrCreateProviderMethod.addParameter({ name: 'props', type: scope.coreInternal - ? CUSTOM_RESOURCE_PROVIDER_OPTIONS.CustomResourceProviderOptions + ? CORE_INTERNAL.CustomResourceProviderOptions : CORE_MODULE.CustomResourceProviderOptions, optional: true, }); @@ -259,7 +253,7 @@ export abstract class CdkCustomResourceClass extends ClassType { ]); this.buildConstructor({ constructorPropsType: scope.coreInternal - ? CUSTOM_RESOURCE_PROVIDER_OPTIONS.CustomResourceProviderOptions + ? CORE_INTERNAL.CustomResourceProviderOptions : CORE_MODULE.CustomResourceProviderOptions, superProps, constructorVisbility: MemberVisibility.Private, @@ -274,6 +268,48 @@ export abstract class CdkCustomResourceClass extends ClassType { */ protected abstract readonly externalModules: ExternalModule[]; + private importExternalModulesInto(scope: CdkCustomResourceModule) { + for (const module of this.externalModules) { + if (!scope.hasExternalModule(module)) { + scope.addExternalModule(module); + this.importExternalModuleInto(scope, module); + } + } + } + + private importExternalModuleInto(scope: CdkCustomResourceModule, module: ExternalModule) { + switch (module.fqn) { + case PATH_MODULE.fqn: { + PATH_MODULE.import(scope, 'path'); + return; + } + case CONSTRUCTS_MODULE.fqn: { + CONSTRUCTS_MODULE.importSelective(scope, ['Construct']); + return; + } + case CORE_MODULE.fqn: { + CORE_MODULE.importSelective(scope, [ + 'Stack', + 'CustomResourceProviderBase', + 'CustomResourceProviderOptions', + ]); + return; + } + case CORE_INTERNAL.fqn: { + CORE_INTERNAL.importSelective(scope, [ + 'Stack', + 'CustomResourceProviderBase', + 'CustomResourceProviderOptions', + ]); + return; + } + case LAMBDA_MODULE.fqn: { + LAMBDA_MODULE.import(scope, 'lambda'); + return; + } + } + } + private getOrCreateInterface(scope: CdkCustomResourceModule, spec: InterfaceSpec) { const existing = scope.getInterface(spec.name); if (existing) { diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/framework.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/framework.ts index 5adf20fafca93..5ffc3a06670ec 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/framework.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/framework.ts @@ -3,7 +3,6 @@ import { ExternalModule, InterfaceType, Module, TypeScriptRenderer } from '@cdkl import * as fs from 'fs-extra'; import { CdkCustomResourceClass, CdkCustomResourceClassProps } from './classes'; import { ComponentType, ConfigProps } from './config'; -import { CONSTRUCTS_MODULE, CORE_MODULE, CUSTOM_RESOURCE_PROVIDER_BASE, CUSTOM_RESOURCE_PROVIDER_OPTIONS, LAMBDA_MODULE, PATH_MODULE, STACK } from './modules'; import { Runtime } from './runtime'; import { RuntimeDeterminer } from './utils/runtime-determiner'; @@ -75,10 +74,9 @@ export class CdkCustomResourceModule extends Module { } /** - * Render built framework into an output file. + * Render module with components into an output file. */ public render(file: string) { - this.importExternalModules(); fs.outputFileSync(file, this.renderer.render(this)); } @@ -91,6 +89,13 @@ export class CdkCustomResourceModule extends Module { } } + /** + * If an external module has been added as an import to this module. + */ + public hasExternalModule(module: ExternalModule) { + return this.externalModules.has(module.fqn); + } + /** * Register an interface with this module. */ @@ -113,40 +118,4 @@ export class CdkCustomResourceModule extends Module { )) + (handler.charAt(0).toUpperCase() + handler.slice(1)); return name.charAt(0).toUpperCase() + name.slice(1) + 'Provider'; } - - private importExternalModules() { - PATH_MODULE.import(this, 'path'); - for (const fqn of this.externalModules.keys()) { - switch (fqn) { - case CONSTRUCTS_MODULE.fqn: { - CONSTRUCTS_MODULE.importSelective(this, ['Construct']); - break; - } - case CORE_MODULE.fqn: { - CORE_MODULE.importSelective(this, [ - 'Stack', - 'CustomResourceProviderBase', - 'CustomResourceProviderOptions', - ]); - break; - } - case STACK.fqn: { - STACK.importSelective(this, ['Stack']); - break; - } - case CUSTOM_RESOURCE_PROVIDER_BASE.fqn: { - CUSTOM_RESOURCE_PROVIDER_BASE.importSelective(this, ['CustomResourceProviderBase']); - break; - } - case CUSTOM_RESOURCE_PROVIDER_OPTIONS.fqn: { - CUSTOM_RESOURCE_PROVIDER_OPTIONS.importSelective(this, ['CustomResourceProviderOptions']); - break; - } - case LAMBDA_MODULE.fqn: { - LAMBDA_MODULE.import(this, 'lambda'); - break; - } - } - } - } } diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/modules.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/modules.ts index f53bbc4883da4..7f0b1a7565490 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/modules.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/modules.ts @@ -16,6 +16,7 @@ class ConstructsModule extends ExternalModule { } class CoreModule extends ExternalModule { + public readonly Stack = Type.fromName(this, 'Stack'); public readonly CustomResourceProviderBase = Type.fromName(this, 'CustomResourceProviderBase'); public readonly CustomResourceProviderOptions = Type.fromName(this, 'CustomResourceProviderOptions'); @@ -24,25 +25,13 @@ class CoreModule extends ExternalModule { } } -class Stack extends ExternalModule { - public constructor() { - super('../../lib/stack'); - } -} - -class CustomResourceProviderBase extends ExternalModule { +class CoreInternal extends ExternalModule { + public readonly Stack = Type.fromName(this, 'Stack'); public readonly CustomResourceProviderBase = Type.fromName(this, 'CustomResourceProviderBase'); - - public constructor() { - super('../../lib/custom-resource-provider/custom-resource-provider-base'); - } -} - -class CustomResourceProviderOptions extends ExternalModule { public readonly CustomResourceProviderOptions = Type.fromName(this, 'CustomResourceProviderOptions'); public constructor() { - super('../../lib/custom-resource-provider/shared'); + super('../../lib'); } } @@ -59,8 +48,5 @@ class LambdaModule extends ExternalModule { export const PATH_MODULE = new PathModule(); export const CONSTRUCTS_MODULE = new ConstructsModule(); export const CORE_MODULE = new CoreModule(); +export const CORE_INTERNAL = new CoreInternal(); export const LAMBDA_MODULE = new LambdaModule(); - -export const STACK = new Stack(); -export const CUSTOM_RESOURCE_PROVIDER_BASE = new CustomResourceProviderBase(); -export const CUSTOM_RESOURCE_PROVIDER_OPTIONS = new CustomResourceProviderOptions(); From f857264c4177e5aa7f06b29dad906d15ef671b10 Mon Sep 17 00:00:00 2001 From: Francis Date: Mon, 11 Dec 2023 10:58:12 -0800 Subject: [PATCH 52/73] naming Signed-off-by: Francis --- .../lib/custom-resources-framework/framework.ts | 2 +- .../@aws-cdk/custom-resource-handlers/scripts/generate.ts | 2 +- .../test/custom-resources-framework/framework.test.ts | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/framework.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/framework.ts index 5ffc3a06670ec..0f628dd448593 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/framework.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/framework.ts @@ -76,7 +76,7 @@ export class CdkCustomResourceModule extends Module { /** * Render module with components into an output file. */ - public render(file: string) { + public renderTo(file: string) { fs.outputFileSync(file, this.renderer.render(this)); } diff --git a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts index ea97f9a044472..e9c46be55bd69 100644 --- a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts +++ b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts @@ -23,7 +23,7 @@ async function main() { } if (module.hasComponents) { - module.render(`dist/${fqn}.generated.ts`); + module.renderTo(`dist/${fqn}.generated.ts`); } } diff --git a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/framework.test.ts b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/framework.test.ts index 53e213fd65267..a6650b54eacf2 100644 --- a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/framework.test.ts +++ b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/framework.test.ts @@ -28,7 +28,7 @@ describe('framework', () => { module.build(component, path.dirname(outfile).split('/').pop() ?? 'dist'); // WHEN - module.render(`${tmpDir}/result.ts`); + module.renderTo(`${tmpDir}/result.ts`); // THEN const result = fs.readFileSync(path.resolve(tmpDir, 'result.ts'), 'utf-8'); @@ -48,7 +48,7 @@ describe('framework', () => { module.build(component, path.dirname(outfile).split('/').pop() ?? 'dist'); // WHEN - module.render(`${tmpDir}/result.ts`); + module.renderTo(`${tmpDir}/result.ts`); // THEN const result = fs.readFileSync(path.resolve(tmpDir, 'result.ts'), 'utf-8'); @@ -68,7 +68,7 @@ describe('framework', () => { module.build(component, path.dirname(outfile).split('/').pop() ?? 'dist'); // WHEN - module.render(`${tmpDir}/result.ts`); + module.renderTo(`${tmpDir}/result.ts`); // THEN const result = fs.readFileSync(path.resolve(tmpDir, 'result.ts'), 'utf-8'); @@ -88,7 +88,7 @@ describe('framework', () => { module.build(component, path.dirname(outfile).split('/').pop() ?? 'dist'); // WHEN - module.render(`${tmpDir}/result.ts`); + module.renderTo(`${tmpDir}/result.ts`); // THEN const result = fs.readFileSync(path.resolve(tmpDir, 'result.ts'), 'utf-8'); From 2db1c20b2327a1773ba26adc363ae4614c065c5e Mon Sep 17 00:00:00 2001 From: Francis Date: Mon, 11 Dec 2023 11:05:04 -0800 Subject: [PATCH 53/73] docstring Signed-off-by: Francis --- .../lib/custom-resources-framework/classes.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts index 5d1c04f88b4db..222227463e0c3 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts @@ -153,6 +153,9 @@ export abstract class CdkCustomResourceClass extends ClassType { export: true, extends: [LAMBDA_MODULE.FunctionOptions], properties: [uuid, lambdaPurpose], + docs: { + summary: `Initialization properties for ${this.name}`, + }, }); const superProps = new ObjectLiteral([ From 3c81f54370cea25740f472daed42ac66e95cc45b Mon Sep 17 00:00:00 2001 From: Francis Date: Mon, 11 Dec 2023 11:15:26 -0800 Subject: [PATCH 54/73] fix framework unit tests Signed-off-by: Francis --- .../expected/cdk-custom-resource-provider-core.ts | 4 +--- .../expected/cdk-singleton-function.ts | 3 +++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-custom-resource-provider-core.ts b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-custom-resource-provider-core.ts index dce7069cfec4d..40fb419be689c 100644 --- a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-custom-resource-provider-core.ts +++ b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-custom-resource-provider-core.ts @@ -1,9 +1,7 @@ /* eslint-disable prettier/prettier,max-len */ import * as path from "path"; import { Construct } from "constructs"; -import { Stack } from "../../lib/stack"; -import { CustomResourceProviderBase } from "../../lib/custom-resource-provider/custom-resource-provider-base"; -import { CustomResourceProviderOptions } from "../../lib/custom-resource-provider/shared"; +import { Stack, CustomResourceProviderBase, CustomResourceProviderOptions } from "../../lib"; export class TestProvider extends CustomResourceProviderBase { /** diff --git a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-singleton-function.ts b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-singleton-function.ts index ee592bbe6c0a2..22d09de7b1219 100644 --- a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-singleton-function.ts +++ b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-singleton-function.ts @@ -14,6 +14,9 @@ export class TestProvider extends lambda.SingletonFunction { } } +/** + * Initialization properties for TestProvider + */ export interface CdkSingletonFunctionProps extends lambda.FunctionOptions { /** * A unique identifier to identify this Lambda. From cc6934556f436d2fdd8d18ac2a0a6a9f1936ab75 Mon Sep 17 00:00:00 2001 From: Francis Date: Mon, 11 Dec 2023 14:00:33 -0800 Subject: [PATCH 55/73] fix failures Signed-off-by: Francis --- .../lib/custom-resources-framework/classes.ts | 24 ++++++++++++------- .../lib/custom-resources-framework/modules.ts | 14 ++++++++--- .../export-reader-provider.ts | 2 +- .../export-writer-provider.ts | 2 +- .../core/lib/private/cfn-utils-provider.ts | 2 +- .../airlift-custom-resource-handlers.sh | 4 ++-- 6 files changed, 31 insertions(+), 17 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts index 222227463e0c3..c8fe142d2cbda 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts @@ -19,7 +19,8 @@ import { CONSTRUCTS_MODULE, LAMBDA_MODULE, CORE_MODULE, - CORE_INTERNAL, + CORE_INTERNAL_STACK, + CORE_INTERNAL_CR_PROVIDER, PATH_MODULE, } from './modules'; import { Runtime } from './runtime'; @@ -184,12 +185,16 @@ export abstract class CdkCustomResourceClass extends ClassType { super(scope, { name: props.name, extends: scope.coreInternal - ? CORE_INTERNAL.CustomResourceProviderBase + ? CORE_INTERNAL_CR_PROVIDER.CustomResourceProviderBase : CORE_MODULE.CustomResourceProviderBase, export: true, }); - this.externalModules.push(scope.coreInternal ? CORE_INTERNAL : CORE_MODULE); + if (scope.coreInternal) { + this.externalModules.push(...[CORE_INTERNAL_STACK, CORE_INTERNAL_CR_PROVIDER]); + } else { + this.externalModules.push(CORE_MODULE); + } this.importExternalModulesInto(scope); const getOrCreateMethod = this.addMethod({ @@ -211,7 +216,7 @@ export abstract class CdkCustomResourceClass extends ClassType { getOrCreateMethod.addParameter({ name: 'props', type: scope.coreInternal - ? CORE_INTERNAL.CustomResourceProviderOptions + ? CORE_INTERNAL_CR_PROVIDER.CustomResourceProviderOptions : CORE_MODULE.CustomResourceProviderOptions, optional: true, }); @@ -238,7 +243,7 @@ export abstract class CdkCustomResourceClass extends ClassType { getOrCreateProviderMethod.addParameter({ name: 'props', type: scope.coreInternal - ? CORE_INTERNAL.CustomResourceProviderOptions + ? CORE_INTERNAL_CR_PROVIDER.CustomResourceProviderOptions : CORE_MODULE.CustomResourceProviderOptions, optional: true, }); @@ -256,7 +261,7 @@ export abstract class CdkCustomResourceClass extends ClassType { ]); this.buildConstructor({ constructorPropsType: scope.coreInternal - ? CORE_INTERNAL.CustomResourceProviderOptions + ? CORE_INTERNAL_CR_PROVIDER.CustomResourceProviderOptions : CORE_MODULE.CustomResourceProviderOptions, superProps, constructorVisbility: MemberVisibility.Private, @@ -298,9 +303,10 @@ export abstract class CdkCustomResourceClass extends ClassType { ]); return; } - case CORE_INTERNAL.fqn: { - CORE_INTERNAL.importSelective(scope, [ - 'Stack', + case CORE_INTERNAL_CR_PROVIDER.fqn: { + const stack = new ExternalModule('../../stack'); + stack.importSelective(scope, ['Stack']); + CORE_INTERNAL_CR_PROVIDER.importSelective(scope, [ 'CustomResourceProviderBase', 'CustomResourceProviderOptions', ]); diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/modules.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/modules.ts index 7f0b1a7565490..9b2f349a06cd7 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/modules.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/modules.ts @@ -25,13 +25,20 @@ class CoreModule extends ExternalModule { } } -class CoreInternal extends ExternalModule { +class CoreInternalStack extends ExternalModule { public readonly Stack = Type.fromName(this, 'Stack'); + + public constructor() { + super('../../stack'); + } +} + +class CoreInternalCustomResourceProvider extends ExternalModule { public readonly CustomResourceProviderBase = Type.fromName(this, 'CustomResourceProviderBase'); public readonly CustomResourceProviderOptions = Type.fromName(this, 'CustomResourceProviderOptions'); public constructor() { - super('../../lib'); + super('../../custom-resource-provider'); } } @@ -48,5 +55,6 @@ class LambdaModule extends ExternalModule { export const PATH_MODULE = new PathModule(); export const CONSTRUCTS_MODULE = new ConstructsModule(); export const CORE_MODULE = new CoreModule(); -export const CORE_INTERNAL = new CoreInternal(); +export const CORE_INTERNAL_STACK = new CoreInternalStack(); +export const CORE_INTERNAL_CR_PROVIDER = new CoreInternalCustomResourceProvider(); export const LAMBDA_MODULE = new LambdaModule(); diff --git a/packages/aws-cdk-lib/core/lib/custom-resource-provider/cross-region-export-providers/export-reader-provider.ts b/packages/aws-cdk-lib/core/lib/custom-resource-provider/cross-region-export-providers/export-reader-provider.ts index 44f54beb574ee..ca7efe1fe6ead 100644 --- a/packages/aws-cdk-lib/core/lib/custom-resource-provider/cross-region-export-providers/export-reader-provider.ts +++ b/packages/aws-cdk-lib/core/lib/custom-resource-provider/cross-region-export-providers/export-reader-provider.ts @@ -1,8 +1,8 @@ import { Construct } from 'constructs'; import { SSM_EXPORT_PATH_PREFIX, ExportReaderCRProps, CrossRegionExports } from './types'; -import { CrossRegionSsmReaderProvider } from '../../../dist/core/cross-region-ssm-reader-provider.generated'; import { CfnResource } from '../../cfn-resource'; import { CustomResource } from '../../custom-resource'; +import { CrossRegionSsmReaderProvider } from '../../dist/core/cross-region-ssm-reader-provider.generated'; import { Lazy } from '../../lazy'; import { Intrinsic } from '../../private/intrinsic'; import { Stack } from '../../stack'; diff --git a/packages/aws-cdk-lib/core/lib/custom-resource-provider/cross-region-export-providers/export-writer-provider.ts b/packages/aws-cdk-lib/core/lib/custom-resource-provider/cross-region-export-providers/export-writer-provider.ts index 50bdd0b8f0fd2..6c2d77b9e6bf5 100644 --- a/packages/aws-cdk-lib/core/lib/custom-resource-provider/cross-region-export-providers/export-writer-provider.ts +++ b/packages/aws-cdk-lib/core/lib/custom-resource-provider/cross-region-export-providers/export-writer-provider.ts @@ -1,9 +1,9 @@ import { Construct } from 'constructs'; import { ExportReader } from './export-reader-provider'; import { CrossRegionExports, SSM_EXPORT_PATH_PREFIX, ExportWriterCRProps } from './types'; -import { CrossRegionSsmWriterProvider } from '../../../dist/core/cross-region-ssm-writer-provider.generated'; import { CfnDynamicReference, CfnDynamicReferenceService } from '../../cfn-dynamic-reference'; import { CustomResource } from '../../custom-resource'; +import { CrossRegionSsmWriterProvider } from '../../dist/core/cross-region-ssm-writer-provider.generated'; import { Lazy } from '../../lazy'; import { Intrinsic } from '../../private/intrinsic'; import { makeUniqueId } from '../../private/uniqueid'; diff --git a/packages/aws-cdk-lib/core/lib/private/cfn-utils-provider.ts b/packages/aws-cdk-lib/core/lib/private/cfn-utils-provider.ts index f838c8576cf32..bab751211c138 100644 --- a/packages/aws-cdk-lib/core/lib/private/cfn-utils-provider.ts +++ b/packages/aws-cdk-lib/core/lib/private/cfn-utils-provider.ts @@ -1,7 +1,7 @@ import { Construct } from 'constructs'; import { CfnUtilsResourceType } from './cfn-utils-provider/consts'; -import { CfnUtilsProvider as _CfnUtilsProvider } from '../../dist/core/cfn-utils-provider.generated'; import { CustomResource } from '../custom-resource'; +import { CfnUtilsProvider as _CfnUtilsProvider } from '../dist/core/cfn-utils-provider.generated'; /** * A custom resource provider for CFN utilities such as `CfnJson`. diff --git a/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh b/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh index 898ca81973dcb..25513cee9a577 100755 --- a/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh +++ b/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh @@ -8,8 +8,8 @@ function airlift() { # core needs to be airlifted directly to core to prevent circular dependencies if [[ ($1 = dist/core/* || $1 = dist/core) && $1 != dist/core/nodejs-entrypoint-handler ]]; then - mkdir -p $awscdklibdir/core/$1 - cp $customresourcedir/$2 $awscdklibdir/core/$1 + mkdir -p $awscdklibdir/core/lib/$1 + cp $customresourcedir/$2 $awscdklibdir/core/lib/$1 else mkdir -p $awscdklibdir/custom-resource-handlers/$1 cp $customresourcedir/$2 $awscdklibdir/custom-resource-handlers/$1 From 034c7ce91db2e98e80051dc5d5962b7d27f70deb Mon Sep 17 00:00:00 2001 From: Francis Date: Mon, 11 Dec 2023 14:12:42 -0800 Subject: [PATCH 56/73] unit tests Signed-off-by: Francis --- .../expected/cdk-custom-resource-provider-core.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-custom-resource-provider-core.ts b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-custom-resource-provider-core.ts index 40fb419be689c..84353b7b77372 100644 --- a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-custom-resource-provider-core.ts +++ b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-custom-resource-provider-core.ts @@ -1,7 +1,8 @@ /* eslint-disable prettier/prettier,max-len */ import * as path from "path"; import { Construct } from "constructs"; -import { Stack, CustomResourceProviderBase, CustomResourceProviderOptions } from "../../lib"; +import { Stack } from "../../stack"; +import { CustomResourceProviderBase, CustomResourceProviderOptions } from "../../custom-resource-provider"; export class TestProvider extends CustomResourceProviderBase { /** From 7ba9f86a2961092be5be70ae86b3223f99f4d275 Mon Sep 17 00:00:00 2001 From: Francis Date: Mon, 11 Dec 2023 14:45:47 -0800 Subject: [PATCH 57/73] selective import from core internal stack module Signed-off-by: Francis --- .../lib/custom-resources-framework/classes.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts index c8fe142d2cbda..88bf6472f45e5 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts @@ -304,8 +304,7 @@ export abstract class CdkCustomResourceClass extends ClassType { return; } case CORE_INTERNAL_CR_PROVIDER.fqn: { - const stack = new ExternalModule('../../stack'); - stack.importSelective(scope, ['Stack']); + CORE_INTERNAL_STACK.importSelective(scope, ['Stack']); CORE_INTERNAL_CR_PROVIDER.importSelective(scope, [ 'CustomResourceProviderBase', 'CustomResourceProviderOptions', From 1db5e8cf1fae21fd81baa4c31b264cfaea01a9bd Mon Sep 17 00:00:00 2001 From: Francis Date: Mon, 11 Dec 2023 17:05:21 -0800 Subject: [PATCH 58/73] formatting Signed-off-by: Francis --- .../@aws-cdk/custom-resource-handlers/scripts/generate.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts index e9c46be55bd69..c40129eaad160 100644 --- a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts +++ b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts @@ -8,6 +8,7 @@ const framework: { [fqn: string]: ConfigProps[] } = {}; async function main() { recurse(config, []); + for (const [fqn, components] of Object.entries(framework)) { const module = new CdkCustomResourceModule(fqn); for (const component of components) { @@ -18,8 +19,8 @@ async function main() { } else { await minifyAndBundle(component.sourceCode, outfile); } - const codeDirectory = path.dirname(outfile).split('/').pop(); - module.build(component, `${codeDirectory}`); + const codeDirectory = path.dirname(outfile).split('/').pop() ?? ''; + module.build(component, codeDirectory); } if (module.hasComponents) { From c11b42a24576a24a2a2fd849b8b066ba4135573b Mon Sep 17 00:00:00 2001 From: Francis Date: Mon, 11 Dec 2023 17:08:31 -0800 Subject: [PATCH 59/73] update importing modules Signed-off-by: Francis --- .../lib/custom-resources-framework/classes.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts index 88bf6472f45e5..559fb9b2811bd 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts @@ -304,13 +304,16 @@ export abstract class CdkCustomResourceClass extends ClassType { return; } case CORE_INTERNAL_CR_PROVIDER.fqn: { - CORE_INTERNAL_STACK.importSelective(scope, ['Stack']); CORE_INTERNAL_CR_PROVIDER.importSelective(scope, [ 'CustomResourceProviderBase', 'CustomResourceProviderOptions', ]); return; } + case CORE_INTERNAL_STACK.fqn: { + CORE_INTERNAL_STACK.importSelective(scope, ['Stack']); + return; + } case LAMBDA_MODULE.fqn: { LAMBDA_MODULE.import(scope, 'lambda'); return; From 3744d655abdaa92f9a29524419cd8c089340575c Mon Sep 17 00:00:00 2001 From: Francis Date: Tue, 12 Dec 2023 20:37:21 -0800 Subject: [PATCH 60/73] documentation Signed-off-by: Francis --- .../lib/custom-resources-framework/README.md | 8 ++++++++ .../lib/custom-resources-framework/classes.ts | 18 ------------------ .../lib/custom-resources-framework/config.ts | 6 ++++-- 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/README.md b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/README.md index e69de29bb2d1d..d8cef29768aa8 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/README.md +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/README.md @@ -0,0 +1,8 @@ +# AWS CDK Custom Resource Framework + +The CDK custom resource framework is an internal framework developed to establish best practices, runtime enforcement, and consistency for custom resource providers and their associated handler code. + +# Creating a Custom Resource Provider + +Custom resource providers can be created in one of three forms - `CdkFunction`, `CdkSingletonFunction`, or `CdkCustomResourceProvider`. These three custom resource provider formats are a code generated wrapper around the lambda `Function`, lambda `SingletonFunction`, and `CustomResourceProvider` constructs, respectively. These new CDK prefixed constructs will offer the same behavior and construction options as their parent minus the ability to configure the `handler`, `code`, `runtime`, and `runtimeName` properties during construction. Instead, `handler`, `code`, `runtime`, and `runtimeName` will be automatically generated using the properties configured in the [config](./config.ts) file. The [config](./config.ts) file is structured with the top most level mapping to a specific service, i.e., `aws-s3`, `aws-dynamodb`, etc. Each service can contain one or more provider modules which are containers for the custom resource providers being requested. Custom resource providers are defined within a provider module as an array of one or more `ConfigProps`. `ConfigProps` allow you to define a provider with the following properties: + - type: the diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts index 559fb9b2811bd..7157ab5605d96 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts @@ -354,21 +354,3 @@ export abstract class CdkCustomResourceClass extends ClassType { init.addBody(new SuperInitializer(...superInitializerArgs)); } } - -// function toLambdaRuntime(runtime: Runtime) { -// switch (runtime.name) { -// case Runtime.NODEJS_16_X.name: { -// return 'lambda.Runtime.NODEJS_16_X'; -// } -// case Runtime.NODEJS_18_X.name: { -// return 'lambda.Runtime.NODEJS_18_X'; -// } -// case Runtime.PYTHON_3_9.name: { -// return 'lambda.Runtime.PYTHON_3_9'; -// } -// case Runtime.PYTHON_3_10.name: { -// return 'lambda.Runtime.PYTHON_3_10'; -// } -// } -// throw new Error('Unable to convert runtime to lambda runtime'); -// } diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/config.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/config.ts index b6ebd575b8a5d..eb97b2341800c 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/config.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/config.ts @@ -3,7 +3,7 @@ import * as path from 'path'; import { Runtime } from './runtime'; /** - * Handler framework component types. + * Custom resource provider types. */ export enum ComponentType { /** @@ -22,7 +22,9 @@ export enum ComponentType { CDK_CUSTOM_RESOURCE_PROVIDER = 'CdkCustomResourceProvider', /** - * Do not create a framework component. This is used to just move source code for airlifting. + * Do not create a custom resource provider. + * + * Note: This is used to just move source code for airlifting. */ CDK_NO_OP = 'CdkNoOp', } From a64afe4c7914eb01fdc7ed941c61eba96a5bff8a Mon Sep 17 00:00:00 2001 From: Francis Date: Wed, 13 Dec 2023 01:05:39 -0800 Subject: [PATCH 61/73] readme and naming Signed-off-by: Francis --- .../lib/custom-resources-framework/README.md | 56 +++++++++- .../lib/custom-resources-framework/classes.ts | 17 ++- .../lib/custom-resources-framework/config.ts | 104 +++++++++--------- .../custom-resources-framework/framework.ts | 38 +++---- .../scripts/generate.ts | 20 ++-- 5 files changed, 142 insertions(+), 93 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/README.md b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/README.md index d8cef29768aa8..8faa6b8570d45 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/README.md +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/README.md @@ -1,8 +1,58 @@ # AWS CDK Custom Resource Framework -The CDK custom resource framework is an internal framework developed to establish best practices, runtime enforcement, and consistency for custom resource providers and their associated handler code. +The CDK custom resource framework is an internal framework developed to establish best practices, runtime enforcement, and consistency for custom resource providers and their associated source code. # Creating a Custom Resource Provider -Custom resource providers can be created in one of three forms - `CdkFunction`, `CdkSingletonFunction`, or `CdkCustomResourceProvider`. These three custom resource provider formats are a code generated wrapper around the lambda `Function`, lambda `SingletonFunction`, and `CustomResourceProvider` constructs, respectively. These new CDK prefixed constructs will offer the same behavior and construction options as their parent minus the ability to configure the `handler`, `code`, `runtime`, and `runtimeName` properties during construction. Instead, `handler`, `code`, `runtime`, and `runtimeName` will be automatically generated using the properties configured in the [config](./config.ts) file. The [config](./config.ts) file is structured with the top most level mapping to a specific service, i.e., `aws-s3`, `aws-dynamodb`, etc. Each service can contain one or more provider modules which are containers for the custom resource providers being requested. Custom resource providers are defined within a provider module as an array of one or more `ConfigProps`. `ConfigProps` allow you to define a provider with the following properties: - - type: the +Custom resource providers can be created in one of three forms - `CdkFunction`, `CdkSingletonFunction`, or `CdkCustomResourceProvider`. These three custom resource provider formats are a code generated wrapper around the lambda `Function`, lambda `SingletonFunction`, and `CustomResourceProvider` constructs, respectively. These new CDK prefixed constructs will offer the same behavior and initialization options as their parent minus the ability to configure the following properties during construction: +- `handler` - the name of the method within your code that the provider calls to execute your function +- `code` - the source code of your provider +- `codeDirectory` - a local file system directory with the provider's code +- `runtime` - the runtime environment for the provider that you are uploading + +Instead, these properties will be automatically generated using the `ProviderProps` configured in the [config](./config.ts) file: +- `type`: the custom resource provider type to generate +- `sourceCode`: the source code that will be executed by the provider +- `compatibleRuntimes`: runtimes that are compatible with the provider's source code +- `handler`: the name of the method within your code that the provider calls to execute your function + +The [config](./config.ts) file is structured with the top most level mapping to a specific service, i.e., `aws-s3`, `aws-dynamodb`, etc. Each service can contain one or more provider modules. Provider modules are containers for custom resource providers and will be rendered as a code generated file. Each provider module can contain one or more `ProviderProps` objects. Each `ProviderProps` object contains all the necessary information required to generate a single custom resource provider. For more clarity, consider the following example of the [config](./config.ts) file: + +```ts +const config = { + 'aws-certificatemanager': { + 'certificate-request-provider': [ + { + type: ComponentType.CDK_FUNCTION, + sourceCode: path.resolve(__dirname, '..', 'aws-certificatemanager' 'dns-validated-certificate-handler', 'index.js'), + compatibleRuntimes: [Runtime.NODEJS_18_X], + handler: 'index.certificateRequestHandler', + }, + ], + }, + 'aws-s3': { + 'replica-provider': [ + { + type: ComponentType.CDK_FUNCTION, + sourceCode: path.resolve(__dirname, '..', 'aws-dynamodb', 'replica-handler', 'index.ts'), + compatibleRuntimes: [Runtime.NODEJS_18_X], + handler: 'index.onEventHandler', + }, + { + type: ComponentType.CDK_FUNCTION, + sourceCode: path.resolve(__dirname, '..', 'aws-dynamodb', 'replica-handler', 'index.ts'), + compatibleRuntimes: [Runtime.NODEJS_18_X], + handler: 'index.isCompleteHandler', + }, + ], + }, +}; +``` + +Inspecting the above example gives us the following information: +- There are two services present: `aws-certificatemanager` and `aws-s3` +- Both the `aws-certificatemanager` and `aws-dynamodb` services contain a single provider module - `certificate-request-provider` and `replica-provider`, respectively +- The `certificate-request-provider` contains a single custom resource provider which is a `CdkFunction` +- The `replica-provider` provider module contains two custom resource providers both of which are `CdkFunction` + + diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts index 7157ab5605d96..d785d1538fa34 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts @@ -55,29 +55,28 @@ interface ConstructorBuildProps { } /** - * Initialization properties used to build a `CdkHandlerFrameworkClass` instance. + * Initialization properties used to build a `CdkCustomResourceClass` instance. */ export interface CdkCustomResourceClassProps { /** - * The name of the component class. + * The name of the provider class. */ readonly name: string; /** - * A local file system directory with the provider's code. The code will be - * bundled into a zip asset and wired to the provider's AWS Lambda function. + * A local file system directory with the provider's code. */ readonly codeDirectory: string; /** - * The runtime environment for the Lambda function. + * The runtime environment for the custom resource provider. */ readonly runtime: Runtime; /** - * The name of the method within your code that Lambda calls to execute your function. + * The name of the method within your code that provider calls to execute your function. */ - readonly entrypoint: string; + readonly handler: string; } export abstract class CdkCustomResourceClass extends ClassType { @@ -100,7 +99,7 @@ export abstract class CdkCustomResourceClass extends ClassType { const superProps = new ObjectLiteral([ new Splat(expr.ident('props')), ['code', expr.directCode(`lambda.Code.fromAsset(path.join(__dirname, '${props.codeDirectory}'))`)], - ['handler', expr.lit(props.entrypoint)], + ['handler', expr.lit(props.handler)], ['runtime', expr.directCode(props.runtime.toLambdaRuntime())], ]); this.buildConstructor({ @@ -162,7 +161,7 @@ export abstract class CdkCustomResourceClass extends ClassType { const superProps = new ObjectLiteral([ new Splat(expr.ident('props')), ['code', expr.directCode(`lambda.Code.fromAsset(path.join(__dirname, '${props.codeDirectory}'))`)], - ['handler', expr.lit(props.entrypoint)], + ['handler', expr.lit(props.handler)], ['runtime', expr.directCode(props.runtime.toLambdaRuntime())], ]); this.buildConstructor({ diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/config.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/config.ts index eb97b2341800c..c44f566f033b1 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/config.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/config.ts @@ -5,7 +5,7 @@ import { Runtime } from './runtime'; /** * Custom resource provider types. */ -export enum ComponentType { +export enum ProviderType { /** * `CdkFunction` */ @@ -30,13 +30,13 @@ export enum ComponentType { } /** - * Properites used to initialize individual handler framework components. + * Properites used to generate individual custom resource providers. */ -export interface ConfigProps { +export interface ProviderProps { /** - * The component type to generate. + * The custom resource provider type to generate. */ - readonly type: ComponentType; + readonly type: ProviderType; /** * The source code that will be executed by the provider. @@ -44,16 +44,16 @@ export interface ConfigProps { readonly sourceCode: string; /** - * Runtimes that are compatible with the source code. + * Runtimes that are compatible with the provider's source code. */ readonly compatibleRuntimes: Runtime[]; /** - * The name of the method within your code that Lambda calls to execute your function. + * The name of the method within your code that the provider calls to execute your function. * * @default 'index.handler' */ - readonly entrypoint?: string; + readonly handler?: string; /** * Prevents the source code from being minified and bundled. This is needed for python @@ -64,13 +64,13 @@ export interface ConfigProps { readonly preventMinifyAndBundle?: boolean; } -export type HandlerFrameworkConfig = { [module: string]: { [identifier: string]: ConfigProps[] } }; +export type CustomResourceFrameworkConfig = { [module: string]: { [identifier: string]: ProviderProps[] } }; -export const config: HandlerFrameworkConfig = { +export const config: CustomResourceFrameworkConfig = { 'aws-amplify-alpha': { 'asset-deployment-handler': [ { - type: ComponentType.CDK_NO_OP, + type: ProviderType.CDK_NO_OP, sourceCode: path.resolve(__dirname, '..', 'aws-amplify-alpha', 'asset-deployment-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -79,17 +79,17 @@ export const config: HandlerFrameworkConfig = { 'aws-certificatemanager': { 'certificate-request-provider': [ { - type: ComponentType.CDK_FUNCTION, + type: ProviderType.CDK_FUNCTION, sourceCode: path.resolve(__dirname, '..', 'aws-certificatemanager', 'dns-validated-certificate-handler', 'index.js'), compatibleRuntimes: [Runtime.NODEJS_18_X], - entrypoint: 'index.certificateRequestHandler', + handler: 'index.certificateRequestHandler', }, ], }, 'aws-cloudfront': { 'cross-region-string-param-reader-provider': [ { - type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + type: ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER, sourceCode: path.resolve(__dirname, '..', 'aws-cloudfront', 'edge-function', 'index.js'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -98,23 +98,23 @@ export const config: HandlerFrameworkConfig = { 'aws-dynamodb': { 'replica-provider': [ { - type: ComponentType.CDK_FUNCTION, + type: ProviderType.CDK_FUNCTION, sourceCode: path.resolve(__dirname, '..', 'aws-dynamodb', 'replica-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], - entrypoint: 'index.onEventHandler', + handler: 'index.onEventHandler', }, { - type: ComponentType.CDK_FUNCTION, + type: ProviderType.CDK_FUNCTION, sourceCode: path.resolve(__dirname, '..', 'aws-dynamodb', 'replica-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], - entrypoint: 'index.isCompleteHandler', + handler: 'index.isCompleteHandler', }, ], }, 'aws-ec2': { 'restrict-default-sg-provider': [ { - type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + type: ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER, sourceCode: path.resolve(__dirname, '..', 'aws-ec2', 'restrict-default-security-group-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -123,7 +123,7 @@ export const config: HandlerFrameworkConfig = { 'aws-ecr': { 'auto-delete-images-provider': [ { - type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + type: ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER, sourceCode: path.resolve(__dirname, '..', 'aws-ecr', 'auto-delete-images-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -132,10 +132,10 @@ export const config: HandlerFrameworkConfig = { 'aws-ecs': { 'drain-hook-provider': [ { - type: ComponentType.CDK_FUNCTION, + type: ProviderType.CDK_FUNCTION, sourceCode: path.resolve(__dirname, '..', 'aws-ecs', 'lambda-source', 'index.py'), compatibleRuntimes: [Runtime.PYTHON_3_9], - entrypoint: 'index.lambda_handler', + handler: 'index.lambda_handler', preventMinifyAndBundle: true, }, ], @@ -143,45 +143,45 @@ export const config: HandlerFrameworkConfig = { 'aws-eks': { 'cluster-resource-provider': [ { - type: ComponentType.CDK_FUNCTION, + type: ProviderType.CDK_FUNCTION, sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'cluster-resource-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], - entrypoint: 'index.onEvent', + handler: 'index.onEvent', }, { - type: ComponentType.CDK_FUNCTION, + type: ProviderType.CDK_FUNCTION, sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'cluster-resource-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], - entrypoint: 'index.isComplete', + handler: 'index.isComplete', }, ], 'kubectl-provider': [ { - type: ComponentType.CDK_FUNCTION, + type: ProviderType.CDK_FUNCTION, sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'index.py'), compatibleRuntimes: [Runtime.PYTHON_3_10], preventMinifyAndBundle: true, }, { - type: ComponentType.CDK_NO_OP, + type: ProviderType.CDK_NO_OP, sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'apply', '__init__.py'), compatibleRuntimes: [Runtime.PYTHON_3_10], preventMinifyAndBundle: true, }, { - type: ComponentType.CDK_NO_OP, + type: ProviderType.CDK_NO_OP, sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'get', '__init__.py'), compatibleRuntimes: [Runtime.PYTHON_3_10], preventMinifyAndBundle: true, }, { - type: ComponentType.CDK_NO_OP, + type: ProviderType.CDK_NO_OP, sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'helm', '__init__.py'), compatibleRuntimes: [Runtime.PYTHON_3_10], preventMinifyAndBundle: true, }, { - type: ComponentType.CDK_NO_OP, + type: ProviderType.CDK_NO_OP, sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'patch', '__init__.py'), compatibleRuntimes: [Runtime.PYTHON_3_10], preventMinifyAndBundle: true, @@ -191,7 +191,7 @@ export const config: HandlerFrameworkConfig = { 'aws-events-targets': { 'aws-api-provider': [ { - type: ComponentType.CDK_SINGLETON_FUNCTION, + type: ProviderType.CDK_SINGLETON_FUNCTION, sourceCode: path.resolve(__dirname, '..', 'aws-events-targets', 'aws-api-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -200,7 +200,7 @@ export const config: HandlerFrameworkConfig = { 'aws-iam': { 'oidc-provider': [ { - type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + type: ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER, sourceCode: path.resolve(__dirname, '..', 'aws-iam', 'oidc-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -209,7 +209,7 @@ export const config: HandlerFrameworkConfig = { 'aws-logs': { 'log-retention': [ { - type: ComponentType.CDK_NO_OP, + type: ProviderType.CDK_NO_OP, sourceCode: path.resolve(__dirname, '..', 'aws-logs', 'log-retention-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -218,7 +218,7 @@ export const config: HandlerFrameworkConfig = { 'aws-redshift-alpha': { 'cluster-reboot-provider': [ { - type: ComponentType.CDK_SINGLETON_FUNCTION, + type: ProviderType.CDK_SINGLETON_FUNCTION, sourceCode: path.resolve(__dirname, '..', 'aws-redshift-alpha', 'cluster-parameter-change-reboot-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -227,14 +227,14 @@ export const config: HandlerFrameworkConfig = { 'aws-route53': { 'cross-account-zone-delegation-provider': [ { - type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + type: ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER, sourceCode: path.resolve(__dirname, '..', 'aws-route53', 'cross-account-zone-delegation-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], 'delete-existing-record-set-provider': [ { - type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + type: ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER, sourceCode: path.resolve(__dirname, '..', 'aws-route53', 'delete-existing-record-set-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -243,14 +243,14 @@ export const config: HandlerFrameworkConfig = { 'aws-s3': { 'auto-delete-objects-provider': [ { - type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + type: ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER, sourceCode: path.resolve(__dirname, '..', 'aws-s3', 'auto-delete-objects-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], 'notifications-resource-handler': [ { - type: ComponentType.CDK_NO_OP, + type: ProviderType.CDK_NO_OP, sourceCode: path.resolve(__dirname, '..', 'aws-s3', 'notifications-resource-handler', 'index.py'), compatibleRuntimes: [Runtime.PYTHON_3_9], preventMinifyAndBundle: true, @@ -260,7 +260,7 @@ export const config: HandlerFrameworkConfig = { 'aws-s3-deployment': { 'bucket-deployment-provider': [ { - type: ComponentType.CDK_SINGLETON_FUNCTION, + type: ProviderType.CDK_SINGLETON_FUNCTION, sourceCode: path.resolve(__dirname, '..', 'aws-s3-deployment', 'bucket-deployment-handler', 'index.py'), compatibleRuntimes: [Runtime.PYTHON_3_9], preventMinifyAndBundle: true, @@ -270,7 +270,7 @@ export const config: HandlerFrameworkConfig = { 'aws-ses': { 'drop-spam-provider': [ { - type: ComponentType.CDK_SINGLETON_FUNCTION, + type: ProviderType.CDK_SINGLETON_FUNCTION, sourceCode: path.resolve(__dirname, '..', 'aws-ses', 'drop-spam-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -279,14 +279,14 @@ export const config: HandlerFrameworkConfig = { 'aws-stepfunctions-tasks': { 'eval-nodejs-provider': [ { - type: ComponentType.CDK_SINGLETON_FUNCTION, + type: ProviderType.CDK_SINGLETON_FUNCTION, sourceCode: path.resolve(__dirname, '..', 'aws-stepfunctions-tasks', 'eval-nodejs-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], 'role-policy-provider': [ { - type: ComponentType.CDK_SINGLETON_FUNCTION, + type: ProviderType.CDK_SINGLETON_FUNCTION, sourceCode: path.resolve(__dirname, '..', 'aws-stepfunctions-tasks', 'role-policy-handler', 'index.py'), compatibleRuntimes: [Runtime.PYTHON_3_9], preventMinifyAndBundle: true, @@ -296,7 +296,7 @@ export const config: HandlerFrameworkConfig = { 'aws-synthetics': { 'auto-delete-underlying-resources-provider': [ { - type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + type: ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER, sourceCode: path.resolve(__dirname, '..', 'aws-synthetics', 'auto-delete-underlying-resources-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -305,28 +305,28 @@ export const config: HandlerFrameworkConfig = { 'core': { 'cfn-utils-provider': [ { - type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + type: ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER, sourceCode: path.resolve(__dirname, '..', 'core', 'cfn-utils-provider', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], 'cross-region-ssm-writer-provider': [ { - type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + type: ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER, sourceCode: path.resolve(__dirname, '..', 'core', 'cross-region-ssm-writer-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], 'cross-region-ssm-reader-provider': [ { - type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + type: ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER, sourceCode: path.resolve(__dirname, '..', 'core', 'cross-region-ssm-reader-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], - 'nodejs-entrypoint': [ + 'nodejs-entrypoint-provider': [ { - type: ComponentType.CDK_NO_OP, + type: ProviderType.CDK_NO_OP, sourceCode: path.resolve(__dirname, '..', 'core', 'nodejs-entrypoint-handler', 'index.js'), compatibleRuntimes: [Runtime.NODEJS_18_X], preventMinifyAndBundle: true, @@ -336,7 +336,7 @@ export const config: HandlerFrameworkConfig = { 'custom-resources': { 'aws-custom-resource-provider': [ { - type: ComponentType.CDK_SINGLETON_FUNCTION, + type: ProviderType.CDK_SINGLETON_FUNCTION, sourceCode: path.resolve(__dirname, '..', 'custom-resources', 'aws-custom-resource-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -345,7 +345,7 @@ export const config: HandlerFrameworkConfig = { 'pipelines': { 'approve-lambda': [ { - type: ComponentType.CDK_FUNCTION, + type: ProviderType.CDK_FUNCTION, sourceCode: path.resolve(__dirname, '..', 'pipelines', 'approve-lambda', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, @@ -354,7 +354,7 @@ export const config: HandlerFrameworkConfig = { 'triggers': { 'trigger-provider': [ { - type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + type: ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER, sourceCode: path.resolve(__dirname, '..', 'triggers', 'lambda', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], }, diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/framework.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/framework.ts index 0f628dd448593..6e8e0c79d66bc 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/framework.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/framework.ts @@ -2,31 +2,31 @@ import { ExternalModule, InterfaceType, Module, TypeScriptRenderer } from '@cdklabs/typewriter'; import * as fs from 'fs-extra'; import { CdkCustomResourceClass, CdkCustomResourceClassProps } from './classes'; -import { ComponentType, ConfigProps } from './config'; +import { ProviderType, ProviderProps } from './config'; import { Runtime } from './runtime'; import { RuntimeDeterminer } from './utils/runtime-determiner'; export class CdkCustomResourceModule extends Module { /** - * The latest nodejs runtime version available across all AWS regions + * The latest nodejs runtime version available across all AWS regions. */ private static readonly DEFAULT_RUNTIME = Runtime.NODEJS_18_X; private readonly renderer = new TypeScriptRenderer(); private readonly externalModules = new Map(); private readonly _interfaces = new Map(); - private _hasComponents = false; + private _hasProviders = false; /** - * Whether the module being generated will live inside of aws-cdk-lib/core + * Whether the module being generated will live inside of aws-cdk-lib/core. */ public readonly coreInternal: boolean; /** - * Whether the module contains framework components + * Whether the module contains custom resource providers. */ - public get hasComponents() { - return this._hasComponents; + public get hasProviders() { + return this._hasProviders; } public constructor(fqn: string) { @@ -37,36 +37,36 @@ export class CdkCustomResourceModule extends Module { /** * Build a framework component inside of this module. */ - public build(component: ConfigProps, codeDirectory: string) { - if (component.type === ComponentType.CDK_NO_OP) { + public build(provider: ProviderProps, codeDirectory: string) { + if (provider.type === ProviderType.CDK_NO_OP) { return; } - this._hasComponents = true; + this._hasProviders = true; - const entrypoint = component.entrypoint ?? 'index.handler'; - const name = this.buildComponentName(entrypoint); + const handler = provider.handler ?? 'index.handler'; + const name = this.buildProviderName(handler); const props: CdkCustomResourceClassProps = { name, - entrypoint, + handler, codeDirectory, runtime: RuntimeDeterminer.determineLatestRuntime( - component.compatibleRuntimes, + provider.compatibleRuntimes, CdkCustomResourceModule.DEFAULT_RUNTIME, ), }; - switch (component.type) { - case ComponentType.CDK_FUNCTION: { + switch (provider.type) { + case ProviderType.CDK_FUNCTION: { CdkCustomResourceClass.buildCdkFunction(this, props); break; } - case ComponentType.CDK_SINGLETON_FUNCTION: { + case ProviderType.CDK_SINGLETON_FUNCTION: { CdkCustomResourceClass.buildCdkSingletonFunction(this, props); break; } - case ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER: { + case ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER: { CdkCustomResourceClass.buildCdkCustomResourceProvider(this, props); break; } @@ -110,7 +110,7 @@ export class CdkCustomResourceModule extends Module { return this._interfaces.get(name); } - private buildComponentName(entrypoint: string) { + private buildProviderName(entrypoint: string) { const id = this.fqn.split('/').at(-1)?.replace('-provider', '') ?? ''; const handler = entrypoint.split('.').at(-1)?.replace(/[_Hh]andler/g, '') ?? ''; const name = (id.replace( diff --git a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts index c40129eaad160..7fd7ac6a854a8 100644 --- a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts +++ b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts @@ -1,29 +1,29 @@ import * as fs from 'fs'; import * as path from 'path'; import * as esbuild from 'esbuild'; -import { config, ConfigProps } from '../lib/custom-resources-framework/config'; +import { config, ProviderProps } from '../lib/custom-resources-framework/config'; import { CdkCustomResourceModule } from '../lib/custom-resources-framework/framework'; -const framework: { [fqn: string]: ConfigProps[] } = {}; +const framework: { [fqn: string]: ProviderProps[] } = {}; async function main() { recurse(config, []); - for (const [fqn, components] of Object.entries(framework)) { + for (const [fqn, providers] of Object.entries(framework)) { const module = new CdkCustomResourceModule(fqn); - for (const component of components) { - const outfile = calculateOutfile(component.sourceCode); - if (component.preventMinifyAndBundle) { + for (const provider of providers) { + const outfile = calculateOutfile(provider.sourceCode); + if (provider.preventMinifyAndBundle) { fs.mkdirSync(path.dirname(outfile), { recursive: true }); - fs.copyFileSync(component.sourceCode, outfile); + fs.copyFileSync(provider.sourceCode, outfile); } else { - await minifyAndBundle(component.sourceCode, outfile); + await minifyAndBundle(provider.sourceCode, outfile); } const codeDirectory = path.dirname(outfile).split('/').pop() ?? ''; - module.build(component, codeDirectory); + module.build(provider, codeDirectory); } - if (module.hasComponents) { + if (module.hasProviders) { module.renderTo(`dist/${fqn}.generated.ts`); } } From 3a8757a341d0ec880ff0611db66078c5f1c3b59a Mon Sep 17 00:00:00 2001 From: Francis Date: Wed, 13 Dec 2023 01:57:48 -0800 Subject: [PATCH 62/73] readme Signed-off-by: Francis --- .../lib/custom-resources-framework/README.md | 69 +++++++++++++------ 1 file changed, 49 insertions(+), 20 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/README.md b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/README.md index 8faa6b8570d45..8141a703d43a1 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/README.md +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/README.md @@ -2,7 +2,7 @@ The CDK custom resource framework is an internal framework developed to establish best practices, runtime enforcement, and consistency for custom resource providers and their associated source code. -# Creating a Custom Resource Provider +## Custom Resource Framework Concepts Custom resource providers can be created in one of three forms - `CdkFunction`, `CdkSingletonFunction`, or `CdkCustomResourceProvider`. These three custom resource provider formats are a code generated wrapper around the lambda `Function`, lambda `SingletonFunction`, and `CustomResourceProvider` constructs, respectively. These new CDK prefixed constructs will offer the same behavior and initialization options as their parent minus the ability to configure the following properties during construction: - `handler` - the name of the method within your code that the provider calls to execute your function @@ -16,43 +16,72 @@ Instead, these properties will be automatically generated using the `ProviderPro - `compatibleRuntimes`: runtimes that are compatible with the provider's source code - `handler`: the name of the method within your code that the provider calls to execute your function -The [config](./config.ts) file is structured with the top most level mapping to a specific service, i.e., `aws-s3`, `aws-dynamodb`, etc. Each service can contain one or more provider modules. Provider modules are containers for custom resource providers and will be rendered as a code generated file. Each provider module can contain one or more `ProviderProps` objects. Each `ProviderProps` object contains all the necessary information required to generate a single custom resource provider. For more clarity, consider the following example of the [config](./config.ts) file: +The [config](./config.ts) file is structured with the top most level mapping to a `aws-cdk-lib` module, i.e., `aws-s3`, `aws-dynamodb`, etc. Each service can contain one or more provider modules. Provider modules are containers for custom resource providers and will be rendered as a code generated file. Each provider module can contain one or more `ProviderProps` objects. Each `ProviderProps` object contains all the necessary information required to generate a single custom resource provider. The following example shows a more structural breakdown of how the [config](./config.ts) file is configured: ```ts const config = { - 'aws-certificatemanager': { - 'certificate-request-provider': [ + 'aws-s3': { // the aws-cdk-lib module + 'replica-provider': [ // the custom resource provider module + // custom resource provider defined as ProviderProps object { + // the custom resource provider type - `CdkFunction` type: ComponentType.CDK_FUNCTION, - sourceCode: path.resolve(__dirname, '..', 'aws-certificatemanager' 'dns-validated-certificate-handler', 'index.js'), + // the source code that the provider will execute + sourceCode: path.resolve(__dirname, '..', 'aws-dynamodb', 'replica-handler', 'index.ts'), + // runtimes that are compatible with the source code compatibleRuntimes: [Runtime.NODEJS_18_X], - handler: 'index.certificateRequestHandler', + // the handler in the source code that the provider will execute + handler: 'index.onEventHandler', }, ], }, - 'aws-s3': { - 'replica-provider': [ + 'aws-stepfunctions-tasks': { + // contains multiple custom resource provider modules + 'eval-nodejs-provider': [ { - type: ComponentType.CDK_FUNCTION, - sourceCode: path.resolve(__dirname, '..', 'aws-dynamodb', 'replica-handler', 'index.ts'), + // the custom resource provider type - `CdkSingletonFunction` + type: ProviderType.CDK_SINGLETON_FUNCTION, + sourceCode: path.resolve(__dirname, '..', 'aws-stepfunctions-tasks', 'eval-nodejs-handler', 'index.ts'), compatibleRuntimes: [Runtime.NODEJS_18_X], - handler: 'index.onEventHandler', }, + ], + 'role-policy-provider': [ { - type: ComponentType.CDK_FUNCTION, - sourceCode: path.resolve(__dirname, '..', 'aws-dynamodb', 'replica-handler', 'index.ts'), - compatibleRuntimes: [Runtime.NODEJS_18_X], - handler: 'index.isCompleteHandler', + type: ProviderType.CDK_SINGLETON_FUNCTION, + sourceCode: path.resolve(__dirname, '..', 'aws-stepfunctions-tasks', 'role-policy-handler', 'index.py'), + compatibleRuntimes: [Runtime.PYTHON_3_9], + // prevent minify and bundle flag is set since the source code is a python file + preventMinifyAndBundle: true, }, ], }, }; ``` -Inspecting the above example gives us the following information: -- There are two services present: `aws-certificatemanager` and `aws-s3` -- Both the `aws-certificatemanager` and `aws-dynamodb` services contain a single provider module - `certificate-request-provider` and `replica-provider`, respectively -- The `certificate-request-provider` contains a single custom resource provider which is a `CdkFunction` -- The `replica-provider` provider module contains two custom resource providers both of which are `CdkFunction` +Code generation for the provider modules is triggered when this package - `@aws-cdk/custom-resource-handlers` - is built. Importantly, this framework is also responsible for minifying and bundling the custom resource providers' source code and dependencies. A flag named `preventMinifyAndBundle` can be configured as part of the `ProviderProps` to prevent minifying and bundling the source code for a specific provider. This flag is `false` by default and is only needed for Python files or for JavaScript/TypeScript files containing require imports. + +Once built, all generated code and bundled source code will be written to `@aws-cdk/custom-resource-handlers/dist`. The top level field in the [config](./config.ts) file defining individual `aws-cdk-lib` modules will be used to create specific directories within `@aws-cdk/custom-resource-handlers/dist` and each provider module will be a separate code generated file within these directories named as `.generated.ts`. As an example, the sample [config](./config.ts) file above would create the following file structure: + +|--- @aws-cdk +| |--- custom-resource-handlers +| | |--- dist +| | | |--- aws-s3 +| | | | |--- replica-handler +| | | | | |--- index.js +| | | | |--- replica-provider.generated.ts +| | | |--- aws-stepfunctions-tasks +| | | | |--- eval-nodejs-handler +| | | | | |--- index.js +| | | | |--- role-policy-handler +| | | | | |--- index.py +| | | | |--- eval-nodejs-provider.generated.ts +| | | | |--- role-policy-provider.generated.ts + +The code generated custom resource providers are usable in `aws-cdk-lib` once `aws-cdk-lib` is built. The custom resource providers will be consumable from `aws-cdk-lib/custom-resource-handlers/dist` and the file structure therein will match what was generated in `@aws-cdk/custom-resource-handlers/dist` except for providers defined in `core`. To prevent circular dependencies, all custom resource providers defined in `core`and any associated source code will be consumable from `aws-cdk-lib/core/dist/core`. +## Creating a Custom Resource Provider +Creating a new custom resource provider involves three steps: +1. Add the custom resource provider's source code to `@aws-cdk/custom-resource-handlers/lib/` +2. Update the [config](./config.ts) file with the custom resource provider to generate by specifying all required `ProviderProps`. +3. At this point you can directly build `@aws-cdk/custom-resource-handlers` with `yarn build` to view the generated provider in `@aws-cdk/custom-resource-handlers/dist`. Alternatively, you can build `aws-cdk-lib` with `npx lerna run build --scope=aws-cdk-lib --skip-nx-cache` to make the generated provider available for use within `aws-cdk-lib` From 7a326ca925d5908142eee3671ab1869aa66366c7 Mon Sep 17 00:00:00 2001 From: Francis Date: Wed, 13 Dec 2023 09:45:28 -0800 Subject: [PATCH 63/73] fix unit tests Signed-off-by: Francis --- .../framework.test.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/framework.test.ts b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/framework.test.ts index a6650b54eacf2..6583376455dcc 100644 --- a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/framework.test.ts +++ b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/framework.test.ts @@ -1,7 +1,7 @@ import * as os from 'os'; import * as path from 'path'; import * as fs from 'fs-extra'; -import { ComponentType, ConfigProps } from '../../lib/custom-resources-framework/config'; +import { ProviderProps, ProviderType } from '../../lib/custom-resources-framework/config'; import { CdkCustomResourceModule } from '../../lib/custom-resources-framework/framework'; import { Runtime } from '../../lib/custom-resources-framework/runtime'; import { calculateOutfile } from '../../scripts/generate'; @@ -19,8 +19,8 @@ describe('framework', () => { test('can codegen cdk function', () => { // GIVEN const module = new CdkCustomResourceModule('cdk-testing/test-provider'); - const component: ConfigProps = { - type: ComponentType.CDK_FUNCTION, + const component: ProviderProps = { + type: ProviderType.CDK_FUNCTION, sourceCode, compatibleRuntimes: [Runtime.NODEJS_18_X], }; @@ -39,8 +39,8 @@ describe('framework', () => { test('can codegen cdk singleton function', () => { // GIVEN const module = new CdkCustomResourceModule('cdk-testing/test-provider'); - const component: ConfigProps = { - type: ComponentType.CDK_SINGLETON_FUNCTION, + const component: ProviderProps = { + type: ProviderType.CDK_SINGLETON_FUNCTION, sourceCode, compatibleRuntimes: [Runtime.NODEJS_18_X], }; @@ -59,8 +59,8 @@ describe('framework', () => { test('can codegen cdk custom resource provider for core internal', () => { // GIVEN const module = new CdkCustomResourceModule('cdk-testing/test-provider'); - const component: ConfigProps = { - type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + const component: ProviderProps = { + type: ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER, sourceCode, compatibleRuntimes: [Runtime.NODEJS_18_X], }; @@ -79,8 +79,8 @@ describe('framework', () => { test('can codegen cdk custom resource provider for core internal', () => { // GIVEN const module = new CdkCustomResourceModule('core/test-provider'); - const component: ConfigProps = { - type: ComponentType.CDK_CUSTOM_RESOURCE_PROVIDER, + const component: ProviderProps = { + type: ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER, sourceCode, compatibleRuntimes: [Runtime.NODEJS_18_X], }; From 812ac0a1aa169dc57d87b7628d2e3fa7cac305b3 Mon Sep 17 00:00:00 2001 From: Francis Date: Thu, 14 Dec 2023 14:01:43 -0800 Subject: [PATCH 64/73] naming Signed-off-by: Francis --- .../lib/custom-resources-framework/classes.ts | 49 ++--- .../lib/custom-resources-framework/config.ts | 204 +++++++++--------- .../custom-resources-framework/framework.ts | 61 ++---- .../lib/custom-resources-framework/runtime.ts | 133 ------------ .../utils/framework-utils.ts | 26 +++ .../utils/runtime-determiner.ts | 109 ---------- .../scripts/generate.ts | 26 +-- .../framework.test.ts | 33 ++- .../utils/runtime-determiner.test.ts | 79 ------- 9 files changed, 200 insertions(+), 520 deletions(-) delete mode 100644 packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/runtime.ts create mode 100644 packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/utils/framework-utils.ts delete mode 100644 packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/utils/runtime-determiner.ts delete mode 100644 packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/utils/runtime-determiner.test.ts diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts index d785d1538fa34..c3fec05370836 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts @@ -14,7 +14,8 @@ import { SuperInitializer, Expression, } from '@cdklabs/typewriter'; -import { CdkCustomResourceModule } from './framework'; +import { Runtime } from './config'; +import { HandlerFrameworkModule } from './framework'; import { CONSTRUCTS_MODULE, LAMBDA_MODULE, @@ -23,7 +24,7 @@ import { CORE_INTERNAL_CR_PROVIDER, PATH_MODULE, } from './modules'; -import { Runtime } from './runtime'; +import { toLambdaRuntime } from './utils/framework-utils'; /** * Initialization properties for a class constructor. @@ -55,36 +56,36 @@ interface ConstructorBuildProps { } /** - * Initialization properties used to build a `CdkCustomResourceClass` instance. + * Initialization properties used to build a `HandlerFrameworkClass` instance. */ -export interface CdkCustomResourceClassProps { +export interface HandlerFrameworkClassProps { /** - * The name of the provider class. + * The name of the framework component class. */ readonly name: string; /** - * A local file system directory with the provider's code. + * A local file system directory with the framework component's code. */ readonly codeDirectory: string; /** - * The runtime environment for the custom resource provider. + * The runtime environment for the framework component. */ readonly runtime: Runtime; /** - * The name of the method within your code that provider calls to execute your function. + * The name of the method within your code that framework component calls. */ readonly handler: string; } -export abstract class CdkCustomResourceClass extends ClassType { +export abstract class HandlerFrameworkClass extends ClassType { /** - * Builds a `CdkFunction` class. + * Builds a code generated Lambda function class. */ - public static buildCdkFunction(scope: CdkCustomResourceModule, props: CdkCustomResourceClassProps): CdkCustomResourceClass { - return new (class CdkFunction extends CdkCustomResourceClass { + public static buildFunction(scope: HandlerFrameworkModule, props: HandlerFrameworkClassProps): HandlerFrameworkClass { + return new (class Function extends HandlerFrameworkClass { protected readonly externalModules = [PATH_MODULE, CONSTRUCTS_MODULE, LAMBDA_MODULE]; public constructor() { @@ -100,7 +101,7 @@ export abstract class CdkCustomResourceClass extends ClassType { new Splat(expr.ident('props')), ['code', expr.directCode(`lambda.Code.fromAsset(path.join(__dirname, '${props.codeDirectory}'))`)], ['handler', expr.lit(props.handler)], - ['runtime', expr.directCode(props.runtime.toLambdaRuntime())], + ['runtime', expr.directCode(toLambdaRuntime(props.runtime))], ]); this.buildConstructor({ constructorPropsType: LAMBDA_MODULE.FunctionOptions, @@ -113,10 +114,10 @@ export abstract class CdkCustomResourceClass extends ClassType { } /** - * Builds a `CdkSingletonFunction` class. + * Builds a code generated Lambda singleton function class. */ - public static buildCdkSingletonFunction(scope: CdkCustomResourceModule, props: CdkCustomResourceClassProps): CdkCustomResourceClass { - return new (class CdkSingletonFunction extends CdkCustomResourceClass { + public static buildSingletonFunction(scope: HandlerFrameworkModule, props: HandlerFrameworkClassProps): HandlerFrameworkClass { + return new (class SingletonFunction extends HandlerFrameworkClass { protected readonly externalModules = [PATH_MODULE, CONSTRUCTS_MODULE, LAMBDA_MODULE]; public constructor() { @@ -162,7 +163,7 @@ export abstract class CdkCustomResourceClass extends ClassType { new Splat(expr.ident('props')), ['code', expr.directCode(`lambda.Code.fromAsset(path.join(__dirname, '${props.codeDirectory}'))`)], ['handler', expr.lit(props.handler)], - ['runtime', expr.directCode(props.runtime.toLambdaRuntime())], + ['runtime', expr.directCode(toLambdaRuntime(props.runtime))], ]); this.buildConstructor({ constructorPropsType: _interface.type, @@ -174,10 +175,10 @@ export abstract class CdkCustomResourceClass extends ClassType { } /** - * Builds a `CdkCustomResourceProvider` class. + * Builds a code generated custom resource provider class. */ - public static buildCdkCustomResourceProvider(scope: CdkCustomResourceModule, props: CdkCustomResourceClassProps): CdkCustomResourceClass { - return new (class CdkCustomResourceProvider extends CdkCustomResourceClass { + public static buildCustomResourceProvider(scope: HandlerFrameworkModule, props: HandlerFrameworkClassProps): HandlerFrameworkClass { + return new (class CustomResourceProvider extends HandlerFrameworkClass { protected readonly externalModules: ExternalModule[] = [PATH_MODULE, CONSTRUCTS_MODULE]; public constructor() { @@ -256,7 +257,7 @@ export abstract class CdkCustomResourceClass extends ClassType { const superProps = new ObjectLiteral([ new Splat(expr.ident('props')), ['codeDirectory', expr.directCode(`path.join(__dirname, '${props.codeDirectory}')`)], - ['runtimeName', expr.lit(props.runtime.name)], + ['runtimeName', expr.lit(props.runtime)], ]); this.buildConstructor({ constructorPropsType: scope.coreInternal @@ -275,7 +276,7 @@ export abstract class CdkCustomResourceClass extends ClassType { */ protected abstract readonly externalModules: ExternalModule[]; - private importExternalModulesInto(scope: CdkCustomResourceModule) { + private importExternalModulesInto(scope: HandlerFrameworkModule) { for (const module of this.externalModules) { if (!scope.hasExternalModule(module)) { scope.addExternalModule(module); @@ -284,7 +285,7 @@ export abstract class CdkCustomResourceClass extends ClassType { } } - private importExternalModuleInto(scope: CdkCustomResourceModule, module: ExternalModule) { + private importExternalModuleInto(scope: HandlerFrameworkModule, module: ExternalModule) { switch (module.fqn) { case PATH_MODULE.fqn: { PATH_MODULE.import(scope, 'path'); @@ -320,7 +321,7 @@ export abstract class CdkCustomResourceClass extends ClassType { } } - private getOrCreateInterface(scope: CdkCustomResourceModule, spec: InterfaceSpec) { + private getOrCreateInterface(scope: HandlerFrameworkModule, spec: InterfaceSpec) { const existing = scope.getInterface(spec.name); if (existing) { return existing; diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/config.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/config.ts index c44f566f033b1..f17c002cc8217 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/config.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/config.ts @@ -1,87 +1,105 @@ /* eslint-disable import/no-extraneous-dependencies */ import * as path from 'path'; -import { Runtime } from './runtime'; /** - * Custom resource provider types. + * Handler framework runtimes used for code generation. */ -export enum ProviderType { +export enum Runtime { /** - * `CdkFunction` + * The NodeJs 18.x runtime */ - CDK_FUNCTION = 'CdkFunction', + NODEJS_18_X = 'nodejs18.x', /** - * `CdkSingletonFunction` + * The Python 3.9 runtime */ - CDK_SINGLETON_FUNCTION = 'CdkSingletonFunction', + PYTHON_3_9 = 'python3.9', /** - * `CdkCustomResourceProvider` + * The Python 3.10 runtime */ - CDK_CUSTOM_RESOURCE_PROVIDER = 'CdkCustomResourceProvider', + PYTHON_3_10 = 'python3.10', +} +/** + * Handler framework component types. + */ +export enum ComponentType { /** - * Do not create a custom resource provider. + * Code generated Lambda function + */ + FUNCTION = 'Function', + + /** + * Code generate a Lambda singleton function + */ + SINGLETON_FUNCTION = 'SingletonFunction', + + /** + * Code generate a custom resource provider. + */ + CUSTOM_RESOURCE_PROVIDER = 'CustomResourceProvider', + + /** + * Do not create a handler framework component. * * Note: This is used to just move source code for airlifting. */ - CDK_NO_OP = 'CdkNoOp', + NO_OP = 'NoOp', } /** - * Properites used to generate individual custom resource providers. + * Properites used to generate individual framework components. */ -export interface ProviderProps { +export interface ComponentProps { /** - * The custom resource provider type to generate. + * The framework component type to generate. */ - readonly type: ProviderType; + readonly type: ComponentType; /** - * The source code that will be executed by the provider. + * The source code that will be executed by the framework component. */ readonly sourceCode: string; /** - * Runtimes that are compatible with the provider's source code. + * The runtime that is compatible with the framework component's source code. + * + * @default Runtime.NODEJS_18_X */ - readonly compatibleRuntimes: Runtime[]; + readonly runtime?: Runtime; /** - * The name of the method within your code that the provider calls to execute your function. + * The name of the method within your code that the framework component calls. * * @default 'index.handler' */ readonly handler?: string; /** - * Prevents the source code from being minified and bundled. This is needed for python - * files or for source code with a `require` import. + * Whether the source code should be minified and bundled. * - * @default false + * @default true */ - readonly preventMinifyAndBundle?: boolean; + readonly minifyAndBundle?: boolean; } -export type CustomResourceFrameworkConfig = { [module: string]: { [identifier: string]: ProviderProps[] } }; +export type HandlerFrameworkConfig = { [module: string]: { [identifier: string]: ComponentProps[] } }; -export const config: CustomResourceFrameworkConfig = { +export const config: HandlerFrameworkConfig = { 'aws-amplify-alpha': { 'asset-deployment-handler': [ { - type: ProviderType.CDK_NO_OP, + type: ComponentType.NO_OP, sourceCode: path.resolve(__dirname, '..', 'aws-amplify-alpha', 'asset-deployment-handler', 'index.ts'), - compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], }, 'aws-certificatemanager': { 'certificate-request-provider': [ { - type: ProviderType.CDK_FUNCTION, + type: ComponentType.FUNCTION, sourceCode: path.resolve(__dirname, '..', 'aws-certificatemanager', 'dns-validated-certificate-handler', 'index.js'), - compatibleRuntimes: [Runtime.NODEJS_18_X], handler: 'index.certificateRequestHandler', }, ], @@ -89,24 +107,21 @@ export const config: CustomResourceFrameworkConfig = { 'aws-cloudfront': { 'cross-region-string-param-reader-provider': [ { - type: ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER, + type: ComponentType.CUSTOM_RESOURCE_PROVIDER, sourceCode: path.resolve(__dirname, '..', 'aws-cloudfront', 'edge-function', 'index.js'), - compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], }, 'aws-dynamodb': { 'replica-provider': [ { - type: ProviderType.CDK_FUNCTION, + type: ComponentType.FUNCTION, sourceCode: path.resolve(__dirname, '..', 'aws-dynamodb', 'replica-handler', 'index.ts'), - compatibleRuntimes: [Runtime.NODEJS_18_X], handler: 'index.onEventHandler', }, { - type: ProviderType.CDK_FUNCTION, + type: ComponentType.FUNCTION, sourceCode: path.resolve(__dirname, '..', 'aws-dynamodb', 'replica-handler', 'index.ts'), - compatibleRuntimes: [Runtime.NODEJS_18_X], handler: 'index.isCompleteHandler', }, ], @@ -114,249 +129,228 @@ export const config: CustomResourceFrameworkConfig = { 'aws-ec2': { 'restrict-default-sg-provider': [ { - type: ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER, + type: ComponentType.CUSTOM_RESOURCE_PROVIDER, sourceCode: path.resolve(__dirname, '..', 'aws-ec2', 'restrict-default-security-group-handler', 'index.ts'), - compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], }, 'aws-ecr': { 'auto-delete-images-provider': [ { - type: ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER, + type: ComponentType.CUSTOM_RESOURCE_PROVIDER, sourceCode: path.resolve(__dirname, '..', 'aws-ecr', 'auto-delete-images-handler', 'index.ts'), - compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], }, 'aws-ecs': { 'drain-hook-provider': [ { - type: ProviderType.CDK_FUNCTION, + type: ComponentType.FUNCTION, sourceCode: path.resolve(__dirname, '..', 'aws-ecs', 'lambda-source', 'index.py'), - compatibleRuntimes: [Runtime.PYTHON_3_9], + runtime: Runtime.PYTHON_3_9, handler: 'index.lambda_handler', - preventMinifyAndBundle: true, + minifyAndBundle: false, }, ], }, 'aws-eks': { 'cluster-resource-provider': [ { - type: ProviderType.CDK_FUNCTION, + type: ComponentType.FUNCTION, sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'cluster-resource-handler', 'index.ts'), - compatibleRuntimes: [Runtime.NODEJS_18_X], handler: 'index.onEvent', }, { - type: ProviderType.CDK_FUNCTION, + type: ComponentType.FUNCTION, sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'cluster-resource-handler', 'index.ts'), - compatibleRuntimes: [Runtime.NODEJS_18_X], handler: 'index.isComplete', }, ], 'kubectl-provider': [ { - type: ProviderType.CDK_FUNCTION, + type: ComponentType.FUNCTION, sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'index.py'), - compatibleRuntimes: [Runtime.PYTHON_3_10], - preventMinifyAndBundle: true, + runtime: Runtime.PYTHON_3_10, + minifyAndBundle: false, }, { - type: ProviderType.CDK_NO_OP, + type: ComponentType.NO_OP, sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'apply', '__init__.py'), - compatibleRuntimes: [Runtime.PYTHON_3_10], - preventMinifyAndBundle: true, + runtime: Runtime.PYTHON_3_10, + minifyAndBundle: false, }, { - type: ProviderType.CDK_NO_OP, + type: ComponentType.NO_OP, sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'get', '__init__.py'), - compatibleRuntimes: [Runtime.PYTHON_3_10], - preventMinifyAndBundle: true, + runtime: Runtime.PYTHON_3_10, + minifyAndBundle: false, }, { - type: ProviderType.CDK_NO_OP, + type: ComponentType.NO_OP, sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'helm', '__init__.py'), - compatibleRuntimes: [Runtime.PYTHON_3_10], - preventMinifyAndBundle: true, + runtime: Runtime.PYTHON_3_10, + minifyAndBundle: false, }, { - type: ProviderType.CDK_NO_OP, + type: ComponentType.NO_OP, sourceCode: path.resolve(__dirname, '..', 'aws-eks', 'kubectl-handler', 'patch', '__init__.py'), - compatibleRuntimes: [Runtime.PYTHON_3_10], - preventMinifyAndBundle: true, + runtime: Runtime.PYTHON_3_10, + minifyAndBundle: false, }, ], }, 'aws-events-targets': { 'aws-api-provider': [ { - type: ProviderType.CDK_SINGLETON_FUNCTION, + type: ComponentType.SINGLETON_FUNCTION, sourceCode: path.resolve(__dirname, '..', 'aws-events-targets', 'aws-api-handler', 'index.ts'), - compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], }, 'aws-iam': { 'oidc-provider': [ { - type: ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER, + type: ComponentType.CUSTOM_RESOURCE_PROVIDER, sourceCode: path.resolve(__dirname, '..', 'aws-iam', 'oidc-handler', 'index.ts'), - compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], }, 'aws-logs': { 'log-retention': [ { - type: ProviderType.CDK_NO_OP, + type: ComponentType.NO_OP, sourceCode: path.resolve(__dirname, '..', 'aws-logs', 'log-retention-handler', 'index.ts'), - compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], }, 'aws-redshift-alpha': { 'cluster-reboot-provider': [ { - type: ProviderType.CDK_SINGLETON_FUNCTION, + type: ComponentType.SINGLETON_FUNCTION, sourceCode: path.resolve(__dirname, '..', 'aws-redshift-alpha', 'cluster-parameter-change-reboot-handler', 'index.ts'), - compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], }, 'aws-route53': { 'cross-account-zone-delegation-provider': [ { - type: ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER, + type: ComponentType.CUSTOM_RESOURCE_PROVIDER, sourceCode: path.resolve(__dirname, '..', 'aws-route53', 'cross-account-zone-delegation-handler', 'index.ts'), - compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], 'delete-existing-record-set-provider': [ { - type: ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER, + type: ComponentType.CUSTOM_RESOURCE_PROVIDER, sourceCode: path.resolve(__dirname, '..', 'aws-route53', 'delete-existing-record-set-handler', 'index.ts'), - compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], }, 'aws-s3': { 'auto-delete-objects-provider': [ { - type: ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER, + type: ComponentType.CUSTOM_RESOURCE_PROVIDER, sourceCode: path.resolve(__dirname, '..', 'aws-s3', 'auto-delete-objects-handler', 'index.ts'), - compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], 'notifications-resource-handler': [ { - type: ProviderType.CDK_NO_OP, + type: ComponentType.NO_OP, sourceCode: path.resolve(__dirname, '..', 'aws-s3', 'notifications-resource-handler', 'index.py'), - compatibleRuntimes: [Runtime.PYTHON_3_9], - preventMinifyAndBundle: true, + runtime: Runtime.PYTHON_3_9, + minifyAndBundle: false, }, ], }, 'aws-s3-deployment': { 'bucket-deployment-provider': [ { - type: ProviderType.CDK_SINGLETON_FUNCTION, + type: ComponentType.SINGLETON_FUNCTION, sourceCode: path.resolve(__dirname, '..', 'aws-s3-deployment', 'bucket-deployment-handler', 'index.py'), - compatibleRuntimes: [Runtime.PYTHON_3_9], - preventMinifyAndBundle: true, + runtime: Runtime.PYTHON_3_9, + minifyAndBundle: false, }, ], }, 'aws-ses': { 'drop-spam-provider': [ { - type: ProviderType.CDK_SINGLETON_FUNCTION, + type: ComponentType.SINGLETON_FUNCTION, sourceCode: path.resolve(__dirname, '..', 'aws-ses', 'drop-spam-handler', 'index.ts'), - compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], }, 'aws-stepfunctions-tasks': { 'eval-nodejs-provider': [ { - type: ProviderType.CDK_SINGLETON_FUNCTION, + type: ComponentType.SINGLETON_FUNCTION, sourceCode: path.resolve(__dirname, '..', 'aws-stepfunctions-tasks', 'eval-nodejs-handler', 'index.ts'), - compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], 'role-policy-provider': [ { - type: ProviderType.CDK_SINGLETON_FUNCTION, + type: ComponentType.SINGLETON_FUNCTION, sourceCode: path.resolve(__dirname, '..', 'aws-stepfunctions-tasks', 'role-policy-handler', 'index.py'), - compatibleRuntimes: [Runtime.PYTHON_3_9], - preventMinifyAndBundle: true, + runtime: Runtime.PYTHON_3_9, + minifyAndBundle: false, }, ], }, 'aws-synthetics': { 'auto-delete-underlying-resources-provider': [ { - type: ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER, + type: ComponentType.CUSTOM_RESOURCE_PROVIDER, sourceCode: path.resolve(__dirname, '..', 'aws-synthetics', 'auto-delete-underlying-resources-handler', 'index.ts'), - compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], }, 'core': { 'cfn-utils-provider': [ { - type: ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER, + type: ComponentType.CUSTOM_RESOURCE_PROVIDER, sourceCode: path.resolve(__dirname, '..', 'core', 'cfn-utils-provider', 'index.ts'), - compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], 'cross-region-ssm-writer-provider': [ { - type: ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER, + type: ComponentType.CUSTOM_RESOURCE_PROVIDER, sourceCode: path.resolve(__dirname, '..', 'core', 'cross-region-ssm-writer-handler', 'index.ts'), - compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], 'cross-region-ssm-reader-provider': [ { - type: ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER, + type: ComponentType.CUSTOM_RESOURCE_PROVIDER, sourceCode: path.resolve(__dirname, '..', 'core', 'cross-region-ssm-reader-handler', 'index.ts'), - compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], 'nodejs-entrypoint-provider': [ { - type: ProviderType.CDK_NO_OP, + type: ComponentType.NO_OP, sourceCode: path.resolve(__dirname, '..', 'core', 'nodejs-entrypoint-handler', 'index.js'), - compatibleRuntimes: [Runtime.NODEJS_18_X], - preventMinifyAndBundle: true, + minifyAndBundle: false, }, ], }, 'custom-resources': { 'aws-custom-resource-provider': [ { - type: ProviderType.CDK_SINGLETON_FUNCTION, + type: ComponentType.SINGLETON_FUNCTION, sourceCode: path.resolve(__dirname, '..', 'custom-resources', 'aws-custom-resource-handler', 'index.ts'), - compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], }, 'pipelines': { 'approve-lambda': [ { - type: ProviderType.CDK_FUNCTION, + type: ComponentType.FUNCTION, sourceCode: path.resolve(__dirname, '..', 'pipelines', 'approve-lambda', 'index.ts'), - compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], }, 'triggers': { 'trigger-provider': [ { - type: ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER, + type: ComponentType.CUSTOM_RESOURCE_PROVIDER, sourceCode: path.resolve(__dirname, '..', 'triggers', 'lambda', 'index.ts'), - compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], }, diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/framework.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/framework.ts index 6e8e0c79d66bc..b77942905fdc3 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/framework.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/framework.ts @@ -1,12 +1,11 @@ /* eslint-disable import/no-extraneous-dependencies */ import { ExternalModule, InterfaceType, Module, TypeScriptRenderer } from '@cdklabs/typewriter'; import * as fs from 'fs-extra'; -import { CdkCustomResourceClass, CdkCustomResourceClassProps } from './classes'; -import { ProviderType, ProviderProps } from './config'; -import { Runtime } from './runtime'; -import { RuntimeDeterminer } from './utils/runtime-determiner'; +import { HandlerFrameworkClass, HandlerFrameworkClassProps } from './classes'; +import { ComponentType, ComponentProps, Runtime } from './config'; +import { buildComponentName } from './utils/framework-utils'; -export class CdkCustomResourceModule extends Module { +export class HandlerFrameworkModule extends Module { /** * The latest nodejs runtime version available across all AWS regions. */ @@ -15,7 +14,7 @@ export class CdkCustomResourceModule extends Module { private readonly renderer = new TypeScriptRenderer(); private readonly externalModules = new Map(); private readonly _interfaces = new Map(); - private _hasProviders = false; + private _hasComponents = false; /** * Whether the module being generated will live inside of aws-cdk-lib/core. @@ -23,10 +22,10 @@ export class CdkCustomResourceModule extends Module { public readonly coreInternal: boolean; /** - * Whether the module contains custom resource providers. + * Whether the module contains handler framework components. */ - public get hasProviders() { - return this._hasProviders; + public get hasComponents() { + return this._hasComponents; } public constructor(fqn: string) { @@ -37,37 +36,34 @@ export class CdkCustomResourceModule extends Module { /** * Build a framework component inside of this module. */ - public build(provider: ProviderProps, codeDirectory: string) { - if (provider.type === ProviderType.CDK_NO_OP) { + public build(component: ComponentProps, codeDirectory: string) { + if (component.type === ComponentType.NO_OP) { return; } - this._hasProviders = true; + this._hasComponents = true; - const handler = provider.handler ?? 'index.handler'; - const name = this.buildProviderName(handler); + const handler = component.handler ?? 'index.handler'; + const name = buildComponentName(this.fqn, component.type, handler); - const props: CdkCustomResourceClassProps = { + const props: HandlerFrameworkClassProps = { name, handler, codeDirectory, - runtime: RuntimeDeterminer.determineLatestRuntime( - provider.compatibleRuntimes, - CdkCustomResourceModule.DEFAULT_RUNTIME, - ), + runtime: component.runtime ?? HandlerFrameworkModule.DEFAULT_RUNTIME, }; - switch (provider.type) { - case ProviderType.CDK_FUNCTION: { - CdkCustomResourceClass.buildCdkFunction(this, props); + switch (component.type) { + case ComponentType.FUNCTION: { + HandlerFrameworkClass.buildFunction(this, props); break; } - case ProviderType.CDK_SINGLETON_FUNCTION: { - CdkCustomResourceClass.buildCdkSingletonFunction(this, props); + case ComponentType.SINGLETON_FUNCTION: { + HandlerFrameworkClass.buildSingletonFunction(this, props); break; } - case ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER: { - CdkCustomResourceClass.buildCdkCustomResourceProvider(this, props); + case ComponentType.CUSTOM_RESOURCE_PROVIDER: { + HandlerFrameworkClass.buildCustomResourceProvider(this, props); break; } } @@ -84,9 +80,7 @@ export class CdkCustomResourceModule extends Module { * Add an external module to be imported. */ public addExternalModule(module: ExternalModule) { - if (!this.externalModules.has(module.fqn)) { - this.externalModules.set(module.fqn, true); - } + this.externalModules.set(module.fqn, true); } /** @@ -109,13 +103,4 @@ export class CdkCustomResourceModule extends Module { public getInterface(name: string) { return this._interfaces.get(name); } - - private buildProviderName(entrypoint: string) { - const id = this.fqn.split('/').at(-1)?.replace('-provider', '') ?? ''; - const handler = entrypoint.split('.').at(-1)?.replace(/[_Hh]andler/g, '') ?? ''; - const name = (id.replace( - /-([a-z])/g, (s) => { return s[1].toUpperCase(); }, - )) + (handler.charAt(0).toUpperCase() + handler.slice(1)); - return name.charAt(0).toUpperCase() + name.slice(1) + 'Provider'; - } } diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/runtime.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/runtime.ts deleted file mode 100644 index eecd73d989724..0000000000000 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/runtime.ts +++ /dev/null @@ -1,133 +0,0 @@ -export enum RuntimeFamily { - NODEJS, - PYTHON, - JAVA, - RUBY, -} - -/** - * Properties used to initialize a framework runtime. - */ -interface RuntimeProps { - /** - * Whether or not this runtime is deprecated. - * - * @default false - */ - readonly isDeprecated?: boolean; -} - -/** - * Custom resource framework runtimes used for code generation. - */ -export class Runtime { - /** - * The NodeJs 14.x runtime (nodejs14.x) - */ - public static readonly NODEJS_12_X = new Runtime('nodejs12.x', RuntimeFamily.NODEJS, { - isDeprecated: true, - }); - - /** - * The NodeJs 14.x runtime (nodejs14.x) - */ - public static readonly NODEJS_14_X = new Runtime('nodejs14.x', RuntimeFamily.NODEJS, { - isDeprecated: true, - }); - - /** - * The NodeJS 16.x runtime (nodejs16.x) - */ - public static readonly NODEJS_16_X = new Runtime('nodejs16.x', RuntimeFamily.NODEJS); - - /** - * The NodeJS 18.x runtime (nodejs18.x) - */ - public static readonly NODEJS_18_X = new Runtime('nodejs18.x', RuntimeFamily.NODEJS); - - /** - * The NodeJS 20.x runtime (nodejs20.x) - */ - public static readonly NODEJS_20_X = new Runtime('nodejs20.x', RuntimeFamily.NODEJS); - - /** - * The Python 2.7 runtime (python2.7) - */ - public static readonly PYTHON_2_7 = new Runtime('python2.7', RuntimeFamily.PYTHON, { - isDeprecated: true, - }); - - /** - * The Python 3.6 runtime (python3.6) - */ - public static readonly PYTHON_3_6 = new Runtime('python3.6', RuntimeFamily.PYTHON, { - isDeprecated: true, - }); - - /** - * The Python 3.7 runtime (python3.7) - */ - public static readonly PYTHON_3_7 = new Runtime('python3.7', RuntimeFamily.PYTHON); - - /** - * The Python 3.9 runtime (python3.9) - */ - public static readonly PYTHON_3_9 = new Runtime('python3.9', RuntimeFamily.PYTHON); - - /** - * The Python 3.10 runtime (python3.10) - */ - public static readonly PYTHON_3_10 = new Runtime('python3.10', RuntimeFamily.PYTHON); - - /** - * The Python 3.11 runtime (python3.11) - */ - public static readonly PYTHON_3_11 = new Runtime('python3.11', RuntimeFamily.PYTHON); - - /** - * The Python 3.12 runtime (python3.12) - */ - public static readonly PYTHON_3_12 = new Runtime('python3.12', RuntimeFamily.PYTHON); - - /** - * The Java 17 runtime (java17) - */ - public static readonly JAVA_17 = new Runtime('java17', RuntimeFamily.JAVA); - - /** - * The Ruby 3.2 runtime (ruby3.2) - */ - public static readonly RUBY_3_2 = new Runtime('ruby3.2', RuntimeFamily.RUBY); - - public readonly name: string; - public readonly family: RuntimeFamily; - public readonly isDeprecated: boolean; - - private constructor(name: string, family: RuntimeFamily, props: RuntimeProps = {}) { - this.name = name; - this.family = family; - this.isDeprecated = props.isDeprecated ?? false; - } - - public runtimeEquals(other: Runtime): boolean { - return other.name === this.name && other.family === this.family; - } - - public toLambdaRuntime() { - switch (this.name) { - case 'nodejs16.x': { - return 'lambda.Runtime.NODEJS_16_X'; - } - case 'nodejs18.x': { - return 'lambda.Runtime.NODEJS_18_X'; - } - case 'python3.9': { - return 'lambda.Runtime.PYTHON_3_9'; - } - case 'python3.10': { - return 'lambda.Runtime.PYTHON_3_10'; - } - } - throw new Error('Unable to convert runtime to lambda runtime'); - } -} diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/utils/framework-utils.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/utils/framework-utils.ts new file mode 100644 index 0000000000000..9184317516cac --- /dev/null +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/utils/framework-utils.ts @@ -0,0 +1,26 @@ +import { ComponentType, Runtime } from '../config'; + +export function buildComponentName(fqn: string, type: ComponentType, entrypoint: string) { + const id = fqn.split('/').at(-1)?.replace('-provider', '') ?? ''; + const handler = entrypoint.split('.').at(-1)?.replace(/[_Hh]andler/g, '') ?? ''; + const name = (id.replace( + /-([a-z])/g, (s) => { return s[1].toUpperCase(); }, + )) + (handler.charAt(0).toUpperCase() + handler.slice(1)); + return name.charAt(0).toUpperCase() + + name.slice(1) + + type === ComponentType.CUSTOM_RESOURCE_PROVIDER ? 'Provider' : 'Function'; +} + +export function toLambdaRuntime(runtime: Runtime) { + switch (runtime) { + case Runtime.NODEJS_18_X: { + return 'lambda.Runtime.NODEJS_18_X'; + } + case Runtime.PYTHON_3_9: { + return 'lambda.Runtime.PYTHON_3_9'; + } + case Runtime.PYTHON_3_10: { + return 'lambda.Runtime.PYTHON_3_10'; + } + } +} diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/utils/runtime-determiner.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/utils/runtime-determiner.ts deleted file mode 100644 index 6de061c928e6e..0000000000000 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/utils/runtime-determiner.ts +++ /dev/null @@ -1,109 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ -import { Runtime, RuntimeFamily } from '../runtime'; - -/** - * A utility class used to determine the latest runtime for a specific runtime family - */ -export class RuntimeDeterminer { - /** - * Determines the latest runtime from a list of runtimes. - * - * Note: runtimes must only be nodejs or python. Nodejs runtimes will be given preference over - * python runtimes. - * - * @param runtimes the list of runtimes to search in - * @returns the latest nodejs or python runtime found, otherwise undefined if no nodejs or python - * runtimes are specified - */ - public static determineLatestRuntime(runtimes: Runtime[], defaultRuntime: Runtime) { - if (runtimes.length === 0) { - throw new Error('You must specify at least one compatible runtime'); - } - - const nodeJsRuntimes = runtimes.filter(runtime => runtime.family === RuntimeFamily.NODEJS); - const latestNodeJsRuntime = RuntimeDeterminer.latestNodeJsRuntime(nodeJsRuntimes, defaultRuntime); - if (latestNodeJsRuntime !== undefined) { - if (latestNodeJsRuntime.isDeprecated) { - throw new Error(`Latest nodejs runtime ${latestNodeJsRuntime} is deprecated. You must upgrade to the latest code compatible nodejs runtime`); - } - return latestNodeJsRuntime; - } - - const pythonRuntimes = runtimes.filter(runtime => runtime.family === RuntimeFamily.PYTHON); - const latestPythonRuntime = RuntimeDeterminer.latestPythonRuntime(pythonRuntimes); - if (latestPythonRuntime !== undefined) { - if (latestPythonRuntime.isDeprecated) { - throw new Error(`Latest python runtime ${latestPythonRuntime} is deprecated. You must upgrade to the latest code compatible python runtime`); - } - return latestPythonRuntime; - } - - throw new Error('Compatible runtimes must contain only nodejs or python runtimes'); - } - - private static latestNodeJsRuntime(nodeJsRuntimes: Runtime[], defaultRuntime: Runtime) { - if (nodeJsRuntimes.length === 0) { - return undefined; - } - - if (nodeJsRuntimes.some(runtime => runtime.runtimeEquals(defaultRuntime))) { - return defaultRuntime; - } - - let latestRuntime = nodeJsRuntimes[0]; - for (let idx = 1; idx < nodeJsRuntimes.length; idx++) { - latestRuntime = RuntimeDeterminer.latestRuntime(latestRuntime, nodeJsRuntimes[idx], RuntimeFamily.NODEJS); - } - - return latestRuntime; - } - - private static latestPythonRuntime(pythonRuntimes: Runtime[]) { - if (pythonRuntimes.length === 0) { - return undefined; - } - - let latestRuntime = pythonRuntimes[0]; - for (let idx = 1; idx < pythonRuntimes.length; idx++) { - latestRuntime = RuntimeDeterminer.latestRuntime(latestRuntime, pythonRuntimes[idx], RuntimeFamily.PYTHON); - } - - return latestRuntime; - } - - private static latestRuntime(runtime1: Runtime, runtime2: Runtime, family: RuntimeFamily) { - let sliceStart: number; - switch (family) { - case RuntimeFamily.NODEJS: { - sliceStart = 'nodejs'.length; - break; - } - case RuntimeFamily.PYTHON: { - sliceStart = 'python'.length; - break; - } - default: { - sliceStart = 0; - break; - } - } - - const version1 = runtime1.name.slice(sliceStart).split('.'); - const version2 = runtime2.name.slice(sliceStart).split('.'); - - const versionLength = Math.min(version1.length, version2.length); - for (let idx = 0; idx < versionLength; idx++) { - if (parseInt(version1[idx]) > parseInt(version2[idx])) { - return runtime1; - } - - if (parseInt(version1[idx]) < parseInt(version2[idx])) { - return runtime2; - } - } - - return runtime1; - } - - private constructor() {} -} diff --git a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts index 7fd7ac6a854a8..965b9a9d1d7d7 100644 --- a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts +++ b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts @@ -1,29 +1,29 @@ import * as fs from 'fs'; import * as path from 'path'; import * as esbuild from 'esbuild'; -import { config, ProviderProps } from '../lib/custom-resources-framework/config'; -import { CdkCustomResourceModule } from '../lib/custom-resources-framework/framework'; +import { config, ComponentProps } from '../lib/custom-resources-framework/config'; +import { HandlerFrameworkModule } from '../lib/custom-resources-framework/framework'; -const framework: { [fqn: string]: ProviderProps[] } = {}; +const framework: { [fqn: string]: ComponentProps[] } = {}; async function main() { recurse(config, []); - for (const [fqn, providers] of Object.entries(framework)) { - const module = new CdkCustomResourceModule(fqn); - for (const provider of providers) { - const outfile = calculateOutfile(provider.sourceCode); - if (provider.preventMinifyAndBundle) { - fs.mkdirSync(path.dirname(outfile), { recursive: true }); - fs.copyFileSync(provider.sourceCode, outfile); + for (const [fqn, components] of Object.entries(framework)) { + const module = new HandlerFrameworkModule(fqn); + for (const component of components) { + const outfile = calculateOutfile(component.sourceCode); + if (component.minifyAndBundle) { + await minifyAndBundle(component.sourceCode, outfile); } else { - await minifyAndBundle(provider.sourceCode, outfile); + fs.mkdirSync(path.dirname(outfile), { recursive: true }); + fs.copyFileSync(component.sourceCode, outfile); } const codeDirectory = path.dirname(outfile).split('/').pop() ?? ''; - module.build(provider, codeDirectory); + module.build(component, codeDirectory); } - if (module.hasProviders) { + if (module.hasComponents) { module.renderTo(`dist/${fqn}.generated.ts`); } } diff --git a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/framework.test.ts b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/framework.test.ts index 6583376455dcc..ab4ff4406231a 100644 --- a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/framework.test.ts +++ b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/framework.test.ts @@ -1,9 +1,8 @@ import * as os from 'os'; import * as path from 'path'; import * as fs from 'fs-extra'; -import { ProviderProps, ProviderType } from '../../lib/custom-resources-framework/config'; -import { CdkCustomResourceModule } from '../../lib/custom-resources-framework/framework'; -import { Runtime } from '../../lib/custom-resources-framework/runtime'; +import { ComponentProps, ComponentType, Runtime } from '../../lib/custom-resources-framework/config'; +import { HandlerFrameworkModule } from '../../lib/custom-resources-framework/framework'; import { calculateOutfile } from '../../scripts/generate'; /* eslint-disable no-console */ @@ -18,11 +17,10 @@ describe('framework', () => { test('can codegen cdk function', () => { // GIVEN - const module = new CdkCustomResourceModule('cdk-testing/test-provider'); - const component: ProviderProps = { - type: ProviderType.CDK_FUNCTION, + const module = new HandlerFrameworkModule('cdk-testing/test-provider'); + const component: ComponentProps = { + type: ComponentType.FUNCTION, sourceCode, - compatibleRuntimes: [Runtime.NODEJS_18_X], }; const outfile = calculateOutfile(sourceCode); module.build(component, path.dirname(outfile).split('/').pop() ?? 'dist'); @@ -38,11 +36,10 @@ describe('framework', () => { test('can codegen cdk singleton function', () => { // GIVEN - const module = new CdkCustomResourceModule('cdk-testing/test-provider'); - const component: ProviderProps = { - type: ProviderType.CDK_SINGLETON_FUNCTION, + const module = new HandlerFrameworkModule('cdk-testing/test-provider'); + const component: ComponentProps = { + type: ComponentType.SINGLETON_FUNCTION, sourceCode, - compatibleRuntimes: [Runtime.NODEJS_18_X], }; const outfile = calculateOutfile(sourceCode); module.build(component, path.dirname(outfile).split('/').pop() ?? 'dist'); @@ -58,11 +55,10 @@ describe('framework', () => { test('can codegen cdk custom resource provider for core internal', () => { // GIVEN - const module = new CdkCustomResourceModule('cdk-testing/test-provider'); - const component: ProviderProps = { - type: ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER, + const module = new HandlerFrameworkModule('cdk-testing/test-provider'); + const component: ComponentProps = { + type: ComponentType.CUSTOM_RESOURCE_PROVIDER, sourceCode, - compatibleRuntimes: [Runtime.NODEJS_18_X], }; const outfile = calculateOutfile(sourceCode); module.build(component, path.dirname(outfile).split('/').pop() ?? 'dist'); @@ -78,11 +74,10 @@ describe('framework', () => { test('can codegen cdk custom resource provider for core internal', () => { // GIVEN - const module = new CdkCustomResourceModule('core/test-provider'); - const component: ProviderProps = { - type: ProviderType.CDK_CUSTOM_RESOURCE_PROVIDER, + const module = new HandlerFrameworkModule('core/test-provider'); + const component: ComponentProps = { + type: ComponentType.CUSTOM_RESOURCE_PROVIDER, sourceCode, - compatibleRuntimes: [Runtime.NODEJS_18_X], }; const outfile = calculateOutfile(sourceCode); module.build(component, path.dirname(outfile).split('/').pop() ?? 'dist'); diff --git a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/utils/runtime-determiner.test.ts b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/utils/runtime-determiner.test.ts deleted file mode 100644 index 11dd6f23d1b64..0000000000000 --- a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/utils/runtime-determiner.test.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { Runtime } from '../../../lib/custom-resources-framework/runtime'; -import { RuntimeDeterminer } from '../../../lib/custom-resources-framework/utils/runtime-determiner'; - -const DEFAULT_RUNTIME = Runtime.NODEJS_18_X; - -describe('latest runtime', () => { - test('selects default runtime', () => { - // GIVEN - const runtimes = [Runtime.NODEJS_16_X, Runtime.PYTHON_3_12, Runtime.NODEJS_18_X]; - - // WHEN - const latestRuntime = RuntimeDeterminer.determineLatestRuntime(runtimes, DEFAULT_RUNTIME); - - // THEN - expect(latestRuntime?.runtimeEquals(DEFAULT_RUNTIME)).toEqual(true); - }); - - test('selects latest nodejs runtime', () => { - // GIVEN - const runtimes = [Runtime.NODEJS_16_X, Runtime.PYTHON_3_12, Runtime.NODEJS_14_X, Runtime.PYTHON_3_11, Runtime.NODEJS_20_X]; - - // WHEN - const latestRuntime = RuntimeDeterminer.determineLatestRuntime(runtimes, DEFAULT_RUNTIME); - - // THEN - expect(latestRuntime?.runtimeEquals(Runtime.NODEJS_20_X)).toEqual(true); - }); - - test('selects latest python runtime', () => { - // GIVEN - const runtimes = [Runtime.PYTHON_3_10, Runtime.PYTHON_3_11, Runtime.PYTHON_3_7]; - - // WHEN - const latestRuntime = RuntimeDeterminer.determineLatestRuntime(runtimes, DEFAULT_RUNTIME); - - // THEN - expect(latestRuntime?.runtimeEquals(Runtime.PYTHON_3_11)).toEqual(true); - }); - - test('throws if no runtimes are specified', () => { - // GIVEN - const runtimes = []; - - // WHEN / THEN - expect(() => { - RuntimeDeterminer.determineLatestRuntime(runtimes, DEFAULT_RUNTIME); - }).toThrow('You must specify at least one compatible runtime'); - }); - - test('throws if latest nodejs runtime is deprecated', () => { - // GIVEN - const runtimes = [Runtime.NODEJS_12_X, Runtime.NODEJS_14_X]; - - // WHEN / THEN - expect(() => { - RuntimeDeterminer.determineLatestRuntime(runtimes, DEFAULT_RUNTIME); - }).toThrow(`Latest nodejs runtime ${Runtime.NODEJS_14_X} is deprecated. You must upgrade to the latest code compatible nodejs runtime`); - }); - - test('throws if latest python runtime is deprecated', () => { - // GIVEN - const runtimes = [Runtime.PYTHON_2_7, Runtime.PYTHON_3_6]; - - // WHEN / THEN - expect(() => { - RuntimeDeterminer.determineLatestRuntime(runtimes, DEFAULT_RUNTIME); - }).toThrow(`Latest python runtime ${Runtime.PYTHON_3_6} is deprecated. You must upgrade to the latest code compatible python runtime`); - }); - - test('throws if runtimes are neither nodejs nor python', () => { - // GIVEN - const runtimes = [Runtime.JAVA_17, Runtime.RUBY_3_2]; - - // WHEN / THEN - expect(() => { - RuntimeDeterminer.determineLatestRuntime(runtimes, DEFAULT_RUNTIME); - }).toThrow('Compatible runtimes must contain only nodejs or python runtimes'); - }); -}); \ No newline at end of file From 7b3e7c10eec9962c0297917c3d4c02bbff21435b Mon Sep 17 00:00:00 2001 From: Francis Date: Thu, 14 Dec 2023 14:15:00 -0800 Subject: [PATCH 65/73] name generator Signed-off-by: Francis --- .../lib/custom-resources-framework/utils/framework-utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/utils/framework-utils.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/utils/framework-utils.ts index 9184317516cac..50d3e645f4056 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/utils/framework-utils.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/utils/framework-utils.ts @@ -8,7 +8,7 @@ export function buildComponentName(fqn: string, type: ComponentType, entrypoint: )) + (handler.charAt(0).toUpperCase() + handler.slice(1)); return name.charAt(0).toUpperCase() + name.slice(1) - + type === ComponentType.CUSTOM_RESOURCE_PROVIDER ? 'Provider' : 'Function'; + + (type === ComponentType.CUSTOM_RESOURCE_PROVIDER ? 'Provider' : type); } export function toLambdaRuntime(runtime: Runtime) { From cf435a0f4a6c9d43f522ddaf30a446eded8f05c6 Mon Sep 17 00:00:00 2001 From: Francis Date: Thu, 14 Dec 2023 14:47:21 -0800 Subject: [PATCH 66/73] updated generated component names Signed-off-by: Francis --- .../@aws-cdk/custom-resource-handlers/scripts/generate.ts | 2 +- .../aws-certificatemanager/lib/dns-validated-certificate.ts | 4 ++-- packages/aws-cdk-lib/aws-dynamodb/lib/replica-provider.ts | 6 +++--- .../aws-cdk-lib/aws-eks/lib/cluster-resource-provider.ts | 6 +++--- packages/aws-cdk-lib/aws-eks/lib/kubectl-provider.ts | 4 ++-- packages/aws-cdk-lib/aws-events-targets/lib/aws-api.ts | 4 ++-- .../aws-cdk-lib/aws-s3-deployment/lib/bucket-deployment.ts | 4 ++-- packages/aws-cdk-lib/aws-ses/lib/receipt-rule.ts | 4 ++-- .../lib/emrcontainers/start-job-run.ts | 4 ++-- .../aws-stepfunctions-tasks/lib/evaluate-expression.ts | 4 ++-- .../lib/aws-custom-resource/aws-custom-resource.ts | 4 ++-- .../pipelines/lib/private/application-security-check.ts | 4 ++-- .../aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh | 2 +- 13 files changed, 26 insertions(+), 26 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts index 965b9a9d1d7d7..c0fd68c140a6e 100644 --- a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts +++ b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts @@ -13,7 +13,7 @@ async function main() { const module = new HandlerFrameworkModule(fqn); for (const component of components) { const outfile = calculateOutfile(component.sourceCode); - if (component.minifyAndBundle) { + if (component.minifyAndBundle ?? true) { await minifyAndBundle(component.sourceCode, outfile); } else { fs.mkdirSync(path.dirname(outfile), { recursive: true }); diff --git a/packages/aws-cdk-lib/aws-certificatemanager/lib/dns-validated-certificate.ts b/packages/aws-cdk-lib/aws-certificatemanager/lib/dns-validated-certificate.ts index 0ea76195a261d..b3167087ae9d6 100644 --- a/packages/aws-cdk-lib/aws-certificatemanager/lib/dns-validated-certificate.ts +++ b/packages/aws-cdk-lib/aws-certificatemanager/lib/dns-validated-certificate.ts @@ -5,7 +5,7 @@ import * as iam from '../../aws-iam'; import * as route53 from '../../aws-route53'; import * as cdk from '../../core'; import { Token } from '../../core'; -import { CertificateRequestCertificateRequestProvider } from '../../custom-resource-handlers/dist/aws-certificatemanager/certificate-request-provider.generated'; +import { CertificateRequestCertificateRequestFunction } from '../../custom-resource-handlers/dist/aws-certificatemanager/certificate-request-provider.generated'; /** * Properties to create a DNS validated certificate managed by AWS Certificate Manager @@ -105,7 +105,7 @@ export class DnsValidatedCertificate extends CertificateBase implements ICertifi certificateTransparencyLoggingPreference = props.transparencyLoggingEnabled ? 'ENABLED' : 'DISABLED'; } - const requestorFunction = new CertificateRequestCertificateRequestProvider(this, 'CertificateRequestorFunction', { + const requestorFunction = new CertificateRequestCertificateRequestFunction(this, 'CertificateRequestorFunction', { timeout: cdk.Duration.minutes(15), role: props.customResourceRole, }); diff --git a/packages/aws-cdk-lib/aws-dynamodb/lib/replica-provider.ts b/packages/aws-cdk-lib/aws-dynamodb/lib/replica-provider.ts index 023a14145666d..7a266668e0d7c 100644 --- a/packages/aws-cdk-lib/aws-dynamodb/lib/replica-provider.ts +++ b/packages/aws-cdk-lib/aws-dynamodb/lib/replica-provider.ts @@ -2,7 +2,7 @@ import { Construct } from 'constructs'; import * as iam from '../../aws-iam'; import * as lambda from '../../aws-lambda'; import { Aws, Duration, NestedStack, Stack } from '../../core'; -import { ReplicaOnEventProvider, ReplicaIsCompleteProvider } from '../../custom-resource-handlers/dist/aws-dynamodb/replica-provider.generated'; +import { ReplicaOnEventFunction, ReplicaIsCompleteFunction } from '../../custom-resource-handlers/dist/aws-dynamodb/replica-provider.generated'; import * as cr from '../../custom-resources'; /** @@ -56,12 +56,12 @@ export class ReplicaProvider extends NestedStack { super(scope, id); // Issues UpdateTable API calls - this.onEventHandler = new ReplicaOnEventProvider(this, 'OnEventHandler', { + this.onEventHandler = new ReplicaOnEventFunction(this, 'OnEventHandler', { timeout: Duration.minutes(5), }); // Checks if table is back to `ACTIVE` state - this.isCompleteHandler = new ReplicaIsCompleteProvider(this, 'IsCompleteHandler', { + this.isCompleteHandler = new ReplicaIsCompleteFunction(this, 'IsCompleteHandler', { timeout: Duration.seconds(30), }); diff --git a/packages/aws-cdk-lib/aws-eks/lib/cluster-resource-provider.ts b/packages/aws-cdk-lib/aws-eks/lib/cluster-resource-provider.ts index 2d2ac695f1ad4..1732dba7aca77 100644 --- a/packages/aws-cdk-lib/aws-eks/lib/cluster-resource-provider.ts +++ b/packages/aws-cdk-lib/aws-eks/lib/cluster-resource-provider.ts @@ -2,7 +2,7 @@ import { Construct } from 'constructs'; import * as ec2 from '../../aws-ec2'; import * as lambda from '../../aws-lambda'; import { Duration, NestedStack, Stack } from '../../core'; -import { ClusterResourceOnEventProvider, ClusterResourceIsCompleteProvider } from '../../custom-resource-handlers/dist/aws-eks/cluster-resource-provider.generated'; +import { ClusterResourceOnEventFunction, ClusterResourceIsCompleteFunction } from '../../custom-resource-handlers/dist/aws-eks/cluster-resource-provider.generated'; import * as cr from '../../custom-resources'; import { NodeProxyAgentLayer } from '../../lambda-layer-node-proxy-agent'; @@ -64,7 +64,7 @@ export class ClusterResourceProvider extends NestedStack { // The NPM dependency proxy-agent is required in order to support proxy routing with the AWS JS SDK. const nodeProxyAgentLayer = new NodeProxyAgentLayer(this, 'NodeProxyAgentLayer'); - const onEvent = new ClusterResourceOnEventProvider(this, 'OnEventHandler', { + const onEvent = new ClusterResourceOnEventFunction(this, 'OnEventHandler', { description: 'onEvent handler for EKS cluster resource provider', environment: { AWS_STS_REGIONAL_ENDPOINTS: 'regional', @@ -78,7 +78,7 @@ export class ClusterResourceProvider extends NestedStack { layers: props.onEventLayer ? [props.onEventLayer] : [nodeProxyAgentLayer], }); - const isComplete = new ClusterResourceIsCompleteProvider(this, 'IsCompleteHandler', { + const isComplete = new ClusterResourceIsCompleteFunction(this, 'IsCompleteHandler', { description: 'isComplete handler for EKS cluster resource provider', environment: { AWS_STS_REGIONAL_ENDPOINTS: 'regional', diff --git a/packages/aws-cdk-lib/aws-eks/lib/kubectl-provider.ts b/packages/aws-cdk-lib/aws-eks/lib/kubectl-provider.ts index 50b414b43a983..ceab1298df378 100644 --- a/packages/aws-cdk-lib/aws-eks/lib/kubectl-provider.ts +++ b/packages/aws-cdk-lib/aws-eks/lib/kubectl-provider.ts @@ -2,7 +2,7 @@ import { Construct, IConstruct } from 'constructs'; import { ICluster, Cluster } from './cluster'; import * as iam from '../../aws-iam'; import { Duration, Stack, NestedStack, Names, CfnCondition, Fn, Aws } from '../../core'; -import { KubectlProvider as _KubectlProvider } from '../../custom-resource-handlers/dist/aws-eks/kubectl-provider.generated'; +import { KubectlFunction } from '../../custom-resource-handlers/dist/aws-eks/kubectl-provider.generated'; import * as cr from '../../custom-resources'; import { AwsCliLayer } from '../../lambda-layer-awscli'; import { KubectlLayer } from '../../lambda-layer-kubectl'; @@ -132,7 +132,7 @@ export class KubectlProvider extends NestedStack implements IKubectlProvider { const memorySize = cluster.kubectlMemory ? cluster.kubectlMemory.toMebibytes() : 1024; - const handler = new _KubectlProvider(this, 'Handler', { + const handler = new KubectlFunction(this, 'Handler', { timeout: Duration.minutes(15), description: 'onEvent handler for EKS kubectl resource provider', memorySize, diff --git a/packages/aws-cdk-lib/aws-events-targets/lib/aws-api.ts b/packages/aws-cdk-lib/aws-events-targets/lib/aws-api.ts index f4eadb3365d17..1b5beebd9a64e 100644 --- a/packages/aws-cdk-lib/aws-events-targets/lib/aws-api.ts +++ b/packages/aws-cdk-lib/aws-events-targets/lib/aws-api.ts @@ -4,7 +4,7 @@ import * as events from '../../aws-events'; import * as iam from '../../aws-iam'; import * as lambda from '../../aws-lambda'; import { Annotations, Duration } from '../../core'; -import { AwsApiProvider } from '../../custom-resource-handlers/dist/aws-events-targets/aws-api-provider.generated'; +import { AwsApiSingletonFunction } from '../../custom-resource-handlers/dist/aws-events-targets/aws-api-provider.generated'; /** * AWS SDK service metadata. @@ -81,7 +81,7 @@ export class AwsApi implements events.IRuleTarget { * result from an EventBridge event. */ public bind(rule: events.IRule, id?: string): events.RuleTargetConfig { - const handler = new AwsApiProvider(rule as events.Rule, `${rule.node.id}${id}Handler`, { + const handler = new AwsApiSingletonFunction(rule as events.Rule, `${rule.node.id}${id}Handler`, { timeout: Duration.seconds(60), memorySize: 256, uuid: 'b4cf1abd-4e4f-4bc6-9944-1af7ccd9ec37', diff --git a/packages/aws-cdk-lib/aws-s3-deployment/lib/bucket-deployment.ts b/packages/aws-cdk-lib/aws-s3-deployment/lib/bucket-deployment.ts index 86a5005b69900..f0f7148f8801d 100644 --- a/packages/aws-cdk-lib/aws-s3-deployment/lib/bucket-deployment.ts +++ b/packages/aws-cdk-lib/aws-s3-deployment/lib/bucket-deployment.ts @@ -11,7 +11,7 @@ import * as lambda from '../../aws-lambda'; import * as logs from '../../aws-logs'; import * as s3 from '../../aws-s3'; import * as cdk from '../../core'; -import { BucketDeploymentProvider } from '../../custom-resource-handlers/dist/aws-s3-deployment/bucket-deployment-provider.generated'; +import { BucketDeploymentSingletonFunction } from '../../custom-resource-handlers/dist/aws-s3-deployment/bucket-deployment-provider.generated'; import { AwsCliLayer } from '../../lambda-layer-awscli'; // tag key has a limit of 128 characters @@ -316,7 +316,7 @@ export class BucketDeployment extends Construct { } const mountPath = `/mnt${accessPointPath}`; - const handler = new BucketDeploymentProvider(this, 'CustomResourceHandler', { + const handler = new BucketDeploymentSingletonFunction(this, 'CustomResourceHandler', { uuid: this.renderSingletonUuid(props.memoryLimit, props.ephemeralStorageSize, props.vpc), layers: [new AwsCliLayer(this, 'AwsCliLayer')], environment: { diff --git a/packages/aws-cdk-lib/aws-ses/lib/receipt-rule.ts b/packages/aws-cdk-lib/aws-ses/lib/receipt-rule.ts index b15cd902efc96..28854d2e7e655 100644 --- a/packages/aws-cdk-lib/aws-ses/lib/receipt-rule.ts +++ b/packages/aws-cdk-lib/aws-ses/lib/receipt-rule.ts @@ -4,7 +4,7 @@ import { IReceiptRuleSet } from './receipt-rule-set'; import { CfnReceiptRule } from './ses.generated'; import * as iam from '../../aws-iam'; import { Aws, IResource, Lazy, Resource } from '../../core'; -import { DropSpamProvider } from '../../custom-resource-handlers/dist/aws-ses/drop-spam-provider.generated'; +import { DropSpamSingletonFunction } from '../../custom-resource-handlers/dist/aws-ses/drop-spam-provider.generated'; /** * A receipt rule. @@ -170,7 +170,7 @@ export class DropSpamReceiptRule extends Construct { constructor(scope: Construct, id: string, props: DropSpamReceiptRuleProps) { super(scope, id); - const fn = new DropSpamProvider(this, 'Function', { + const fn = new DropSpamSingletonFunction(this, 'Function', { uuid: '224e77f9-a32e-4b4d-ac32-983477abba16', }); diff --git a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/emrcontainers/start-job-run.ts b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/emrcontainers/start-job-run.ts index 9ef5938ad72b6..7ade86376a2a3 100644 --- a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/emrcontainers/start-job-run.ts +++ b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/emrcontainers/start-job-run.ts @@ -5,7 +5,7 @@ import * as s3 from '../../../aws-s3'; import * as sfn from '../../../aws-stepfunctions'; import { TaskInput } from '../../../aws-stepfunctions'; import * as cdk from '../../../core'; -import { RolePolicyProvider } from '../../../custom-resource-handlers/dist/aws-stepfunctions-tasks/role-policy-provider.generated'; +import { RolePolicySingletonFunction } from '../../../custom-resource-handlers/dist/aws-stepfunctions-tasks/role-policy-provider.generated'; import * as cr from '../../../custom-resources'; import * as awscli from '../../../lambda-layer-awscli'; import { integrationResourceArn, validatePatternSupported } from '../private/task-utils'; @@ -349,7 +349,7 @@ export class EmrContainersStartJobRun extends sfn.TaskStateBase implements iam.I * Commands available through CLI: https://awscli.amazonaws.com/v2/documentation/api/latest/reference/emr-containers/index.html */ const cliLayer = new awscli.AwsCliLayer(this, 'awsclilayer'); - const shellCliLambda = new RolePolicyProvider(this, 'Call Update-Role-Trust-Policy', { + const shellCliLambda = new RolePolicySingletonFunction(this, 'Call Update-Role-Trust-Policy', { uuid: '8693BB64-9689-44B6-9AAF-B0CC9EB8757C', timeout: cdk.Duration.seconds(30), memorySize: 256, diff --git a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/evaluate-expression.ts b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/evaluate-expression.ts index 1edf51f51117b..e6d742a2ed2a9 100644 --- a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/evaluate-expression.ts +++ b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/evaluate-expression.ts @@ -2,7 +2,7 @@ import { Construct } from 'constructs'; import * as iam from '../../aws-iam'; import * as lambda from '../../aws-lambda'; import * as sfn from '../../aws-stepfunctions'; -import { EvalNodejsProvider } from '../../custom-resource-handlers/dist/aws-stepfunctions-tasks/eval-nodejs-provider.generated'; +import { EvalNodejsSingletonFunction } from '../../custom-resource-handlers/dist/aws-stepfunctions-tasks/eval-nodejs-provider.generated'; /** * Properties for EvaluateExpression @@ -116,7 +116,7 @@ function createEvalFn(runtime: lambda.Runtime | undefined, scope: Construct) { throw new Error(`The runtime ${runtime?.name} is currently not supported.`); } - return new EvalNodejsProvider(scope, 'EvalFunction', { + return new EvalNodejsSingletonFunction(scope, 'EvalFunction', { uuid, lambdaPurpose, }); diff --git a/packages/aws-cdk-lib/custom-resources/lib/aws-custom-resource/aws-custom-resource.ts b/packages/aws-cdk-lib/custom-resources/lib/aws-custom-resource/aws-custom-resource.ts index 0744bf629a1ce..e9c0da466015e 100644 --- a/packages/aws-cdk-lib/custom-resources/lib/aws-custom-resource/aws-custom-resource.ts +++ b/packages/aws-cdk-lib/custom-resources/lib/aws-custom-resource/aws-custom-resource.ts @@ -4,7 +4,7 @@ import * as iam from '../../../aws-iam'; import * as logs from '../../../aws-logs'; import * as cdk from '../../../core'; import { Annotations } from '../../../core'; -import { AwsCustomResourceProvider } from '../../../custom-resource-handlers/dist/custom-resources/aws-custom-resource-provider.generated'; +import { AwsCustomResourceSingletonFunction } from '../../../custom-resource-handlers/dist/custom-resources/aws-custom-resource-provider.generated'; import * as cxapi from '../../../cx-api'; import { awsSdkToIamAction } from '../helpers-internal/sdk-info'; @@ -445,7 +445,7 @@ export class AwsCustomResource extends Construct implements iam.IGrantable { this.props = props; - const provider = new AwsCustomResourceProvider(this, 'Provider', { + const provider = new AwsCustomResourceSingletonFunction(this, 'Provider', { uuid: AwsCustomResource.PROVIDER_FUNCTION_UUID, lambdaPurpose: 'AWS', timeout: props.timeout || cdk.Duration.minutes(2), diff --git a/packages/aws-cdk-lib/pipelines/lib/private/application-security-check.ts b/packages/aws-cdk-lib/pipelines/lib/private/application-security-check.ts index fa38ed6fb768a..b625862d9eea2 100644 --- a/packages/aws-cdk-lib/pipelines/lib/private/application-security-check.ts +++ b/packages/aws-cdk-lib/pipelines/lib/private/application-security-check.ts @@ -5,7 +5,7 @@ import * as cp from '../../../aws-codepipeline'; import * as iam from '../../../aws-iam'; import * as lambda from '../../../aws-lambda'; import { Duration, Tags } from '../../../core'; -import { ApproveLambdaProvider } from '../../../custom-resource-handlers/dist/pipelines/approve-lambda.generated'; +import { ApproveLambdaFunction } from '../../../custom-resource-handlers/dist/pipelines/approve-lambda.generated'; /** * Properties for an ApplicationSecurityCheck @@ -57,7 +57,7 @@ export class ApplicationSecurityCheck extends Construct { includeResourceTypes: ['AWS::CodePipeline::Pipeline'], }); - this.preApproveLambda = new ApproveLambdaProvider(this, 'CDKPipelinesAutoApprove', { + this.preApproveLambda = new ApproveLambdaFunction(this, 'CDKPipelinesAutoApprove', { timeout: Duration.minutes(5), }); diff --git a/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh b/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh index 25513cee9a577..6b8a48f9fcab5 100755 --- a/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh +++ b/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh @@ -6,7 +6,7 @@ customresourcedir=$(node -p "path.dirname(require.resolve('@aws-cdk/custom-resou function airlift() { # core needs to be airlifted directly to core to prevent circular dependencies - if [[ ($1 = dist/core/* || $1 = dist/core) && $1 != dist/core/nodejs-entrypoint-handler ]]; + if [[ $1 != dist/core/nodejs-entrypoint-handler && ($1 = dist/core/* || $1 = dist/core) ]]; then mkdir -p $awscdklibdir/core/lib/$1 cp $customresourcedir/$2 $awscdklibdir/core/lib/$1 From 4d259413cc6a00c63ee8320f63f11f7d9b4e7fc9 Mon Sep 17 00:00:00 2001 From: Francis Date: Thu, 14 Dec 2023 15:01:38 -0800 Subject: [PATCH 67/73] fixed unit tests with new naming Signed-off-by: Francis --- .../lib/custom-resources-framework/classes.ts | 2 +- ...-provider-core.ts => custom-resource-provider-core.ts} | 0 ...m-resource-provider.ts => custom-resource-provider.ts} | 0 .../expected/{cdk-function.ts => function.ts} | 2 +- .../{cdk-singleton-function.ts => singleton-function.ts} | 6 +++--- .../test/custom-resources-framework/framework.test.ts | 8 ++++---- 6 files changed, 9 insertions(+), 9 deletions(-) rename packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/{cdk-custom-resource-provider-core.ts => custom-resource-provider-core.ts} (100%) rename packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/{cdk-custom-resource-provider.ts => custom-resource-provider.ts} (100%) rename packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/{cdk-function.ts => function.ts} (89%) rename packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/{cdk-singleton-function.ts => singleton-function.ts} (83%) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts index c3fec05370836..ed9e24231a699 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/classes.ts @@ -150,7 +150,7 @@ export abstract class HandlerFrameworkClass extends ClassType { }, }; const _interface = this.getOrCreateInterface(scope, { - name: 'CdkSingletonFunctionProps', + name: `${this.name}Props`, export: true, extends: [LAMBDA_MODULE.FunctionOptions], properties: [uuid, lambdaPurpose], diff --git a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-custom-resource-provider-core.ts b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/custom-resource-provider-core.ts similarity index 100% rename from packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-custom-resource-provider-core.ts rename to packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/custom-resource-provider-core.ts diff --git a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-custom-resource-provider.ts b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/custom-resource-provider.ts similarity index 100% rename from packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-custom-resource-provider.ts rename to packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/custom-resource-provider.ts diff --git a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-function.ts b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/function.ts similarity index 89% rename from packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-function.ts rename to packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/function.ts index 960b75644da3a..8907e5f2c0540 100644 --- a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-function.ts +++ b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/function.ts @@ -3,7 +3,7 @@ import * as path from "path"; import { Construct } from "constructs"; import * as lambda from "../../../aws-lambda"; -export class TestProvider extends lambda.Function { +export class TestFunction extends lambda.Function { public constructor(scope: Construct, id: string, props?: lambda.FunctionOptions) { super(scope, id, { ...props, diff --git a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-singleton-function.ts b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/singleton-function.ts similarity index 83% rename from packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-singleton-function.ts rename to packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/singleton-function.ts index 22d09de7b1219..d4683021074c5 100644 --- a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/cdk-singleton-function.ts +++ b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/singleton-function.ts @@ -3,7 +3,7 @@ import * as path from "path"; import { Construct } from "constructs"; import * as lambda from "../../../aws-lambda"; -export class TestProvider extends lambda.SingletonFunction { +export class TestSingletonFunction extends lambda.SingletonFunction { public constructor(scope: Construct, id: string, props: CdkSingletonFunctionProps) { super(scope, id, { ...props, @@ -15,9 +15,9 @@ export class TestProvider extends lambda.SingletonFunction { } /** - * Initialization properties for TestProvider + * Initialization properties for TestSingletonFunction */ -export interface CdkSingletonFunctionProps extends lambda.FunctionOptions { +export interface TestSingletonFunctionProps extends lambda.FunctionOptions { /** * A unique identifier to identify this Lambda. * diff --git a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/framework.test.ts b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/framework.test.ts index ab4ff4406231a..14e4ee2491f45 100644 --- a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/framework.test.ts +++ b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/framework.test.ts @@ -30,7 +30,7 @@ describe('framework', () => { // THEN const result = fs.readFileSync(path.resolve(tmpDir, 'result.ts'), 'utf-8'); - const expected = fs.readFileSync(path.resolve(__dirname, 'expected', 'cdk-function.ts'), 'utf-8'); + const expected = fs.readFileSync(path.resolve(__dirname, 'expected', 'function.ts'), 'utf-8'); expect(result).toContain(expected); }); @@ -49,7 +49,7 @@ describe('framework', () => { // THEN const result = fs.readFileSync(path.resolve(tmpDir, 'result.ts'), 'utf-8'); - const expected = fs.readFileSync(path.resolve(__dirname, 'expected', 'cdk-singleton-function.ts'), 'utf-8'); + const expected = fs.readFileSync(path.resolve(__dirname, 'expected', 'singleton-function.ts'), 'utf-8'); expect(result).toContain(expected); }); @@ -68,7 +68,7 @@ describe('framework', () => { // THEN const result = fs.readFileSync(path.resolve(tmpDir, 'result.ts'), 'utf-8'); - const expected = fs.readFileSync(path.resolve(__dirname, 'expected', 'cdk-custom-resource-provider.ts'), 'utf-8'); + const expected = fs.readFileSync(path.resolve(__dirname, 'expected', 'custom-resource-provider.ts'), 'utf-8'); expect(result).toContain(expected); }); @@ -87,7 +87,7 @@ describe('framework', () => { // THEN const result = fs.readFileSync(path.resolve(tmpDir, 'result.ts'), 'utf-8'); - const expected = fs.readFileSync(path.resolve(__dirname, 'expected', 'cdk-custom-resource-provider-core.ts'), 'utf-8'); + const expected = fs.readFileSync(path.resolve(__dirname, 'expected', 'custom-resource-provider-core.ts'), 'utf-8'); expect(result).toContain(expected); }); }); From e8f5a3ae1cef41b78dcdd81d2783b4dd6c8d0d83 Mon Sep 17 00:00:00 2001 From: Francis Date: Thu, 14 Dec 2023 15:16:29 -0800 Subject: [PATCH 68/73] reverted lambda runtime deprecated flag Signed-off-by: Francis --- .../aws-cdk-lib/aws-lambda/lib/runtime.ts | 79 ++++--------------- .../aws-lambda/test/runtime.test.ts | 21 ----- 2 files changed, 15 insertions(+), 85 deletions(-) diff --git a/packages/aws-cdk-lib/aws-lambda/lib/runtime.ts b/packages/aws-cdk-lib/aws-lambda/lib/runtime.ts index f21fbd0b57083..0013d67ad2079 100644 --- a/packages/aws-cdk-lib/aws-lambda/lib/runtime.ts +++ b/packages/aws-cdk-lib/aws-lambda/lib/runtime.ts @@ -30,12 +30,6 @@ export interface LambdaRuntimeProps { * @default false */ readonly supportsSnapStart?: boolean; - - /** - * Whether this runtime is deprecated. - * @default false - */ - readonly isDeprecated?: boolean; } export enum RuntimeFamily { @@ -62,64 +56,43 @@ export class Runtime { * The NodeJS runtime (nodejs) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS runtime. */ - public static readonly NODEJS = new Runtime('nodejs', RuntimeFamily.NODEJS, { - supportsInlineCode: true, - isDeprecated: true, - }); + public static readonly NODEJS = new Runtime('nodejs', RuntimeFamily.NODEJS, { supportsInlineCode: true }); /** * The NodeJS 4.3 runtime (nodejs4.3) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS runtime. */ - public static readonly NODEJS_4_3 = new Runtime('nodejs4.3', RuntimeFamily.NODEJS, { - supportsInlineCode: true, - isDeprecated: true, - }); + public static readonly NODEJS_4_3 = new Runtime('nodejs4.3', RuntimeFamily.NODEJS, { supportsInlineCode: true }); /** * The NodeJS 6.10 runtime (nodejs6.10) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS runtime. */ - public static readonly NODEJS_6_10 = new Runtime('nodejs6.10', RuntimeFamily.NODEJS, { - supportsInlineCode: true, - isDeprecated: true, - }); + public static readonly NODEJS_6_10 = new Runtime('nodejs6.10', RuntimeFamily.NODEJS, { supportsInlineCode: true }); /** * The NodeJS 8.10 runtime (nodejs8.10) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS runtime. */ - public static readonly NODEJS_8_10 = new Runtime('nodejs8.10', RuntimeFamily.NODEJS, { - supportsInlineCode: true, - isDeprecated: true, - }); + public static readonly NODEJS_8_10 = new Runtime('nodejs8.10', RuntimeFamily.NODEJS, { supportsInlineCode: true }); /** * The NodeJS 10.x runtime (nodejs10.x) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS runtime. */ - public static readonly NODEJS_10_X = new Runtime('nodejs10.x', RuntimeFamily.NODEJS, { - supportsInlineCode: true, - isDeprecated: true, - }); + public static readonly NODEJS_10_X = new Runtime('nodejs10.x', RuntimeFamily.NODEJS, { supportsInlineCode: true }); /** * The NodeJS 12.x runtime (nodejs12.x) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS runtime. */ - public static readonly NODEJS_12_X = new Runtime('nodejs12.x', RuntimeFamily.NODEJS, { - supportsInlineCode: true, - isDeprecated: true, - }); + public static readonly NODEJS_12_X = new Runtime('nodejs12.x', RuntimeFamily.NODEJS, { supportsInlineCode: true }); /** * The NodeJS 14.x runtime (nodejs14.x) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS runtime. */ - public static readonly NODEJS_14_X = new Runtime('nodejs14.x', RuntimeFamily.NODEJS, { - supportsInlineCode: true, - isDeprecated: true, - }); + public static readonly NODEJS_14_X = new Runtime('nodejs14.x', RuntimeFamily.NODEJS, { supportsInlineCode: true }); /** * The NodeJS 16.x runtime (nodejs16.x) @@ -146,10 +119,7 @@ export class Runtime { * The Python 2.7 runtime (python2.7) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest Python runtime. */ - public static readonly PYTHON_2_7 = new Runtime('python2.7', RuntimeFamily.PYTHON, { - supportsInlineCode: true, - isDeprecated: true, - }); + public static readonly PYTHON_2_7 = new Runtime('python2.7', RuntimeFamily.PYTHON, { supportsInlineCode: true }); /** * The Python 3.6 runtime (python3.6) (not recommended) @@ -161,7 +131,6 @@ export class Runtime { public static readonly PYTHON_3_6 = new Runtime('python3.6', RuntimeFamily.PYTHON, { supportsInlineCode: true, supportsCodeGuruProfiling: true, - isDeprecated: true, }); /** @@ -259,49 +228,37 @@ export class Runtime { * The .NET Core 1.0 runtime (dotnetcore1.0) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest .NET Core runtime. */ - public static readonly DOTNET_CORE_1 = new Runtime('dotnetcore1.0', RuntimeFamily.DOTNET_CORE, { - isDeprecated: true, - }); + public static readonly DOTNET_CORE_1 = new Runtime('dotnetcore1.0', RuntimeFamily.DOTNET_CORE); /** * The .NET Core 2.0 runtime (dotnetcore2.0) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest .NET Core runtime. */ - public static readonly DOTNET_CORE_2 = new Runtime('dotnetcore2.0', RuntimeFamily.DOTNET_CORE, { - isDeprecated: true, - }); + public static readonly DOTNET_CORE_2 = new Runtime('dotnetcore2.0', RuntimeFamily.DOTNET_CORE); /** * The .NET Core 2.1 runtime (dotnetcore2.1) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest .NET Core runtime. */ - public static readonly DOTNET_CORE_2_1 = new Runtime('dotnetcore2.1', RuntimeFamily.DOTNET_CORE, { - isDeprecated: true, - }); + public static readonly DOTNET_CORE_2_1 = new Runtime('dotnetcore2.1', RuntimeFamily.DOTNET_CORE); /** * The .NET Core 3.1 runtime (dotnetcore3.1) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest .NET Core runtime. */ - public static readonly DOTNET_CORE_3_1 = new Runtime('dotnetcore3.1', RuntimeFamily.DOTNET_CORE, { - isDeprecated: true, - }); + public static readonly DOTNET_CORE_3_1 = new Runtime('dotnetcore3.1', RuntimeFamily.DOTNET_CORE); /** * The Go 1.x runtime (go1.x) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the PROVIDED_AL2023 runtime. */ - public static readonly GO_1_X = new Runtime('go1.x', RuntimeFamily.GO, { - isDeprecated: true, - }); + public static readonly GO_1_X = new Runtime('go1.x', RuntimeFamily.GO); /** * The Ruby 2.5 runtime (ruby2.5) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest Ruby runtime. */ - public static readonly RUBY_2_5 = new Runtime('ruby2.5', RuntimeFamily.RUBY, { - isDeprecated: true, - }); + public static readonly RUBY_2_5 = new Runtime('ruby2.5', RuntimeFamily.RUBY); /** * The Ruby 2.7 runtime (ruby2.7) @@ -317,7 +274,7 @@ export class Runtime { * The custom provided runtime (provided) * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest provided.al2023 runtime. */ - public static readonly PROVIDED = new Runtime('provided', RuntimeFamily.OTHER, { isDeprecated: true }); + public static readonly PROVIDED = new Runtime('provided', RuntimeFamily.OTHER); /** * The custom provided runtime with Amazon Linux 2 (provided.al2) @@ -376,17 +333,11 @@ export class Runtime { */ public readonly isVariable: boolean; - /** - * Whether or not this runtime is deprecated. - */ - public readonly isDeprecated: boolean; - constructor(name: string, family?: RuntimeFamily, props: LambdaRuntimeProps = {}) { this.name = name; this.supportsInlineCode = !!props.supportsInlineCode; this.family = family; this.isVariable = !!props.isVariable; - this.isDeprecated = props.isDeprecated ?? false; const imageName = props.bundlingDockerImage ?? `public.ecr.aws/sam/build-${name}`; this.bundlingDockerImage = DockerImage.fromRegistry(imageName); diff --git a/packages/aws-cdk-lib/aws-lambda/test/runtime.test.ts b/packages/aws-cdk-lib/aws-lambda/test/runtime.test.ts index d1d89cbbd5b6a..f3976e70c4327 100644 --- a/packages/aws-cdk-lib/aws-lambda/test/runtime.test.ts +++ b/packages/aws-cdk-lib/aws-lambda/test/runtime.test.ts @@ -54,25 +54,4 @@ describe('runtime', () => { // THEN expect(runtime.bundlingDockerImage.image).toEqual('my-docker-image'); }); - - test.each([ - [lambda.Runtime.PYTHON_2_7], - [lambda.Runtime.PYTHON_3_6], - [lambda.Runtime.NODEJS], - [lambda.Runtime.NODEJS_4_3], - [lambda.Runtime.NODEJS_6_10], - [lambda.Runtime.NODEJS_8_10], - [lambda.Runtime.NODEJS_10_X], - [lambda.Runtime.NODEJS_12_X], - [lambda.Runtime.NODEJS_14_X], - [lambda.Runtime.DOTNET_CORE_1], - [lambda.Runtime.DOTNET_CORE_2], - [lambda.Runtime.DOTNET_CORE_2_1], - [lambda.Runtime.DOTNET_CORE_3_1], - [lambda.Runtime.GO_1_X], - [lambda.Runtime.RUBY_2_5], - [lambda.Runtime.PROVIDED], - ])('%s is deprecated', (runtime) => { - expect(runtime.isDeprecated).toEqual(true); - }); }); From 46aa2214987961d27445e48200dd1a604bb2dc08 Mon Sep 17 00:00:00 2001 From: Francis Date: Thu, 14 Dec 2023 15:29:12 -0800 Subject: [PATCH 69/73] fix constructor props Signed-off-by: Francis --- .../custom-resources-framework/expected/singleton-function.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/singleton-function.ts b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/singleton-function.ts index d4683021074c5..7aa1d645e3f71 100644 --- a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/singleton-function.ts +++ b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/expected/singleton-function.ts @@ -4,7 +4,7 @@ import { Construct } from "constructs"; import * as lambda from "../../../aws-lambda"; export class TestSingletonFunction extends lambda.SingletonFunction { - public constructor(scope: Construct, id: string, props: CdkSingletonFunctionProps) { + public constructor(scope: Construct, id: string, props: TestSingletonFunctionProps) { super(scope, id, { ...props, "code": lambda.Code.fromAsset(path.join(__dirname, 'my-handler')), From ab259ed29e9b927fbed065fd5006f2443ecc5467 Mon Sep 17 00:00:00 2001 From: Francis Date: Thu, 14 Dec 2023 15:58:46 -0800 Subject: [PATCH 70/73] node18 as esbuild target Signed-off-by: Francis --- packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts index c0fd68c140a6e..79018a2fea504 100644 --- a/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts +++ b/packages/@aws-cdk/custom-resource-handlers/scripts/generate.ts @@ -54,6 +54,7 @@ async function minifyAndBundle(infile: string, outfile: string) { external: ['@aws-sdk/*', 'aws-sdk'], format: 'cjs', platform: 'node', + target: 'node18', bundle: true, minify: true, minifyWhitespace: true, From 11f94765c1e0043633b058740f6b531b255f3f40 Mon Sep 17 00:00:00 2001 From: Francis Date: Thu, 14 Dec 2023 20:51:01 -0800 Subject: [PATCH 71/73] readme Signed-off-by: Francis --- .../lib/custom-resources-framework/README.md | 72 +++++++++---------- .../airlift-custom-resource-handlers.sh | 2 +- 2 files changed, 35 insertions(+), 39 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/README.md b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/README.md index 8141a703d43a1..af7319bad89cd 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/README.md +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/README.md @@ -1,66 +1,62 @@ -# AWS CDK Custom Resource Framework +# CDK Handler Framework -The CDK custom resource framework is an internal framework developed to establish best practices, runtime enforcement, and consistency for custom resource providers and their associated source code. +The CDK handler framework is an internal framework used to code generate constructs that extend a lambda `Function`, lambda `SingletonFunction`, or core `CustomResourceProvider` construct and prohibit the user from directly configuring the `handler`, `runtime`, `code`, and `codeDirectory` properties. In doing this, we are able to establish best practices, runtime enforcement, and consistency across all handlers we build and vend within the aws-cdk. -## Custom Resource Framework Concepts +## CDK Handler Framework Concepts -Custom resource providers can be created in one of three forms - `CdkFunction`, `CdkSingletonFunction`, or `CdkCustomResourceProvider`. These three custom resource provider formats are a code generated wrapper around the lambda `Function`, lambda `SingletonFunction`, and `CustomResourceProvider` constructs, respectively. These new CDK prefixed constructs will offer the same behavior and initialization options as their parent minus the ability to configure the following properties during construction: -- `handler` - the name of the method within your code that the provider calls to execute your function -- `code` - the source code of your provider -- `codeDirectory` - a local file system directory with the provider's code -- `runtime` - the runtime environment for the provider that you are uploading +This framework allows for the creation of three component types: +1. `ComponentType.FUNCTION` - This is a wrapper around the lambda `Function` construct. It offers the same behavior and performance as a lambda `Function`, but it restricts the consumer from configuring the `handler`, `runtime`, and `code` properties. +2. `ComponentType.SINGLETON_FUNCTION` - This is a wrapper around the lambda `SingletonFunction` construct. It offers the same behavior and performance as a lambda `SingletonFunction`, but it restricts the consumer from configuring the `handler`, `runtime`, and `code` properties. +3. `ComponentType.CUSTOM_RESOURCE_PROVIDER` - This is a wrapper around the core `CustomResourceProvider` construct. It offers the same behavior and performance as a `CustomResourceProvider` and can be instantiated via the `getOrCreate` or `getOrCreateProvider` methods. This component restricts the consumer from configuring the `runtime` and `codeDirectory` properties. -Instead, these properties will be automatically generated using the `ProviderProps` configured in the [config](./config.ts) file: -- `type`: the custom resource provider type to generate -- `sourceCode`: the source code that will be executed by the provider -- `compatibleRuntimes`: runtimes that are compatible with the provider's source code -- `handler`: the name of the method within your code that the provider calls to execute your function +Code generating one of these three component types requires adding the component properties to the [config](./config.ts) file by providing `ComponentProps`. The `ComponentProps` are responsible for code generating the specified `ComponentType` with the `handler`, `runtime`, `code`, and `codeDirectory` properties set internally. `ComponentProps` includes the following properties: +- `type` - the framework component type to generate. +- `sourceCode` - the source code that will be excuted by the framework component. +- `runtime` - the runtime that is compatible with the framework component's source code. This is an optional property with a default node runtime maintained by the framework. +- `handler` - the name of the method with the source code that the framework component will call. This is an optional property and the default is `index.handler`. +- `minifyAndBundle` - whether the source code should be minified and bundled. This an optional property and the default is `true`. This should only be set to `false` for python files or for typescript/javascript files with a require import. -The [config](./config.ts) file is structured with the top most level mapping to a `aws-cdk-lib` module, i.e., `aws-s3`, `aws-dynamodb`, etc. Each service can contain one or more provider modules. Provider modules are containers for custom resource providers and will be rendered as a code generated file. Each provider module can contain one or more `ProviderProps` objects. Each `ProviderProps` object contains all the necessary information required to generate a single custom resource provider. The following example shows a more structural breakdown of how the [config](./config.ts) file is configured: +The [config](./config.ts) file is structured with the top level mapping to an aws-cdk module, i.e., aws-s3, aws-dynamodb, etc. Each service can contain one or more component modules. Component modules are containers for handler framework components and will be rendered as a code generated file. Each component module can contain one or more `ComponentProps` objects. The following example shows a more structural breakdown of how the [config](./config.ts) file is configured: ```ts const config = { 'aws-s3': { // the aws-cdk-lib module - 'replica-provider': [ // the custom resource provider module - // custom resource provider defined as ProviderProps object + 'replica-provider': [ // the provider module + // handler framework component defined as a `ComponentProps` object { - // the custom resource provider type - `CdkFunction` - type: ComponentType.CDK_FUNCTION, - // the source code that the provider will execute + // the handler framework component type + type: ComponentType.FUNCTION, + // the source code that the component will use sourceCode: path.resolve(__dirname, '..', 'aws-dynamodb', 'replica-handler', 'index.ts'), - // runtimes that are compatible with the source code - compatibleRuntimes: [Runtime.NODEJS_18_X], - // the handler in the source code that the provider will execute + // the handler in the source code that the component will execute handler: 'index.onEventHandler', }, ], }, 'aws-stepfunctions-tasks': { - // contains multiple custom resource provider modules + // contains multiple provider modules 'eval-nodejs-provider': [ { - // the custom resource provider type - `CdkSingletonFunction` - type: ProviderType.CDK_SINGLETON_FUNCTION, + type: ComponentType.SINGLETON_FUNCTION, sourceCode: path.resolve(__dirname, '..', 'aws-stepfunctions-tasks', 'eval-nodejs-handler', 'index.ts'), - compatibleRuntimes: [Runtime.NODEJS_18_X], }, ], 'role-policy-provider': [ { - type: ProviderType.CDK_SINGLETON_FUNCTION, + type: ComponentType.SINGLETON_FUNCTION, sourceCode: path.resolve(__dirname, '..', 'aws-stepfunctions-tasks', 'role-policy-handler', 'index.py'), - compatibleRuntimes: [Runtime.PYTHON_3_9], - // prevent minify and bundle flag is set since the source code is a python file - preventMinifyAndBundle: true, + runtime: Runtime.PYTHON_3_9, + // prevent minify and bundle since the source code is a python file + minifyAndBundle: false, }, ], }, }; ``` -Code generation for the provider modules is triggered when this package - `@aws-cdk/custom-resource-handlers` - is built. Importantly, this framework is also responsible for minifying and bundling the custom resource providers' source code and dependencies. A flag named `preventMinifyAndBundle` can be configured as part of the `ProviderProps` to prevent minifying and bundling the source code for a specific provider. This flag is `false` by default and is only needed for Python files or for JavaScript/TypeScript files containing require imports. +Code generation for the component modules is triggered when this package - `@aws-cdk/custom-resource-handlers` - is built. Importantly, this framework is also responsible for minifying and bundling the custom resource providers' source code and dependencies. A flag named `minifyAndBundle` can be configured as part of the `ComponentProps` to prevent minifying and bundling the source code for a specific provider. This flag is only needed for python files or for typescript/javascript files containing require imports. -Once built, all generated code and bundled source code will be written to `@aws-cdk/custom-resource-handlers/dist`. The top level field in the [config](./config.ts) file defining individual `aws-cdk-lib` modules will be used to create specific directories within `@aws-cdk/custom-resource-handlers/dist` and each provider module will be a separate code generated file within these directories named as `.generated.ts`. As an example, the sample [config](./config.ts) file above would create the following file structure: +Once built, all generated code and bundled source code will be written to `@aws-cdk/custom-resource-handlers/dist`. The top level field in the [config](./config.ts) file defining individual aws-cdk modules will be used to create specific directories within `@aws-cdk/custom-resource-handlers/dist` and each component module will be a separate code generated file within these directories named `.generated.ts`. As an example, the sample [config](./config.ts) file above would create the following file structure: |--- @aws-cdk | |--- custom-resource-handlers @@ -77,11 +73,11 @@ Once built, all generated code and bundled source code will be written to `@aws- | | | | |--- eval-nodejs-provider.generated.ts | | | | |--- role-policy-provider.generated.ts -The code generated custom resource providers are usable in `aws-cdk-lib` once `aws-cdk-lib` is built. The custom resource providers will be consumable from `aws-cdk-lib/custom-resource-handlers/dist` and the file structure therein will match what was generated in `@aws-cdk/custom-resource-handlers/dist` except for providers defined in `core`. To prevent circular dependencies, all custom resource providers defined in `core`and any associated source code will be consumable from `aws-cdk-lib/core/dist/core`. +The code generated handler framework components are consumable from `aws-cdk-lib/custom-resource-handlers/dist` once `aws-cdk-lib` is built. The file structure of `aws-cdk-lib/custom-resource-handlers/dist` will have the same structure as `@aws-cdk/custom-resource-handlers/dist` with the exception of `core`. To prevent circular dependencies, all handler framework components defined in `core`and any associated source code will be consumable from `aws-cdk-lib/core/dist/core`. -## Creating a Custom Resource Provider +## Creating a Handler Framework Component -Creating a new custom resource provider involves three steps: -1. Add the custom resource provider's source code to `@aws-cdk/custom-resource-handlers/lib/` -2. Update the [config](./config.ts) file with the custom resource provider to generate by specifying all required `ProviderProps`. -3. At this point you can directly build `@aws-cdk/custom-resource-handlers` with `yarn build` to view the generated provider in `@aws-cdk/custom-resource-handlers/dist`. Alternatively, you can build `aws-cdk-lib` with `npx lerna run build --scope=aws-cdk-lib --skip-nx-cache` to make the generated provider available for use within `aws-cdk-lib` +Creating a new handler framework component involves three steps: +1. Add the source code to `@aws-cdk/custom-resource-handlers/lib/` +2. Update the [config](./config.ts) file by specifying all required `ComponentProps`. +3. At this point you can directly build `@aws-cdk/custom-resource-handlers` with `yarn build` to view the generated component in `@aws-cdk/custom-resource-handlers/dist`. Alternatively, you can build `aws-cdk-lib` with `npx lerna run build --scope=aws-cdk-lib --skip-nx-cache` to make the generated component available for use within `aws-cdk-lib` diff --git a/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh b/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh index 6b8a48f9fcab5..86ac92a219c64 100755 --- a/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh +++ b/packages/aws-cdk-lib/scripts/airlift-custom-resource-handlers.sh @@ -6,7 +6,7 @@ customresourcedir=$(node -p "path.dirname(require.resolve('@aws-cdk/custom-resou function airlift() { # core needs to be airlifted directly to core to prevent circular dependencies - if [[ $1 != dist/core/nodejs-entrypoint-handler && ($1 = dist/core/* || $1 = dist/core) ]]; + if [[ $1 != dist/core/nodejs-entrypoint-handler && ($1 = dist/core || $1 = dist/core/*) ]]; then mkdir -p $awscdklibdir/core/lib/$1 cp $customresourcedir/$2 $awscdklibdir/core/lib/$1 From f77cacff5e15bfe028dc02eca6eac77dc4f1f20d Mon Sep 17 00:00:00 2001 From: Francis Date: Thu, 14 Dec 2023 20:54:27 -0800 Subject: [PATCH 72/73] readme Signed-off-by: Francis --- .../lib/custom-resources-framework/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/README.md b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/README.md index af7319bad89cd..f18afce9a4fb0 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/README.md +++ b/packages/@aws-cdk/custom-resource-handlers/lib/custom-resources-framework/README.md @@ -21,7 +21,7 @@ The [config](./config.ts) file is structured with the top level mapping to an aw ```ts const config = { 'aws-s3': { // the aws-cdk-lib module - 'replica-provider': [ // the provider module + 'replica-provider': [ // the component module // handler framework component defined as a `ComponentProps` object { // the handler framework component type @@ -34,7 +34,7 @@ const config = { ], }, 'aws-stepfunctions-tasks': { - // contains multiple provider modules + // contains multiple component modules 'eval-nodejs-provider': [ { type: ComponentType.SINGLETON_FUNCTION, From dedc574e5768fcf7f89ddb818550036f9145a1ed Mon Sep 17 00:00:00 2001 From: Francis Date: Fri, 15 Dec 2023 08:38:20 -0800 Subject: [PATCH 73/73] utils tests Signed-off-by: Francis --- .../utils/framework-utils.test.ts | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/utils/framework-utils.test.ts diff --git a/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/utils/framework-utils.test.ts b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/utils/framework-utils.test.ts new file mode 100644 index 0000000000000..e8f804df3253a --- /dev/null +++ b/packages/@aws-cdk/custom-resource-handlers/test/custom-resources-framework/utils/framework-utils.test.ts @@ -0,0 +1,66 @@ +import { ComponentType, Runtime } from '../../../lib/custom-resources-framework/config'; +import { buildComponentName, toLambdaRuntime } from '../../../lib/custom-resources-framework/utils/framework-utils'; + +describe('to lambda runtime', () => { + test.each([ + [Runtime.NODEJS_18_X, 'lambda.Runtime.NODEJS_18_X'], + [Runtime.PYTHON_3_9, 'lambda.Runtime.PYTHON_3_9'], + [Runtime.PYTHON_3_10, 'lambda.Runtime.PYTHON_3_10'], + ])('to lambda %s runtime', (runtime, expectedRuntime) => { + expect(toLambdaRuntime(runtime)).toEqual(expectedRuntime); + }); +}); + +describe('build compoonent name', () => { + test('build function component name', () => { + // GIVEN + const fqn = 'test/aws-cdk-provider'; + const type = ComponentType.FUNCTION; + const entrypoint = 'index.handler'; + + // WHEN + const name = buildComponentName(fqn, type, entrypoint); + + // THEN + expect(name).toEqual('AwsCdkFunction'); + }); + + test('build singleton function component name', () => { + // GIVEN + const fqn = 'test/aws-cdk-provider'; + const type = ComponentType.SINGLETON_FUNCTION; + const entrypoint = 'index.handler'; + + // WHEN + const name = buildComponentName(fqn, type, entrypoint); + + // THEN + expect(name).toEqual('AwsCdkSingletonFunction'); + }); + + test('build custom resource provider component name', () => { + // GIVEN + const fqn = 'test/aws-cdk-provider'; + const type = ComponentType.CUSTOM_RESOURCE_PROVIDER; + const entrypoint = 'index.handler'; + + // WHEN + const name = buildComponentName(fqn, type, entrypoint); + + // THEN + expect(name).toEqual('AwsCdkProvider'); + }); + + test('with non-default handler', () => { + // GIVEN + const fqn = 'test/aws-cdk-provider'; + const type = ComponentType.FUNCTION; + const entrypoint = 'index.onEventHandler'; + + // WHEN + const name = buildComponentName(fqn, type, entrypoint); + + // THEN + expect(name).toEqual('AwsCdkOnEventFunction'); + }); +});