From 8ac0e8719044e6f6563fdce6c3f86a5c214532cf Mon Sep 17 00:00:00 2001 From: MURAKAMI Masahiko Date: Fri, 29 Nov 2024 09:28:33 +0900 Subject: [PATCH 1/7] Add ephemeralStorageSize option to defineFunction --- .changeset/nasty-tables-heal.md | 5 ++ packages/backend-function/API.md | 1 + packages/backend-function/src/factory.test.ts | 64 +++++++++++++++++++ packages/backend-function/src/factory.ts | 32 +++++++++- packages/client-config/API.md | 2 +- packages/platform-core/API.md | 4 +- 6 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 .changeset/nasty-tables-heal.md diff --git a/.changeset/nasty-tables-heal.md b/.changeset/nasty-tables-heal.md new file mode 100644 index 0000000000..7e025c68ba --- /dev/null +++ b/.changeset/nasty-tables-heal.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-function': minor +--- + +Add ephemeralStorageSize option to defineFunction diff --git a/packages/backend-function/API.md b/packages/backend-function/API.md index 500972914d..f889e7f98f 100644 --- a/packages/backend-function/API.md +++ b/packages/backend-function/API.md @@ -52,6 +52,7 @@ export type FunctionProps = { entry?: string; timeoutSeconds?: number; memoryMB?: number; + ephemeralStorageSize?: number; environment?: Record; runtime?: NodeVersion; schedule?: FunctionSchedule | FunctionSchedule[]; diff --git a/packages/backend-function/src/factory.test.ts b/packages/backend-function/src/factory.test.ts index 8b964f336c..0e15c9cb84 100644 --- a/packages/backend-function/src/factory.test.ts +++ b/packages/backend-function/src/factory.test.ts @@ -579,4 +579,68 @@ void describe('AmplifyFunctionFactory', () => { 'function-Lambda' ); }); + + void describe('ephemeralStorageSize property', () => { + void it('sets valid ephemeralStorageSize', () => { + const lambda = defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + ephemeralStorageSize: 1024, + }).getInstance(getInstanceProps); + const template = Template.fromStack(lambda.stack); + + template.hasResourceProperties('AWS::Lambda::Function', { + EphemeralStorage: { Size: 1024 }, + }); + }); + + void it('sets default ephemeralStorageSize', () => { + const lambda = defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + }).getInstance(getInstanceProps); + const template = Template.fromStack(lambda.stack); + + template.hasResourceProperties('AWS::Lambda::Function', { + EphemeralStorage: { Size: 512 }, + }); + }); + + void it('throws on ephemeralStorageSize below 512 MB', () => { + assert.throws( + () => + defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + ephemeralStorageSize: 511, + }).getInstance(getInstanceProps), + new Error( + 'ephemeralStorageSize must be a whole number between 512 and 10240 inclusive' + ) + ); + }); + + void it('throws on ephemeralStorageSize above 10240 MB', () => { + assert.throws( + () => + defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + ephemeralStorageSize: 10241, + }).getInstance(getInstanceProps), + new Error( + 'ephemeralStorageSize must be a whole number between 128 and 10240 inclusive' + ) + ); + }); + + void it('throws on fractional ephemeralStorageSize', () => { + assert.throws( + () => + defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + memoryMB: 512.5, + }).getInstance(getInstanceProps), + new Error( + 'ephemeralStorageSize must be a whole number between 512 and 10240 inclusive' + ) + ); + }); + }); }); diff --git a/packages/backend-function/src/factory.ts b/packages/backend-function/src/factory.ts index e752b46d44..5dc6928875 100644 --- a/packages/backend-function/src/factory.ts +++ b/packages/backend-function/src/factory.ts @@ -26,7 +26,7 @@ import { SsmEnvironmentEntry, StackProvider, } from '@aws-amplify/plugin-types'; -import { Duration, Stack, Tags } from 'aws-cdk-lib'; +import { Duration, Size, Stack, Tags } from 'aws-cdk-lib'; import { Rule } from 'aws-cdk-lib/aws-events'; import * as targets from 'aws-cdk-lib/aws-events-targets'; import { Policy } from 'aws-cdk-lib/aws-iam'; @@ -115,6 +115,13 @@ export type FunctionProps = { */ memoryMB?: number; + /** + * The size of the function's /tmp directory in MiB. + * Must be a whole number. + * Default is 512MiB. + */ + ephemeralStorageSize?: number; + /** * Environment variables that will be available during function execution */ @@ -232,6 +239,7 @@ class FunctionFactory implements ConstructFactory { entry: this.resolveEntry(), timeoutSeconds: this.resolveTimeout(), memoryMB: this.resolveMemory(), + ephemeralStorageSize: this.resolveEphemeralStorageSize(), environment: this.props.environment ?? {}, runtime: this.resolveRuntime(), schedule: this.resolveSchedule(), @@ -318,6 +326,27 @@ class FunctionFactory implements ConstructFactory { return this.props.memoryMB; }; + private resolveEphemeralStorageSize = () => { + const ephemeralStorageSizeMin = 512; + const ephemeralStorageSizeMax = 10240; + const ephemeralStorageSizeDefault = 512; + if (this.props.ephemeralStorageSize === undefined) { + return ephemeralStorageSizeDefault; + } + if ( + !isWholeNumberBetweenInclusive( + this.props.ephemeralStorageSize, + ephemeralStorageSizeMin, + ephemeralStorageSizeMax + ) + ) { + throw new Error( + `ephemeralStorageSize must be a whole number between ${ephemeralStorageSizeMin} and ${ephemeralStorageSizeMax} inclusive` + ); + } + return this.props.ephemeralStorageSize; + }; + private resolveRuntime = () => { const runtimeDefault = 18; @@ -465,6 +494,7 @@ class AmplifyFunction entry: props.entry, timeout: Duration.seconds(props.timeoutSeconds), memorySize: props.memoryMB, + ephemeralStorageSize: Size.mebibytes(props.ephemeralStorageSize), runtime: nodeVersionMap[props.runtime], layers: props.resolvedLayers, bundling: { diff --git a/packages/client-config/API.md b/packages/client-config/API.md index cccafacd46..27cadd9351 100644 --- a/packages/client-config/API.md +++ b/packages/client-config/API.md @@ -627,7 +627,7 @@ export type CustomClientConfig = { export const DEFAULT_CLIENT_CONFIG_VERSION: ClientConfigVersion; // @public -export const generateClientConfig: (backendIdentifier: DeployedBackendIdentifier, version: T, awsClientProvider?: AWSClientProvider<{ +export const generateClientConfig: (backendIdentifier: DeployedBackendIdentifier, version: T, awsClientProvider?: AWSClientProvider<{ getS3Client: S3Client; getAmplifyClient: AmplifyClient; getCloudFormationClient: CloudFormationClient; diff --git a/packages/platform-core/API.md b/packages/platform-core/API.md index 55997c8747..3c1aa7a4b2 100644 --- a/packages/platform-core/API.md +++ b/packages/platform-core/API.md @@ -190,12 +190,12 @@ export const packageJsonSchema: z.ZodObject<{ type: z.ZodOptional, z.ZodLiteral<"commonjs">]>>; }, "strip", z.ZodTypeAny, { name?: string | undefined; - type?: "module" | "commonjs" | undefined; version?: string | undefined; + type?: "module" | "commonjs" | undefined; }, { name?: string | undefined; - type?: "module" | "commonjs" | undefined; version?: string | undefined; + type?: "module" | "commonjs" | undefined; }>; // @public From b30ad27dbd87a9dd28a337fbccf3e6febb321764 Mon Sep 17 00:00:00 2001 From: MURAKAMI Masahiko Date: Fri, 29 Nov 2024 14:49:27 +0900 Subject: [PATCH 2/7] fixup --- packages/backend-function/src/factory.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend-function/src/factory.test.ts b/packages/backend-function/src/factory.test.ts index 0e15c9cb84..26efd10adc 100644 --- a/packages/backend-function/src/factory.test.ts +++ b/packages/backend-function/src/factory.test.ts @@ -625,7 +625,7 @@ void describe('AmplifyFunctionFactory', () => { ephemeralStorageSize: 10241, }).getInstance(getInstanceProps), new Error( - 'ephemeralStorageSize must be a whole number between 128 and 10240 inclusive' + 'ephemeralStorageSize must be a whole number between 512 and 10240 inclusive' ) ); }); @@ -635,7 +635,7 @@ void describe('AmplifyFunctionFactory', () => { () => defineFunction({ entry: './test-assets/default-lambda/handler.ts', - memoryMB: 512.5, + ephemeralStorageSize: 512.5, }).getInstance(getInstanceProps), new Error( 'ephemeralStorageSize must be a whole number between 512 and 10240 inclusive' From 7ad9e3f723fbcc0dab33ac90e8c7c1167f7ca901 Mon Sep 17 00:00:00 2001 From: MURAKAMI Masahiko Date: Thu, 12 Dec 2024 14:08:25 +0900 Subject: [PATCH 3/7] fix: remove unit from default value --- packages/backend-function/src/factory.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend-function/src/factory.ts b/packages/backend-function/src/factory.ts index 0099a0dfc7..2305407c57 100644 --- a/packages/backend-function/src/factory.ts +++ b/packages/backend-function/src/factory.ts @@ -118,7 +118,7 @@ export type FunctionProps = { /** * The size of the function's /tmp directory in MiB. * Must be a whole number. - * Default is 512MiB. + * @default 512 */ ephemeralStorageSize?: number; From 6594212656ee3139bc9ef468eb2b14c34a44a65d Mon Sep 17 00:00:00 2001 From: MURAKAMI Masahiko Date: Fri, 13 Dec 2024 09:15:19 +0900 Subject: [PATCH 4/7] re-generate api reports --- packages/client-config/API.md | 2 +- packages/platform-core/API.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/client-config/API.md b/packages/client-config/API.md index 27cadd9351..cccafacd46 100644 --- a/packages/client-config/API.md +++ b/packages/client-config/API.md @@ -627,7 +627,7 @@ export type CustomClientConfig = { export const DEFAULT_CLIENT_CONFIG_VERSION: ClientConfigVersion; // @public -export const generateClientConfig: (backendIdentifier: DeployedBackendIdentifier, version: T, awsClientProvider?: AWSClientProvider<{ +export const generateClientConfig: (backendIdentifier: DeployedBackendIdentifier, version: T, awsClientProvider?: AWSClientProvider<{ getS3Client: S3Client; getAmplifyClient: AmplifyClient; getCloudFormationClient: CloudFormationClient; diff --git a/packages/platform-core/API.md b/packages/platform-core/API.md index 5aaf5d45ac..f40d1c5364 100644 --- a/packages/platform-core/API.md +++ b/packages/platform-core/API.md @@ -190,12 +190,12 @@ export const packageJsonSchema: z.ZodObject<{ type: z.ZodOptional, z.ZodLiteral<"commonjs">]>>; }, "strip", z.ZodTypeAny, { name?: string | undefined; - version?: string | undefined; type?: "module" | "commonjs" | undefined; + version?: string | undefined; }, { name?: string | undefined; - version?: string | undefined; type?: "module" | "commonjs" | undefined; + version?: string | undefined; }>; // @public From c802c7c9b4031942383df03570b79f6e93127c0d Mon Sep 17 00:00:00 2001 From: MURAKAMI Masahiko Date: Fri, 13 Dec 2024 09:34:42 +0900 Subject: [PATCH 5/7] rename ephemeralStorageSize to ephemeralStorageSizeMB --- .changeset/nasty-tables-heal.md | 2 +- packages/backend-function/API.md | 2 +- packages/backend-function/src/factory.test.ts | 24 +++++++++---------- packages/backend-function/src/factory.ts | 16 ++++++------- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/.changeset/nasty-tables-heal.md b/.changeset/nasty-tables-heal.md index 7e025c68ba..777a807a34 100644 --- a/.changeset/nasty-tables-heal.md +++ b/.changeset/nasty-tables-heal.md @@ -2,4 +2,4 @@ '@aws-amplify/backend-function': minor --- -Add ephemeralStorageSize option to defineFunction +Add ephemeralStorageSizeMB option to defineFunction diff --git a/packages/backend-function/API.md b/packages/backend-function/API.md index 0c47ab3372..4c44cd115d 100644 --- a/packages/backend-function/API.md +++ b/packages/backend-function/API.md @@ -93,7 +93,7 @@ export type FunctionProps = { entry?: string; timeoutSeconds?: number; memoryMB?: number; - ephemeralStorageSize?: number; + ephemeralStorageSizeMB?: number; environment?: Record; runtime?: NodeVersion; schedule?: FunctionSchedule | FunctionSchedule[]; diff --git a/packages/backend-function/src/factory.test.ts b/packages/backend-function/src/factory.test.ts index c089010503..5a5e79f2eb 100644 --- a/packages/backend-function/src/factory.test.ts +++ b/packages/backend-function/src/factory.test.ts @@ -683,11 +683,11 @@ void describe('AmplifyFunctionFactory', () => { ); }); - void describe('ephemeralStorageSize property', () => { + void describe('ephemeralStorageSizeMB property', () => { void it('sets valid ephemeralStorageSize', () => { const lambda = defineFunction({ entry: './test-assets/default-lambda/handler.ts', - ephemeralStorageSize: 1024, + ephemeralStorageSizeMB: 1024, }).getInstance(getInstanceProps); const template = Template.fromStack(lambda.stack); @@ -696,7 +696,7 @@ void describe('AmplifyFunctionFactory', () => { }); }); - void it('sets default ephemeralStorageSize', () => { + void it('sets default ephemeralStorageSizeMB', () => { const lambda = defineFunction({ entry: './test-assets/default-lambda/handler.ts', }).getInstance(getInstanceProps); @@ -707,41 +707,41 @@ void describe('AmplifyFunctionFactory', () => { }); }); - void it('throws on ephemeralStorageSize below 512 MB', () => { + void it('throws on ephemeralStorageSizeMB below 512 MB', () => { assert.throws( () => defineFunction({ entry: './test-assets/default-lambda/handler.ts', - ephemeralStorageSize: 511, + ephemeralStorageSizeMB: 511, }).getInstance(getInstanceProps), new Error( - 'ephemeralStorageSize must be a whole number between 512 and 10240 inclusive' + 'ephemeralStorageSizeMB must be a whole number between 512 and 10240 inclusive' ) ); }); - void it('throws on ephemeralStorageSize above 10240 MB', () => { + void it('throws on ephemeralStorageSizeMB above 10240 MB', () => { assert.throws( () => defineFunction({ entry: './test-assets/default-lambda/handler.ts', - ephemeralStorageSize: 10241, + ephemeralStorageSizeMB: 10241, }).getInstance(getInstanceProps), new Error( - 'ephemeralStorageSize must be a whole number between 512 and 10240 inclusive' + 'ephemeralStorageSizeMB must be a whole number between 512 and 10240 inclusive' ) ); }); - void it('throws on fractional ephemeralStorageSize', () => { + void it('throws on fractional ephemeralStorageSizeMB', () => { assert.throws( () => defineFunction({ entry: './test-assets/default-lambda/handler.ts', - ephemeralStorageSize: 512.5, + ephemeralStorageSizeMB: 512.5, }).getInstance(getInstanceProps), new Error( - 'ephemeralStorageSize must be a whole number between 512 and 10240 inclusive' + 'ephemeralStorageSizeMB must be a whole number between 512 and 10240 inclusive' ) ); }); diff --git a/packages/backend-function/src/factory.ts b/packages/backend-function/src/factory.ts index 2305407c57..91b7d52c3d 100644 --- a/packages/backend-function/src/factory.ts +++ b/packages/backend-function/src/factory.ts @@ -116,11 +116,11 @@ export type FunctionProps = { memoryMB?: number; /** - * The size of the function's /tmp directory in MiB. + * The size of the function's /tmp directory in MB. * Must be a whole number. * @default 512 */ - ephemeralStorageSize?: number; + ephemeralStorageSizeMB?: number; /** * Environment variables that will be available during function execution @@ -243,7 +243,7 @@ class FunctionFactory implements ConstructFactory { entry: this.resolveEntry(), timeoutSeconds: this.resolveTimeout(), memoryMB: this.resolveMemory(), - ephemeralStorageSize: this.resolveEphemeralStorageSize(), + ephemeralStorageSizeMB: this.resolveEphemeralStorageSize(), environment: this.resolveEnvironment(), runtime: this.resolveRuntime(), schedule: this.resolveSchedule(), @@ -336,21 +336,21 @@ class FunctionFactory implements ConstructFactory { const ephemeralStorageSizeMin = 512; const ephemeralStorageSizeMax = 10240; const ephemeralStorageSizeDefault = 512; - if (this.props.ephemeralStorageSize === undefined) { + if (this.props.ephemeralStorageSizeMB === undefined) { return ephemeralStorageSizeDefault; } if ( !isWholeNumberBetweenInclusive( - this.props.ephemeralStorageSize, + this.props.ephemeralStorageSizeMB, ephemeralStorageSizeMin, ephemeralStorageSizeMax ) ) { throw new Error( - `ephemeralStorageSize must be a whole number between ${ephemeralStorageSizeMin} and ${ephemeralStorageSizeMax} inclusive` + `ephemeralStorageSizeMB must be a whole number between ${ephemeralStorageSizeMin} and ${ephemeralStorageSizeMax} inclusive` ); } - return this.props.ephemeralStorageSize; + return this.props.ephemeralStorageSizeMB; }; private resolveEnvironment = () => { @@ -538,7 +538,7 @@ class AmplifyFunction entry: props.entry, timeout: Duration.seconds(props.timeoutSeconds), memorySize: props.memoryMB, - ephemeralStorageSize: Size.mebibytes(props.ephemeralStorageSize), + ephemeralStorageSize: Size.mebibytes(props.ephemeralStorageSizeMB), runtime: nodeVersionMap[props.runtime], layers: props.resolvedLayers, bundling: { From 6c8d04f9c2e05b2bb491f6db4327347c0cf1eddd Mon Sep 17 00:00:00 2001 From: MURAKAMI Masahiko Date: Fri, 13 Dec 2024 09:50:06 +0900 Subject: [PATCH 6/7] add `mebibytes` to eslint dictionary --- .eslint_dictionary.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.eslint_dictionary.json b/.eslint_dictionary.json index 71e74fbd53..708bec2391 100644 --- a/.eslint_dictionary.json +++ b/.eslint_dictionary.json @@ -100,6 +100,7 @@ "lstat", "macos", "matchers", + "mebibytes", "mfas", "minify", "mkdtemp", From 847124ad2c0ec871af1ac166832bf050be0d9442 Mon Sep 17 00:00:00 2001 From: MURAKAMI Masahiko Date: Sat, 14 Dec 2024 10:13:25 +0900 Subject: [PATCH 7/7] fix: throw AmplifyUserError instead of Error --- packages/backend-function/src/factory.test.ts | 21 +++++++++++-------- packages/backend-function/src/factory.ts | 7 ++++--- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/packages/backend-function/src/factory.test.ts b/packages/backend-function/src/factory.test.ts index 5a5e79f2eb..7f7abffd1d 100644 --- a/packages/backend-function/src/factory.test.ts +++ b/packages/backend-function/src/factory.test.ts @@ -714,9 +714,10 @@ void describe('AmplifyFunctionFactory', () => { entry: './test-assets/default-lambda/handler.ts', ephemeralStorageSizeMB: 511, }).getInstance(getInstanceProps), - new Error( - 'ephemeralStorageSizeMB must be a whole number between 512 and 10240 inclusive' - ) + new AmplifyUserError('InvalidEphemeralStorageSizeMBError', { + message: `Invalid function ephemeralStorageSizeMB of 511`, + resolution: `ephemeralStorageSizeMB must be a whole number between 512 and 10240 inclusive`, + }) ); }); @@ -727,9 +728,10 @@ void describe('AmplifyFunctionFactory', () => { entry: './test-assets/default-lambda/handler.ts', ephemeralStorageSizeMB: 10241, }).getInstance(getInstanceProps), - new Error( - 'ephemeralStorageSizeMB must be a whole number between 512 and 10240 inclusive' - ) + new AmplifyUserError('InvalidEphemeralStorageSizeMBError', { + message: `Invalid function ephemeralStorageSizeMB of 10241`, + resolution: `ephemeralStorageSizeMB must be a whole number between 512 and 10240 inclusive`, + }) ); }); @@ -740,9 +742,10 @@ void describe('AmplifyFunctionFactory', () => { entry: './test-assets/default-lambda/handler.ts', ephemeralStorageSizeMB: 512.5, }).getInstance(getInstanceProps), - new Error( - 'ephemeralStorageSizeMB must be a whole number between 512 and 10240 inclusive' - ) + new AmplifyUserError('InvalidEphemeralStorageSizeMBError', { + message: `Invalid function ephemeralStorageSizeMB of 512.5`, + resolution: `ephemeralStorageSizeMB must be a whole number between 512 and 10240 inclusive`, + }) ); }); }); diff --git a/packages/backend-function/src/factory.ts b/packages/backend-function/src/factory.ts index 91b7d52c3d..7f2f04b081 100644 --- a/packages/backend-function/src/factory.ts +++ b/packages/backend-function/src/factory.ts @@ -346,9 +346,10 @@ class FunctionFactory implements ConstructFactory { ephemeralStorageSizeMax ) ) { - throw new Error( - `ephemeralStorageSizeMB must be a whole number between ${ephemeralStorageSizeMin} and ${ephemeralStorageSizeMax} inclusive` - ); + throw new AmplifyUserError('InvalidEphemeralStorageSizeMBError', { + message: `Invalid function ephemeralStorageSizeMB of ${this.props.ephemeralStorageSizeMB}`, + resolution: `ephemeralStorageSizeMB must be a whole number between ${ephemeralStorageSizeMin} and ${ephemeralStorageSizeMax} inclusive`, + }); } return this.props.ephemeralStorageSizeMB; };