From 44d635581e25040653b19fee253d9c597e705b58 Mon Sep 17 00:00:00 2001 From: danielconde Date: Sat, 4 Jan 2020 15:43:04 +0000 Subject: [PATCH 1/4] feat: add custom inputs for lambdas timeout --- .../__tests__/custom-inputs.test.js | 77 ++++++++++++++++--- .../__tests__/deploy.test.js | 2 + .../serverless-nextjs-component/serverless.js | 23 ++++-- 3 files changed, 82 insertions(+), 20 deletions(-) diff --git a/packages/serverless-nextjs-component/__tests__/custom-inputs.test.js b/packages/serverless-nextjs-component/__tests__/custom-inputs.test.js index f2af991aa1..3e4ccd4757 100644 --- a/packages/serverless-nextjs-component/__tests__/custom-inputs.test.js +++ b/packages/serverless-nextjs-component/__tests__/custom-inputs.test.js @@ -93,13 +93,16 @@ describe("Custom inputs", () => { }); describe.each([ - [undefined, { defaultMem: 512, apiMem: 512 }], - [{}, { defaultMem: 512, apiMem: 512 }], - [1024, { defaultMem: 1024, apiMem: 1024 }], - [{ defaultLambda: 1024 }, { defaultMem: 1024, apiMem: 512 }], - [{ apiLambda: 2048 }, { defaultMem: 512, apiMem: 2048 }], - [{ defaultLambda: 128, apiLambda: 2048 }, { defaultMem: 128, apiMem: 2048 }] - ])("Custom memory", (memory, expectedMemory) => { + [undefined, { defaultMemory: 512, apiMemory: 512 }], + [{}, { defaultMemory: 512, apiMemory: 512 }], + [1024, { defaultMemory: 1024, apiMemory: 1024 }], + [{ defaultLambda: 1024 }, { defaultMemory: 1024, apiMemory: 512 }], + [{ apiLambda: 2048 }, { defaultMemory: 512, apiMemory: 2048 }], + [ + { defaultLambda: 128, apiLambda: 2048 }, + { defaultMemory: 128, apiMemory: 2048 } + ] + ])("Lambda memory input", (inputMemory, expectedMemory) => { let tmpCwd; const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); @@ -115,7 +118,57 @@ describe("Custom inputs", () => { const component = new NextjsComponent(); componentOutputs = await component.default({ - memory + memory: inputMemory + }); + }); + it(`sets default lambda memory to ${expectedMemory.defaultMemory} and api lambda memory to ${expectedMemory.apiMemory}`, () => { + const { defaultMemory, apiMemory } = expectedMemory; + + // Default Lambda + expect(mockLambda).toBeCalledWith( + expect.objectContaining({ + code: path.join(fixturePath, DEFAULT_LAMBDA_CODE_DIR), + memory: defaultMemory + }) + ); + + // Api Lambda + expect(mockLambda).toBeCalledWith( + expect.objectContaining({ + code: path.join(fixturePath, API_LAMBDA_CODE_DIR), + memory: apiMemory + }) + ); + }); + }); + + describe.each([ + [undefined, { defaultTimeout: 10, apiTimeout: 10 }], + [{}, { defaultTimeout: 10, apiTimeout: 10 }], + [40, { defaultTimeout: 40, apiTimeout: 40 }], + [{ defaultLambda: 20 }, { defaultTimeout: 20, apiTimeout: 10 }], + [{ apiLambda: 20 }, { defaultTimeout: 10, apiTimeout: 20 }], + [ + { defaultLambda: 15, apiLambda: 20 }, + { defaultTimeout: 15, apiTimeout: 20 } + ] + ])("Lambda timeout input", (inputTimeout, expectedTimeout) => { + let tmpCwd; + const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); + + beforeEach(async () => { + execa.mockResolvedValueOnce(); + + tmpCwd = process.cwd(); + process.chdir(fixturePath); + + mockCloudFront.mockResolvedValueOnce({ + url: "https://cloudfrontdistrib.amazonaws.com" + }); + + const component = new NextjsComponent(); + componentOutputs = await component.default({ + timeout: inputTimeout }); }); @@ -123,14 +176,14 @@ describe("Custom inputs", () => { process.chdir(tmpCwd); }); - it("uses custom memory", () => { - const { defaultMem, apiMem } = expectedMemory; + it(`sets default lambda timeout to ${expectedTimeout.defaultTimeout} and api lambda timeout to ${expectedTimeout.apiTimeout}`, () => { + const { defaultTimeout, apiTimeout } = expectedTimeout; // Default Lambda expect(mockLambda).toBeCalledWith( expect.objectContaining({ code: path.join(fixturePath, DEFAULT_LAMBDA_CODE_DIR), - memory: defaultMem + timeout: defaultTimeout }) ); @@ -138,7 +191,7 @@ describe("Custom inputs", () => { expect(mockLambda).toBeCalledWith( expect.objectContaining({ code: path.join(fixturePath, API_LAMBDA_CODE_DIR), - memory: apiMem + timeout: apiTimeout }) ); }); diff --git a/packages/serverless-nextjs-component/__tests__/deploy.test.js b/packages/serverless-nextjs-component/__tests__/deploy.test.js index 4cfae09db8..cadf01fe8c 100644 --- a/packages/serverless-nextjs-component/__tests__/deploy.test.js +++ b/packages/serverless-nextjs-component/__tests__/deploy.test.js @@ -81,6 +81,7 @@ describe("deploy tests", () => { handler: "index.handler", code: path.join(fixturePath, DEFAULT_LAMBDA_CODE_DIR), memory: 512, + timeout: 10, role: { service: ["lambda.amazonaws.com", "edgelambda.amazonaws.com"], policy: { @@ -97,6 +98,7 @@ describe("deploy tests", () => { handler: "index.handler", code: path.join(fixturePath, API_LAMBDA_CODE_DIR), memory: 512, + timeout: 10, role: { service: ["lambda.amazonaws.com", "edgelambda.amazonaws.com"], policy: { diff --git a/packages/serverless-nextjs-component/serverless.js b/packages/serverless-nextjs-component/serverless.js index 0f96a2db96..ed1b14cb22 100644 --- a/packages/serverless-nextjs-component/serverless.js +++ b/packages/serverless-nextjs-component/serverless.js @@ -385,6 +385,16 @@ class NextjsComponent extends Component { (Object.keys(apiBuildManifest.apis.nonDynamic).length > 0 || Object.keys(apiBuildManifest.apis.dynamic).length > 0); + const getLambdaMemory = lambdaType => + typeof inputs.memory === "number" + ? inputs.memory + : (inputs.memory && inputs.memory[lambdaType]) || 512; + + const getLambdaTimeout = lambdaType => + typeof inputs.timeout === "number" + ? inputs.timeout + : (inputs.timeout && inputs.timeout[lambdaType]) || 10; + if (hasAPIPages) { apiEdgeLambdaOutputs = await apiEdgeLambda({ description: "API Lambda@Edge for Next CloudFront distribution", @@ -398,10 +408,8 @@ class NextjsComponent extends Component { "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" } }, - memory: - typeof inputs.memory === "number" - ? inputs.memory - : (inputs.memory && inputs.memory.apiLambda) || 512 + memory: getLambdaMemory("apiLambda"), + timeout: getLambdaTimeout("apiLambda") }); apiEdgeLambdaPublishOutputs = await apiEdgeLambda.publishVersion(); @@ -435,10 +443,8 @@ class NextjsComponent extends Component { "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" } }, - memory: - typeof inputs.memory === "number" - ? inputs.memory - : (inputs.memory && inputs.memory.defaultLambda) || 512 + memory: getLambdaMemory("defaultLambda"), + timeout: getLambdaTimeout("defaultLambda") }); const defaultEdgeLambdaPublishOutputs = await defaultEdgeLambda.publishVersion(); @@ -492,3 +498,4 @@ class NextjsComponent extends Component { } module.exports = NextjsComponent; +module.exports = NextjsComponent; From 649400c3bf818b3bb99a818ca72cdb00ccf05d4f Mon Sep 17 00:00:00 2001 From: danielconde Date: Sat, 4 Jan 2020 16:11:10 +0000 Subject: [PATCH 2/4] chore: remove duplicate line --- packages/serverless-nextjs-component/serverless.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/serverless-nextjs-component/serverless.js b/packages/serverless-nextjs-component/serverless.js index ed1b14cb22..0e51c2755a 100644 --- a/packages/serverless-nextjs-component/serverless.js +++ b/packages/serverless-nextjs-component/serverless.js @@ -498,4 +498,3 @@ class NextjsComponent extends Component { } module.exports = NextjsComponent; -module.exports = NextjsComponent; From 71ea05abb7bd5573fd4cee66842fb935f2a3e2bf Mon Sep 17 00:00:00 2001 From: danielconde Date: Sat, 4 Jan 2020 17:34:44 +0000 Subject: [PATCH 3/4] docs: add timeout input --- README.md | 23 +++++++++++++++++-- .../serverless-nextjs-component/README.md | 22 ++++++++++++++++-- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b937695667..8a4a938f1c 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ A zero configuration Nextjs 9.0 [serverless component](https://github.com/server - [Design principles](#design-principles) - [Features](#features) - [Getting started](#getting-started) +- [Lambda@Edge configuration](#lambda-at-edge-configuration) - [Custom domain name](#custom-domain-name) - [AWS Permissions](#aws-permissions) - [Architecture](#architecture) @@ -133,8 +134,9 @@ myNextApplication: Make sure you add CloudWatch log permissions to your custom policy. -### Lambda memory -Both **default** and **api** lambdas will be assigned 512mb of memory by default. This value can be altered by assigning a number to the `memory` input +### Lambda At Edge Configuration + +Both **default** and **api** edge lambdas will be assigned 512mb of memory by default. This value can be altered by assigning a number to the `memory` input ```yml # serverless.yml @@ -158,6 +160,21 @@ myNextApplication: apiLambda: 2048 ``` +Similarly, the timeout by default is 10 seconds. To customise you can: + +```yml +# serverless.yml + +myNextApplication: + component: serverless-next.js + inputs: + timeout: + defaultLambda: 20 + apiLambda: 15 +``` + +Note the maximum timeout allowed for Lambda@Edge is 30 seconds. See https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-requirements-limits.html + ### Architecture ![architecture](./arch_no_grid.png) @@ -187,6 +204,8 @@ The fourth cache behaviour handles next API requests `api/*`. | nextConfigDir | `string` | `./` | Directory where your application `next.config.js` file is. This input is useful when the `serverless.yml` is not in the same directory as the next app.
**Note:** `nextConfigDir` should be set if `next.config.js` `distDir` is used. | | nextStaticDir | `string` | `./` | If your `static` or `public` directory is not a direct child of `nextConfigDir` this is needed. | | memory | `number` or `object` | `512` | When assigned a number, both the default and api lambdas will assigned memory of that value. When assigned to an object, values for the default and api lambdas can be separately defined | | +| timeout | `number` or `object` | `10` | When assigned a number, both the default and api lambdas will assigned a timeout of that value. When assigned to an object, values for the default and api lambdas can be separately defined | | + | build | `boolean` or `Object` | `true` | When true builds and deploys app, when false assume the app has been built and uses the `.next` `.serverless_nextjs` directories in `nextConfigDir` to deploy. If an object is passed `build` allows for overriding what script gets called and with what arguments. | | build.cmd | `string` | `node_modules/.bin/next` | Build command | | build.args | `Array|string` | `['build']` | Arguments to pass to the build | diff --git a/packages/serverless-nextjs-component/README.md b/packages/serverless-nextjs-component/README.md index b764bb96a1..489f71572a 100644 --- a/packages/serverless-nextjs-component/README.md +++ b/packages/serverless-nextjs-component/README.md @@ -16,6 +16,7 @@ A zero configuration Nextjs 9.0 [serverless component](https://github.com/server - [Design principles](#design-principles) - [Features](#features) - [Getting started](#getting-started) +- [Lambda@Edge configuration](#lambda-at-edge-configuration) - [Custom domain name](#custom-domain-name) - [AWS Permissions](#aws-permissions) - [Architecture](#architecture) @@ -133,9 +134,9 @@ myNextApplication: Make sure you add CloudWatch log permissions to your custom policy. -### Lambda memory +### Lambda At Edge Configuration -Both **default** and **api** lambdas will be assigned 512mb of memory by default. This value can be altered by assigning a number to the `memory` input +Both **default** and **api** edge lambdas will be assigned 512mb of memory by default. This value can be altered by assigning a number to the `memory` input ```yml # serverless.yml @@ -159,6 +160,21 @@ myNextApplication: apiLambda: 2048 ``` +Similarly, the timeout by default is 10 seconds. To customise you can: + +```yml +# serverless.yml + +myNextApplication: + component: serverless-next.js + inputs: + timeout: + defaultLambda: 20 + apiLambda: 15 +``` + +Note the maximum timeout allowed for Lambda@Edge is 30 seconds. See https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-requirements-limits.html + ### Architecture ![architecture](./arch_no_grid.png) @@ -188,6 +204,8 @@ The fourth cache behaviour handles next API requests `api/*`. | nextConfigDir | `string` | `./` | Directory where your application `next.config.js` file is. This input is useful when the `serverless.yml` is not in the same directory as the next app.
**Note:** `nextConfigDir` should be set if `next.config.js` `distDir` is used. | | nextStaticDir | `string` | `./` | If your `static` or `public` directory is not a direct child of `nextConfigDir` this is needed. | | memory | `number` or `object` | `512` | When assigned a number, both the default and api lambdas will assigned memory of that value. When assigned to an object, values for the default and api lambdas can be separately defined | | +| timeout | `number` or `object` | `10` | When assigned a number, both the default and api lambdas will assigned a timeout of that value. When assigned to an object, values for the default and api lambdas can be separately defined | | + | build | `boolean` or `Object` | `true` | When true builds and deploys app, when false assume the app has been built and uses the `.next` `.serverless_nextjs` directories in `nextConfigDir` to deploy. If an object is passed `build` allows for overriding what script gets called and with what arguments. | | build.cmd | `string` | `node_modules/.bin/next` | Build command | | build.args | `Array|string` | `['build']` | Arguments to pass to the build | From 0775e63d6b9ce61f9b718c330ca7245d574870d0 Mon Sep 17 00:00:00 2001 From: danielconde Date: Sat, 4 Jan 2020 17:48:52 +0000 Subject: [PATCH 4/4] docs: Few formatting fixes --- README.md | 30 +++++++++---------- .../serverless-nextjs-component/README.md | 29 +++++++++--------- 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 8a4a938f1c..6bd9d04f5f 100644 --- a/README.md +++ b/README.md @@ -197,21 +197,20 @@ The fourth cache behaviour handles next API requests `api/*`. ### Inputs -| Name | Type | Default Value | Description | -| ------------- | --------------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| domain | `Array` | `null` | For example `['admin', 'portal.com']`. | -| bucketName | `string` | `null` | Custom bucket name where static assets are stored. By default is autogenerated. | -| nextConfigDir | `string` | `./` | Directory where your application `next.config.js` file is. This input is useful when the `serverless.yml` is not in the same directory as the next app.
**Note:** `nextConfigDir` should be set if `next.config.js` `distDir` is used. | -| nextStaticDir | `string` | `./` | If your `static` or `public` directory is not a direct child of `nextConfigDir` this is needed. | -| memory | `number` or `object` | `512` | When assigned a number, both the default and api lambdas will assigned memory of that value. When assigned to an object, values for the default and api lambdas can be separately defined | | -| timeout | `number` or `object` | `10` | When assigned a number, both the default and api lambdas will assigned a timeout of that value. When assigned to an object, values for the default and api lambdas can be separately defined | | - -| build | `boolean` or `Object` | `true` | When true builds and deploys app, when false assume the app has been built and uses the `.next` `.serverless_nextjs` directories in `nextConfigDir` to deploy. If an object is passed `build` allows for overriding what script gets called and with what arguments. | -| build.cmd | `string` | `node_modules/.bin/next` | Build command | -| build.args | `Array|string` | `['build']` | Arguments to pass to the build | -| build.cwd | `string` | `./` | Override the current working directory | -| build.enabled | `boolean` | `true` | Same as passing `build:false` but from within the config. | -| build.env | `Object` | `{}` | Add additional environment variables to the script. | +| Name | Type | Default Value | Description | +| ------------- | ----------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| domain | `Array` | `null` | For example `['admin', 'portal.com']` | +| bucketName | `string` | `null` | Custom bucket name where static assets are stored. By default is autogenerated. | +| nextConfigDir | `string` | `./` | Directory where your application `next.config.js` file is. This input is useful when the `serverless.yml` is not in the same directory as the next app.
**Note:** `nextConfigDir` should be set if `next.config.js` `distDir` is used | +| nextStaticDir | `string` | `./` | If your `static` or `public` directory is not a direct child of `nextConfigDir` this is needed | +| memory | `number\|object` | `512` | When assigned a number, both the default and api lambdas will assigned memory of that value. When assigned to an object, values for the default and api lambdas can be separately defined | | +| timeout | `number\|object` | `10` | Same as above | +| build | `boolean\|object` | `true` | When true builds and deploys app, when false assume the app has been built and uses the `.next` `.serverless_nextjs` directories in `nextConfigDir` to deploy. If an object is passed `build` allows for overriding what script gets called and with what arguments. | +| build.cmd | `string` | `node_modules/.bin/next` | Build command | +| build.args | `Array\|string` | `['build']` | Arguments to pass to the build | +| build.cwd | `string` | `./` | Override the current working directory | +| build.enabled | `boolean` | `true` | Same as passing `build:false` but from within the config | +| build.env | `object` | `{}` | Add additional environment variables to the script | Custom inputs can be configured like this: @@ -329,4 +328,3 @@ Support this project with your organization. Your logo will show up here with a - diff --git a/packages/serverless-nextjs-component/README.md b/packages/serverless-nextjs-component/README.md index 489f71572a..6bd9d04f5f 100644 --- a/packages/serverless-nextjs-component/README.md +++ b/packages/serverless-nextjs-component/README.md @@ -197,21 +197,20 @@ The fourth cache behaviour handles next API requests `api/*`. ### Inputs -| Name | Type | Default Value | Description | -| ------------- | --------------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| domain | `Array` | `null` | For example `['admin', 'portal.com']`. | -| bucketName | `string` | `null` | Custom bucket name where static assets are stored. By default is autogenerated. | -| nextConfigDir | `string` | `./` | Directory where your application `next.config.js` file is. This input is useful when the `serverless.yml` is not in the same directory as the next app.
**Note:** `nextConfigDir` should be set if `next.config.js` `distDir` is used. | -| nextStaticDir | `string` | `./` | If your `static` or `public` directory is not a direct child of `nextConfigDir` this is needed. | -| memory | `number` or `object` | `512` | When assigned a number, both the default and api lambdas will assigned memory of that value. When assigned to an object, values for the default and api lambdas can be separately defined | | -| timeout | `number` or `object` | `10` | When assigned a number, both the default and api lambdas will assigned a timeout of that value. When assigned to an object, values for the default and api lambdas can be separately defined | | - -| build | `boolean` or `Object` | `true` | When true builds and deploys app, when false assume the app has been built and uses the `.next` `.serverless_nextjs` directories in `nextConfigDir` to deploy. If an object is passed `build` allows for overriding what script gets called and with what arguments. | -| build.cmd | `string` | `node_modules/.bin/next` | Build command | -| build.args | `Array|string` | `['build']` | Arguments to pass to the build | -| build.cwd | `string` | `./` | Override the current working directory | -| build.enabled | `boolean` | `true` | Same as passing `build:false` but from within the config. | -| build.env | `Object` | `{}` | Add additional environment variables to the script. | +| Name | Type | Default Value | Description | +| ------------- | ----------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| domain | `Array` | `null` | For example `['admin', 'portal.com']` | +| bucketName | `string` | `null` | Custom bucket name where static assets are stored. By default is autogenerated. | +| nextConfigDir | `string` | `./` | Directory where your application `next.config.js` file is. This input is useful when the `serverless.yml` is not in the same directory as the next app.
**Note:** `nextConfigDir` should be set if `next.config.js` `distDir` is used | +| nextStaticDir | `string` | `./` | If your `static` or `public` directory is not a direct child of `nextConfigDir` this is needed | +| memory | `number\|object` | `512` | When assigned a number, both the default and api lambdas will assigned memory of that value. When assigned to an object, values for the default and api lambdas can be separately defined | | +| timeout | `number\|object` | `10` | Same as above | +| build | `boolean\|object` | `true` | When true builds and deploys app, when false assume the app has been built and uses the `.next` `.serverless_nextjs` directories in `nextConfigDir` to deploy. If an object is passed `build` allows for overriding what script gets called and with what arguments. | +| build.cmd | `string` | `node_modules/.bin/next` | Build command | +| build.args | `Array\|string` | `['build']` | Arguments to pass to the build | +| build.cwd | `string` | `./` | Override the current working directory | +| build.enabled | `boolean` | `true` | Same as passing `build:false` but from within the config | +| build.env | `object` | `{}` | Add additional environment variables to the script | Custom inputs can be configured like this: