serverless-plugin-warmup 6.2.0 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,14 +2,15 @@
2
2
  [![Serverless][serverless-badge]](serverless-badge-url)
3
3
  [![npm version][npm-version-badge]][npm-version-badge-url]
4
4
  [![npm monthly downloads][npm-downloads-badge]][npm-version-badge-url]
5
- [![Build Status][travis-badge]][travis-badge-url]
5
+ [![Node.js CI](https://github.com/juanjoDiaz/serverless-plugin-warmup/actions/workflows/on-push.yaml/badge.svg)](https://github.com/juanjoDiaz/serverless-plugin-warmup/actions/workflows/on-push.yaml)
6
6
  [![Coverage Status][coveralls-badge]][coveralls-badge-url]
7
7
  [![license](https://img.shields.io/npm/l/serverless-plugin-warmup.svg)](https://raw.githubusercontent.com/juanjoDiaz/serverless-plugin-warmup/master/LICENSE)
8
8
 
9
9
  Keep your lambdas warm during winter.
10
10
 
11
11
  **Requirements:**
12
- * Serverless *v2.32.x* or higher
12
+ * Node *v14.x* or higher
13
+ * Serverless *v3.x* or higher
13
14
  * AWS provider
14
15
 
15
16
  ## How it works
@@ -43,6 +44,7 @@ custom:
43
44
  events:
44
45
  - schedule: cron(0/5 8-17 ? * MON-FRI *)
45
46
  concurrency: 10
47
+ verbose: true
46
48
  logRetentionInDays: 10
47
49
  outOfOfficeHoursWarmer:
48
50
  enabled: true
@@ -51,6 +53,7 @@ custom:
51
53
  - schedule: cron(0/5 18-23 ? * MON-FRI *)
52
54
  - schedule: cron(0/5 * ? * SAT-SUN *)
53
55
  concurrency: 1
56
+ verbose: false
54
57
  testWarmer:
55
58
  enabled: false
56
59
  ```
@@ -69,6 +72,7 @@ The options are the same for all the warmers:
69
72
  * **timeout** How many seconds until the warmer lambda times out. (defaults to `10`)
70
73
  * **environment** Can be used to set environment variables in the warmer lambda. You can also unset variables configured at the provider by setting them to undefined. However, you should almost never have to change the default. (defaults to unset all package level environment variables. )
71
74
  * **tracing** Specify whether to enable/disable tracing at the function level. When tracing is enabled, warmer functions will use NPM to install the X-Ray client and use it to trace requests (It takes any of the values supported by serverless as `boolean`, `Active`or `PassThrough` and defaults to the provider-level setting)
75
+ * **verbose** If set to false, it disables the console.logs placed on this warmer lambda (defaults to `true`)
72
76
  * **logRetentionInDays** Set the retention time in days for the log group associated to this warmer lamba
73
77
  * **prewarm** If set to true, it warms up your lambdas right after deploying (defaults to `false`)
74
78
 
@@ -105,6 +109,7 @@ custom:
105
109
  - ./**
106
110
  timeout: 20
107
111
  tracing: true
112
+ verbose: false # Disable the logs
108
113
  logRetentionInDays: 10
109
114
  prewarm: true # Run WarmUp immediately after a deploymentlambda
110
115
  clientContext:
@@ -603,8 +608,6 @@ This software is released under the MIT license. See [the license file](LICENSE)
603
608
  [npm-version-badge]: https://badge.fury.io/js/serverless-plugin-warmup.svg
604
609
  [npm-version-badge-url]: https://www.npmjs.com/package/serverless-plugin-warmup
605
610
  [npm-downloads-badge]: https://img.shields.io/npm/dm/serverless-plugin-warmup.svg
606
- [travis-badge]: https://travis-ci.org/juanjoDiaz/serverless-plugin-warmup.svg
607
- [travis-badge-url]: https://travis-ci.org/juanjoDiaz/serverless-plugin-warmup
608
611
  [coveralls-badge]: https://coveralls.io/repos/juanjoDiaz/serverless-plugin-warmup/badge.svg?branch=master
609
612
  [coveralls-badge-url]: https://coveralls.io/r/juanjoDiaz/serverless-plugin-warmup?branch=master
610
613
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "serverless-plugin-warmup",
3
- "version": "6.2.0",
3
+ "version": "7.1.0",
4
4
  "description": "Keep your lambdas warm during winter.",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -35,6 +35,6 @@
35
35
  "eslint-config-airbnb-base": "^15.0.0",
36
36
  "eslint-plugin-import": "^2.24.2",
37
37
  "husky": "^7.0.2",
38
- "jest": "^27.2.0"
38
+ "jest": "^27.5.0"
39
39
  }
40
40
  }
package/src/config.js CHANGED
@@ -41,6 +41,7 @@ function getWarmerConfig(config, defaultOpts) {
41
41
  ? config.environment
42
42
  : defaultOpts.environment,
43
43
  tracing: (config.tracing !== undefined) ? config.tracing : defaultOpts.tracing,
44
+ verbose: (config.verbose !== undefined) ? config.verbose : defaultOpts.verbose,
44
45
  logRetentionInDays: (config.logRetentionInDays !== undefined)
45
46
  ? config.logRetentionInDays
46
47
  : defaultOpts.logRetentionInDays,
@@ -82,7 +83,7 @@ function getFunctionConfig(config, defaultOpts) {
82
83
  *
83
84
  * @return {Array} - List of functions to be warmed up and their specific configs
84
85
  * */
85
- function getFunctionsByWarmer(service, stage, configsByWarmer) {
86
+ function getFunctionsByWarmer(service, stage, configsByWarmer, serverlessClasses) {
86
87
  const functions = service.getAllFunctions()
87
88
  .map((name) => service.getFunction(name))
88
89
  .map((config) => {
@@ -100,7 +101,7 @@ function getFunctionsByWarmer(service, stage, configsByWarmer) {
100
101
  const unknownWarmers = Object.keys(config.warmup)
101
102
  .filter((warmerName) => configsByWarmer[warmerName] === undefined);
102
103
  if (unknownWarmers.length > 0) {
103
- throw new Error(`WarmUp: Invalid function-level warmup configuration (${unknownWarmers.join(', ')}) in function ${config.name}. Every warmer should be declared in the custom section.`);
104
+ throw new serverlessClasses.Error(`WarmUp: Invalid function-level warmup configuration (${unknownWarmers.join(', ')}) in function ${config.name}. Every warmer should be declared in the custom section.`);
104
105
  }
105
106
 
106
107
  return {
@@ -137,7 +138,7 @@ function getFunctionsByWarmer(service, stage, configsByWarmer) {
137
138
  *
138
139
  * @return {Object} - Configuration options to be used by the plugin
139
140
  * */
140
- function getConfigsByWarmer(service, stage) {
141
+ function getConfigsByWarmer({ service, classes }, stage) {
141
142
  const getWarmerDefaultOpts = (warmerName) => ({
142
143
  folderName: path.join('.warmup', warmerName),
143
144
  cleanFolder: true,
@@ -151,6 +152,7 @@ function getConfigsByWarmer(service, stage) {
151
152
  timeout: 10,
152
153
  environment: Object.keys(service.provider.environment || [])
153
154
  .reduce((obj, k) => ({ ...obj, [k]: undefined }), {}),
155
+ verbose: true,
154
156
  prewarm: false,
155
157
  });
156
158
 
@@ -170,7 +172,7 @@ function getConfigsByWarmer(service, stage) {
170
172
  },
171
173
  }), {});
172
174
 
173
- const functionsByWarmer = getFunctionsByWarmer(service, stage, configsByWarmer);
175
+ const functionsByWarmer = getFunctionsByWarmer(service, stage, configsByWarmer, classes);
174
176
 
175
177
  return Object.entries(configsByWarmer).reduce((warmers, [warmerName, warmerConfig]) => ({
176
178
  ...warmers,
package/src/index.js CHANGED
@@ -28,12 +28,13 @@ class WarmUp {
28
28
  * @constructor
29
29
  *
30
30
  * @param {!Object} serverless - Serverless object
31
- * @param {!Object} options - Serverless options
31
+ * @param {!Object} cliOptions - Serverless cliOptions
32
32
  * */
33
- constructor(serverless, options) {
33
+ constructor(serverless, cliOptions, { log }) {
34
34
  /** Serverless variables */
35
35
  this.serverless = serverless;
36
- this.options = options;
36
+ this.cliOptions = cliOptions;
37
+ this.log = log;
37
38
 
38
39
  this.provider = this.serverless.getProvider('aws');
39
40
 
@@ -46,7 +47,7 @@ class WarmUp {
46
47
  cleanupTempDir: { lifecycleEvents: ['cleanup'] },
47
48
  prewarm: {
48
49
  lifecycleEvents: ['start', 'end'],
49
- options: {
50
+ cliOptions: {
50
51
  warmers: {
51
52
  shortcut: 'w',
52
53
  usage: 'Comma-separated list of warmer names to prewarm.',
@@ -70,8 +71,8 @@ class WarmUp {
70
71
  'warmup:cleanupTempDir:cleanup': this.cleanUp.bind(this),
71
72
  'before:warmup:prewarm:start': this.configPlugin.bind(this),
72
73
  'warmup:prewarm:start': this.prewarmFunctions.bind(this),
73
- // Workaround webpack/bundle plugins, reset the plugin and ignore changes
74
- 'before:package:createDeploymentArtifacts': this.initializeWarmers.bind(this),
74
+ // Workaround webpack/bundle plugins and serverless_sdk
75
+ 'before:package:createDeploymentArtifacts': this.resetWarmerConfigs.bind(this),
75
76
  };
76
77
 
77
78
  // Fixed for issues in Serverles
@@ -85,7 +86,7 @@ class WarmUp {
85
86
  configPlugin() {
86
87
  this.stage = this.stage || this.provider.getStage();
87
88
  this.configsByWarmer = this.configsByWarmer
88
- || getConfigsByWarmer(this.serverless.service, this.stage);
89
+ || getConfigsByWarmer(this.serverless, this.stage);
89
90
  }
90
91
 
91
92
  /**
@@ -101,6 +102,23 @@ class WarmUp {
101
102
  .map(([warmerName, warmerConfig]) => this.configureWarmer(warmerName, warmerConfig)));
102
103
  }
103
104
 
105
+ /**
106
+ * @description Workaround webpack/bundle plugins and serverless_sdk.
107
+ * Reset the plugin and ignore changes
108
+ *
109
+ * @fulfil {} — Warm up set
110
+ * @reject {Error} Warm up error
111
+ *
112
+ * @return {Promise}
113
+ * */
114
+ async resetWarmerConfigs() {
115
+ Object.entries(this.configsByWarmer)
116
+ .forEach(([warmerName, warmerConfig]) => {
117
+ if (warmerConfig.functions.length === 0) return;
118
+ addWarmUpFunctionToService(this.serverless.service, warmerName, warmerConfig);
119
+ });
120
+ }
121
+
104
122
  /**
105
123
  * @description Warmup cleanup hook.
106
124
  *
@@ -122,7 +140,7 @@ class WarmUp {
122
140
  );
123
141
  } catch (err) {
124
142
  if (err.code !== 'ENOENT') {
125
- this.serverless.cli.log(`WarmUp: Couldn't clean up temporary folder ${folderToClean}.`);
143
+ this.log.error(`WarmUp: Couldn't clean up temporary folder ${folderToClean}.`);
126
144
  }
127
145
  }
128
146
  }));
@@ -137,7 +155,7 @@ class WarmUp {
137
155
  }
138
156
  } catch (err) {
139
157
  if (err.code !== 'ENOENT') {
140
- this.serverless.cli.log('WarmUp: Couldn\'t clean up temporary folder .warmup.');
158
+ this.log.error('WarmUp: Couldn\'t clean up temporary folder .warmup.');
141
159
  }
142
160
  }
143
161
  }
@@ -151,8 +169,8 @@ class WarmUp {
151
169
  * @return {Promise}
152
170
  * */
153
171
  async prewarmFunctions() {
154
- const warmerNames = (this.options.warmers)
155
- ? this.options.warmers.split(',')
172
+ const warmerNames = (this.cliOptions.warmers)
173
+ ? this.cliOptions.warmers.split(',')
156
174
  : Object.entries(this.configsByWarmer)
157
175
  .filter(([, warmerConfig]) => warmerConfig.prewarm)
158
176
  .map(([warmerName]) => warmerName);
@@ -160,7 +178,7 @@ class WarmUp {
160
178
  await Promise.all(warmerNames.map(async (warmerName) => {
161
179
  const warmerConfig = this.configsByWarmer[warmerName];
162
180
  if (!warmerConfig) {
163
- throw new Error(`Warmer names ${warmerName} doesn't exist.`);
181
+ throw new this.serverless.classes.Error(`Warmer names ${warmerName} doesn't exist.`);
164
182
  }
165
183
  addWarmUpFunctionToService(this.serverless.service, warmerName, warmerConfig);
166
184
  await this.invokeWarmer(warmerName, warmerConfig);
@@ -173,24 +191,20 @@ class WarmUp {
173
191
  * */
174
192
  async configureWarmer(warmerName, warmerConfig) {
175
193
  if (warmerConfig.functions.length === 0) {
176
- this.serverless.cli.log(`WarmUp: Skipping warmer "${warmerName}" creation. No functions to warm up.`);
177
- return;
178
- }
179
-
180
- // Avoid double processing due to the workaround for webpack/bundle plugins
181
- // resetting the plugin and ignoring changes
182
- if (this.serverless.service.functions[`warmUpPlugin${capitalize(warmerName)}`]) {
194
+ this.log.warning(`WarmUp: Skipping warmer "${warmerName}" creation. No functions to warm up.`);
183
195
  return;
184
196
  }
185
197
 
186
- this.serverless.cli.log(`WarmUp: Creating warmer "${warmerName}" to warm up ${warmerConfig.functions.length} function${warmerConfig.functions.length === 1 ? '' : 's'}:`);
187
- warmerConfig.functions.forEach((func) => this.serverless.cli.log(` * ${func.name}`));
198
+ this.log.notice(`WarmUp: Creating warmer "${warmerName}" to warm up ${warmerConfig.functions.length} function${warmerConfig.functions.length === 1 ? '' : 's'}`);
199
+ this.log.info(':');
200
+ warmerConfig.functions.forEach((func) => this.log.info(` * ${func.name}`));
188
201
 
189
202
  const handlerFolder = path.join(this.serviceDir, warmerConfig.folderName);
190
203
 
191
204
  await createWarmUpFunctionArtifact(
192
205
  warmerConfig.functions,
193
206
  warmerConfig.tracing,
207
+ warmerConfig.verbose,
194
208
  this.provider.getRegion(),
195
209
  handlerFolder,
196
210
  );
@@ -209,11 +223,11 @@ class WarmUp {
209
223
 
210
224
  async invokeWarmer(warmerName, warmerConfig) {
211
225
  if (warmerConfig.functions.length === 0) {
212
- this.serverless.cli.log(`WarmUp: Skipping prewarming using warmer "${warmerName}". No functions to warm up.`);
226
+ this.log.warning(`WarmUp: Skipping prewarming using warmer "${warmerName}". No functions to warm up.`);
213
227
  return;
214
228
  }
215
229
 
216
- this.serverless.cli.log(`WarmUp: Prewarming up your functions using warmer "${warmerName}".`);
230
+ this.log.notice(`WarmUp: Prewarming up your functions using warmer "${warmerName}".`);
217
231
 
218
232
  try {
219
233
  const { SERVERLESS_ALIAS } = this.serverless.service.getFunction(`warmUpPlugin${capitalize(warmerName)}`).environment || {};
@@ -226,9 +240,9 @@ class WarmUp {
226
240
  };
227
241
 
228
242
  await this.provider.request('Lambda', 'invoke', params);
229
- this.serverless.cli.log(`WarmUp: Warmer "${warmerName}" successfully prewarmed your functions.`);
243
+ this.log.notice(`WarmUp: Warmer "${warmerName}" successfully prewarmed your functions.`);
230
244
  } catch (err) {
231
- this.serverless.cli.log(`WarmUp: Error while prewarming your functions using warmer "${warmerName}".`, err);
245
+ this.log.error(`WarmUp: Error while prewarming your functions using warmer "${warmerName}".`, err);
232
246
  }
233
247
  }
234
248
  }
package/src/schema.js CHANGED
@@ -107,6 +107,7 @@ function extendServerlessSchema(serverless) {
107
107
  timeout: { $ref: '#/definitions/awsLambdaTimeout' },
108
108
  environment: { $ref: '#/definitions/awsLambdaEnvironment' },
109
109
  tracing: { $ref: '#/definitions/awsLambdaTracing' },
110
+ verbose: { type: 'boolean' },
110
111
  logRetentionInDays: {
111
112
  type: 'number',
112
113
  enum: [1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, 3653],
@@ -139,6 +140,7 @@ function extendServerlessSchema(serverless) {
139
140
  serverless.configSchemaHandler.defineCustomProperties({
140
141
  properties: {
141
142
  warmup: {
143
+ type: 'object',
142
144
  patternProperties: {
143
145
  '.*': {
144
146
  type: 'object',
@@ -156,6 +158,7 @@ function extendServerlessSchema(serverless) {
156
158
  type: 'object',
157
159
  properties: {
158
160
  warmup: {
161
+ type: 'object',
159
162
  patternProperties: {
160
163
  '.*': {
161
164
  type: 'object',
package/src/warmer.js CHANGED
@@ -94,7 +94,7 @@ function addWarmUpFunctionRoleToResources(service, stage, warmerName, warmerConf
94
94
  'lambda:InvokeFunction',
95
95
  ],
96
96
  Resource: warmerConfig.functions.map((fn) => ({
97
- 'Fn::Sub': `arn:\${AWS::Partition}:lambda:\${AWS::Region}:\${AWS::AccountId}:function:${fn.name}`,
97
+ 'Fn::Sub': `arn:\${AWS::Partition}:lambda:\${AWS::Region}:\${AWS::AccountId}:function:${fn.name}*`,
98
98
  })),
99
99
  },
100
100
  {
@@ -125,7 +125,7 @@ function addWarmUpFunctionRoleToResources(service, stage, warmerName, warmerConf
125
125
  *
126
126
  * @return {Promise}
127
127
  * */
128
- async function createWarmUpFunctionArtifact(functions, tracing, region, handlerFolder) {
128
+ async function createWarmUpFunctionArtifact(functions, tracing, verbose, region, handlerFolder) {
129
129
  const warmUpFunction = `'use strict';
130
130
 
131
131
  /** Generated by Serverless WarmUp Plugin **/
@@ -143,28 +143,32 @@ const lambda = new AWS.Lambda({
143
143
  });
144
144
  const functions = ${JSON.stringify(functions, null, ' ')};
145
145
 
146
+ function logVerbose(str) {
147
+ ${verbose ? 'console.log(str);' : ''}
148
+ }
149
+
146
150
  function getConcurrency(func, envVars) {
147
151
  const functionConcurrency = envVars[\`WARMUP_CONCURRENCY_\${func.name.toUpperCase().replace(/-/g, '_')}\`];
148
152
 
149
153
  if (functionConcurrency) {
150
154
  const concurrency = parseInt(functionConcurrency);
151
- console.log(\`Warming up function: \${func.name} with concurrency: \${concurrency} (from function-specific environment variable)\`);
155
+ logVerbose(\`Warming up function: \${func.name} with concurrency: \${concurrency} (from function-specific environment variable)\`);
152
156
  return concurrency;
153
157
  }
154
158
 
155
159
  if (envVars.WARMUP_CONCURRENCY) {
156
160
  const concurrency = parseInt(envVars.WARMUP_CONCURRENCY);
157
- console.log(\`Warming up function: \${func.name} with concurrency: \${concurrency} (from global environment variable)\`);
161
+ logVerbose(\`Warming up function: \${func.name} with concurrency: \${concurrency} (from global environment variable)\`);
158
162
  return concurrency;
159
163
  }
160
164
 
161
165
  const concurrency = parseInt(func.config.concurrency);
162
- console.log(\`Warming up function: \${func.name} with concurrency: \${concurrency}\`);
166
+ logVerbose(\`Warming up function: \${func.name} with concurrency: \${concurrency}\`);
163
167
  return concurrency;
164
168
  }
165
169
 
166
170
  module.exports.warmUp = async (event, context) => {
167
- console.log('Warm Up Start');
171
+ logVerbose('Warm Up Start');
168
172
 
169
173
  const invokes = await Promise.all(functions.map(async (func) => {
170
174
  const concurrency = getConcurrency(func, process.env);
@@ -186,15 +190,15 @@ module.exports.warmUp = async (event, context) => {
186
190
 
187
191
  try {
188
192
  await Promise.all(Array(concurrency).fill(0).map(async () => await lambda.invoke(params).promise()));
189
- console.log(\`Warm Up Invoke Success: \${func.name}\`);
193
+ logVerbose(\`Warm Up Invoke Success: \${func.name}\`);
190
194
  return true;
191
195
  } catch (e) {
192
- console.log(\`Warm Up Invoke Error: \${func.name}\`, e);
196
+ console.error(\`Warm Up Invoke Error: \${func.name}\`, e);
193
197
  return false;
194
198
  }
195
199
  }));
196
200
 
197
- console.log(\`Warm Up Finished with \${invokes.filter(r => !r).length} invoke errors\`);
201
+ logVerbose(\`Warm Up Finished with \${invokes.filter(r => !r).length} invoke errors\`);
198
202
  }`;
199
203
 
200
204
  /** Write warm up file */