Skip to content

Commit

Permalink
feat: implement code generated handler framework (#28251)
Browse files Browse the repository at this point in the history
This PR introduces an internal handler 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.

As expected, no integ tests were changed as a result of this PR. To verify that the code generated custom resource providers are working correctly I force ran three integ tests all targeted at an individual custom resource provider:
1. integ.global.ts to test replica provider and the code generated construct extending `Function`
2. integ.bucket-auto-delete-objects.ts to test auto delete objects provider and the code generated construct extending `CustomResourceProvider`
3. integ.aws-api.ts to test aws api provider and the code generated construct `SingletonFunction`

All of these integ tests passed successfully.

Closes #27303

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
colifran committed Dec 16, 2023
1 parent 21a0fa6 commit 1a9c30e
Show file tree
Hide file tree
Showing 44 changed files with 1,855 additions and 619 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# CDK Handler Framework

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.

## CDK Handler Framework Concepts

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.

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 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 component module
// handler framework component defined as a `ComponentProps` object
{
// 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'),
// the handler in the source code that the component will execute
handler: 'index.onEventHandler',
},
],
},
'aws-stepfunctions-tasks': {
// contains multiple component modules
'eval-nodejs-provider': [
{
type: ComponentType.SINGLETON_FUNCTION,
sourceCode: path.resolve(__dirname, '..', 'aws-stepfunctions-tasks', 'eval-nodejs-handler', 'index.ts'),
},
],
'role-policy-provider': [
{
type: ComponentType.SINGLETON_FUNCTION,
sourceCode: path.resolve(__dirname, '..', 'aws-stepfunctions-tasks', 'role-policy-handler', 'index.py'),
runtime: Runtime.PYTHON_3_9,
// prevent minify and bundle since the source code is a python file
minifyAndBundle: false,
},
],
},
};
```

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 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 `<component-module>.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 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 Handler Framework Component

Creating a new handler framework component involves three steps:
1. Add the source code to `@aws-cdk/custom-resource-handlers/lib/<aws-cdk-lib-module>`
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`
Loading

0 comments on commit 1a9c30e

Please sign in to comment.