From 3ce40b4455215b066833fa0ebe0e0a99a2928573 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy <36202692+kaizen3031593@users.noreply.github.com> Date: Wed, 20 Apr 2022 16:08:14 -0400 Subject: [PATCH] feat(s3-deployment): ephemeral storage size property for bucket deployment (#19958) Closes #19947. Followed the same convention for `memoryLimit` property so that a new singleton lambda configuration will be created when bucket deployments are specified with different `ephemeralStorageSize` configurations. ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/master/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-lambda/lib/function.ts | 2 +- packages/@aws-cdk/aws-s3-deployment/README.md | 16 ++++--- .../lib/bucket-deployment.ts | 35 +++++++++++---- .../test/bucket-deployment.test.ts | 44 +++++++++++++++++-- 4 files changed, 79 insertions(+), 18 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/function.ts b/packages/@aws-cdk/aws-lambda/lib/function.ts index 904fe515e5a7c..608bbf4e7bc02 100644 --- a/packages/@aws-cdk/aws-lambda/lib/function.ts +++ b/packages/@aws-cdk/aws-lambda/lib/function.ts @@ -96,7 +96,7 @@ export interface FunctionOptions extends EventInvokeConfigOptions { readonly memorySize?: number; /** - * The size of the function’s /tmp directory in MB. + * The size of the function’s /tmp directory in MiB. * * @default 512 MiB */ diff --git a/packages/@aws-cdk/aws-s3-deployment/README.md b/packages/@aws-cdk/aws-s3-deployment/README.md index 84ccec9e23c55..8117553ccbb4f 100644 --- a/packages/@aws-cdk/aws-s3-deployment/README.md +++ b/packages/@aws-cdk/aws-s3-deployment/README.md @@ -139,7 +139,8 @@ new s3deploy.BucketDeployment(this, 'DeployMeWithoutDeletingFilesOnDestination', }); ``` -This option also enables you to specify multiple bucket deployments for the same destination bucket & prefix, +This option also enables you to +multiple bucket deployments for the same destination bucket & prefix, each with its own characteristics. For example, you can set different cache-control headers based on file extensions: @@ -259,14 +260,19 @@ new s3deploy.BucketDeployment(this, 'DeployWithInvalidation', { }); ``` -## Memory Limit +## Size Limits The default memory limit for the deployment resource is 128MiB. If you need to -copy larger files, you can use the `memoryLimit` configuration to specify the +copy larger files, you can use the `memoryLimit` configuration to increase the size of the AWS Lambda resource handler. -> NOTE: a new AWS Lambda handler will be created in your stack for each memory -> limit configuration. +The default ephemeral storage size for the deployment resource is 512MiB. If you +need to upload larger files, you may hit this limit. You can use the +`ephemeralStorageSize` configuration to increase the storage size of the AWS Lambda +resource handler. + +> NOTE: a new AWS Lambda handler will be created in your stack for each combination +> of memory and storage size. ## EFS Support diff --git a/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts b/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts index 542fdffb6b7b7..30d3aae5d6099 100644 --- a/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts +++ b/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts @@ -121,6 +121,13 @@ export interface BucketDeploymentProps { */ readonly memoryLimit?: number; + /** + * The size of the AWS Lambda function’s /tmp directory in MiB. + * + * @default 512 MiB + */ + readonly ephemeralStorageSize?: cdk.Size; + /** * Mount an EFS file system. Enable this if your assets are large and you encounter disk space errors. * Enabling this option will require a VPC to be specified. @@ -292,7 +299,7 @@ export class BucketDeployment extends CoreConstruct { const mountPath = `/mnt${accessPointPath}`; const handler = new lambda.SingletonFunction(this, 'CustomResourceHandler', { - uuid: this.renderSingletonUuid(props.memoryLimit, props.vpc), + uuid: this.renderSingletonUuid(props.memoryLimit, props.ephemeralStorageSize, props.vpc), code: lambda.Code.fromAsset(path.join(__dirname, 'lambda')), layers: [new AwsCliLayer(this, 'AwsCliLayer')], runtime: lambda.Runtime.PYTHON_3_7, @@ -304,6 +311,7 @@ export class BucketDeployment extends CoreConstruct { timeout: cdk.Duration.minutes(15), role: props.role, memorySize: props.memoryLimit, + ephemeralStorageSize: props.ephemeralStorageSize, vpc: props.vpc, vpcSubnets: props.vpcSubnets, filesystem: accessPoint ? lambda.FileSystem.fromEfsAccessPoint( @@ -331,7 +339,7 @@ export class BucketDeployment extends CoreConstruct { // the sources actually has markers. const hasMarkers = sources.some(source => source.markers); - const crUniqueId = `CustomResource${this.renderUniqueId(props.memoryLimit, props.vpc)}`; + const crUniqueId = `CustomResource${this.renderUniqueId(props.memoryLimit, props.ephemeralStorageSize, props.vpc)}`; this.cr = new cdk.CustomResource(this, crUniqueId, { serviceToken: handler.functionArn, resourceType: 'Custom::CDKBucketDeployment', @@ -426,21 +434,32 @@ export class BucketDeployment extends CoreConstruct { return this._deployedBucket; } - private renderUniqueId(memoryLimit?: number, vpc?: ec2.IVpc) { + private renderUniqueId(memoryLimit?: number, ephemeralStorageSize?: cdk.Size, vpc?: ec2.IVpc) { let uuid = ''; - // if user specify a custom memory limit, define another singleton handler + // if the user specifes a custom memory limit, we define another singleton handler // with this configuration. otherwise, it won't be possible to use multiple // configurations since we have a singleton. if (memoryLimit) { if (cdk.Token.isUnresolved(memoryLimit)) { - throw new Error('Can\'t use tokens when specifying "memoryLimit" since we use it to identify the singleton custom resource handler'); + throw new Error("Can't use tokens when specifying 'memoryLimit' since we use it to identify the singleton custom resource handler."); } uuid += `-${memoryLimit.toString()}MiB`; } - // if user specify to use VPC, define another singleton handler + // if the user specifies a custom ephemeral storage size, we define another singleton handler + // with this configuration. otherwise, it won't be possible to use multiple + // configurations since we have a singleton. + if (ephemeralStorageSize) { + if (ephemeralStorageSize.isUnresolved()) { + throw new Error("Can't use tokens when specifying 'ephemeralStorageSize' since we use it to identify the singleton custom resource handler."); + } + + uuid += `-${ephemeralStorageSize.toMebibytes().toString()}MiB`; + } + + // if the user specifies a VPC, we define another singleton handler // with this configuration. otherwise, it won't be possible to use multiple // configurations since we have a singleton. // A VPC is a must if EFS storage is used and that's why we are only using VPC in uuid. @@ -451,10 +470,10 @@ export class BucketDeployment extends CoreConstruct { return uuid; } - private renderSingletonUuid(memoryLimit?: number, vpc?: ec2.IVpc) { + private renderSingletonUuid(memoryLimit?: number, ephemeralStorageSize?: cdk.Size, vpc?: ec2.IVpc) { let uuid = '8693BB64-9689-44B6-9AAF-B0CC9EB8756C'; - uuid += this.renderUniqueId(memoryLimit, vpc); + uuid += this.renderUniqueId(memoryLimit, ephemeralStorageSize, vpc); return uuid; } diff --git a/packages/@aws-cdk/aws-s3-deployment/test/bucket-deployment.test.ts b/packages/@aws-cdk/aws-s3-deployment/test/bucket-deployment.test.ts index 9706f9d9d33f1..0aadf05e54f39 100644 --- a/packages/@aws-cdk/aws-s3-deployment/test/bucket-deployment.test.ts +++ b/packages/@aws-cdk/aws-s3-deployment/test/bucket-deployment.test.ts @@ -714,9 +714,7 @@ test('memoryLimit can be used to specify the memory limit for the deployment res const bucket = new s3.Bucket(stack, 'Dest'); // WHEN - // we define 3 deployments with 2 different memory configurations - new s3deploy.BucketDeployment(stack, 'Deploy256-1', { sources: [s3deploy.Source.asset(path.join(__dirname, 'my-website'))], destinationBucket: bucket, @@ -736,14 +734,52 @@ test('memoryLimit can be used to specify the memory limit for the deployment res }); // THEN - // we expect to find only two handlers, one for each configuration - Template.fromStack(stack).resourceCountIs('AWS::Lambda::Function', 2); Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { MemorySize: 256 }); Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { MemorySize: 1024 }); }); +test('ephemeralStorageSize can be used to specify the storage size for the deployment resource handler', () => { + // GIVEN + const stack = new cdk.Stack(); + const bucket = new s3.Bucket(stack, 'Dest'); + + // WHEN + // we define 3 deployments with 2 different memory configurations + new s3deploy.BucketDeployment(stack, 'Deploy256-1', { + sources: [s3deploy.Source.asset(path.join(__dirname, 'my-website'))], + destinationBucket: bucket, + ephemeralStorageSize: cdk.Size.mebibytes(512), + }); + + new s3deploy.BucketDeployment(stack, 'Deploy256-2', { + sources: [s3deploy.Source.asset(path.join(__dirname, 'my-website'))], + destinationBucket: bucket, + ephemeralStorageSize: cdk.Size.mebibytes(512), + }); + + new s3deploy.BucketDeployment(stack, 'Deploy1024', { + sources: [s3deploy.Source.asset(path.join(__dirname, 'my-website'))], + destinationBucket: bucket, + ephemeralStorageSize: cdk.Size.mebibytes(1024), + }); + + // THEN + // we expect to find only two handlers, one for each configuration + Template.fromStack(stack).resourceCountIs('AWS::Lambda::Function', 2); + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { + EphemeralStorage: { + Size: 512, + }, + }); + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { + EphemeralStorage: { + Size: 1024, + }, + }); +}); + test('deployment allows custom role to be supplied', () => { // GIVEN