serverless-plugin-warmup 8.3.0 → 8.4.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 +3 -3
- package/biome.json +47 -0
- package/package.json +36 -38
- package/src/config.js +159 -147
- package/src/index.js +245 -225
- package/src/schema.js +178 -163
- package/src/utils.js +1 -1
- package/src/warmer.js +130 -142
- package/.eslintignore +0 -2
- package/.eslintrc.js +0 -9
package/src/index.js
CHANGED
|
@@ -12,9 +12,9 @@ const path = require('path');
|
|
|
12
12
|
const { extendServerlessSchema } = require('./schema');
|
|
13
13
|
const { getConfigsByWarmer } = require('./config');
|
|
14
14
|
const {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
addWarmUpFunctionRoleToResources,
|
|
16
|
+
createWarmUpFunctionArtifact,
|
|
17
|
+
addWarmUpFunctionToService,
|
|
18
18
|
} = require('./warmer');
|
|
19
19
|
const { capitalize } = require('./utils');
|
|
20
20
|
|
|
@@ -23,228 +23,248 @@ const { capitalize } = require('./utils');
|
|
|
23
23
|
* @class WarmUp
|
|
24
24
|
* */
|
|
25
25
|
class WarmUp {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
26
|
+
/**
|
|
27
|
+
* @description Serverless Warm Up
|
|
28
|
+
* @constructor
|
|
29
|
+
*
|
|
30
|
+
* @param {!Object} serverless - Serverless object
|
|
31
|
+
* @param {!Object} cliOptions - Serverless cliOptions
|
|
32
|
+
* */
|
|
33
|
+
constructor(serverless, cliOptions, { log }) {
|
|
34
|
+
/** Serverless variables */
|
|
35
|
+
this.serverless = serverless;
|
|
36
|
+
this.cliOptions = cliOptions;
|
|
37
|
+
this.log = log;
|
|
38
|
+
|
|
39
|
+
this.provider = this.serverless.getProvider('aws');
|
|
40
|
+
|
|
41
|
+
extendServerlessSchema(this.serverless);
|
|
42
|
+
|
|
43
|
+
this.commands = {
|
|
44
|
+
warmup: {
|
|
45
|
+
commands: {
|
|
46
|
+
addWarmers: { lifecycleEvents: ['addWarmers'] },
|
|
47
|
+
cleanupTempDir: { lifecycleEvents: ['cleanup'] },
|
|
48
|
+
prewarm: {
|
|
49
|
+
lifecycleEvents: ['start', 'end'],
|
|
50
|
+
cliOptions: {
|
|
51
|
+
warmers: {
|
|
52
|
+
shortcut: 'w',
|
|
53
|
+
usage: 'Comma-separated list of warmer names to prewarm.',
|
|
54
|
+
type: 'string',
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
usage: 'Invoke a warmer to warm the functions on demand.',
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
this.hooks = {
|
|
64
|
+
'after:package:initialize': () => this.serverless.pluginManager.spawn('warmup:addWarmers'),
|
|
65
|
+
'after:package:createDeploymentArtifacts': () =>
|
|
66
|
+
this.serverless.pluginManager.spawn('warmup:cleanupTempDir'),
|
|
67
|
+
'after:deploy:deploy': () => this.serverless.pluginManager.spawn('warmup:prewarm'),
|
|
68
|
+
'after:deploy:function:deploy': () => this.serverless.pluginManager.spawn('warmup:prewarm'),
|
|
69
|
+
'before:warmup:addWarmers:addWarmers': this.configPlugin.bind(this),
|
|
70
|
+
'warmup:addWarmers:addWarmers': this.initializeWarmers.bind(this),
|
|
71
|
+
'before:warmup:cleanupTempDir:cleanup': this.configPlugin.bind(this),
|
|
72
|
+
'warmup:cleanupTempDir:cleanup': this.cleanUp.bind(this),
|
|
73
|
+
'before:warmup:prewarm:start': this.configPlugin.bind(this),
|
|
74
|
+
'warmup:prewarm:start': this.prewarmFunctions.bind(this),
|
|
75
|
+
// Workaround webpack/bundle plugins and serverless_sdk
|
|
76
|
+
'before:package:createDeploymentArtifacts': this.resetWarmerConfigs.bind(this),
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Fixed for issues in Serverles
|
|
80
|
+
// https://github.com/serverless/serverless/pull/9307
|
|
81
|
+
this.serviceDir = this.serverless.serviceDir || this.serverless.config.servicePath || '';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @description Configures the plugin if needed or do nothing if already configured.
|
|
86
|
+
* */
|
|
87
|
+
configPlugin() {
|
|
88
|
+
this.stage = this.stage || this.provider.getStage();
|
|
89
|
+
this.configsByWarmer = this.configsByWarmer || getConfigsByWarmer(this.serverless, this.stage);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @description Warm up initialize hook. Create warmer function and add it to the service.
|
|
94
|
+
*
|
|
95
|
+
* @fulfil {} — Warm up set
|
|
96
|
+
* @reject {Error} Warm up error
|
|
97
|
+
*
|
|
98
|
+
* @return {Promise}
|
|
99
|
+
* */
|
|
100
|
+
async initializeWarmers() {
|
|
101
|
+
await Promise.all(
|
|
102
|
+
Object.entries(this.configsByWarmer).map(([warmerName, warmerConfig]) =>
|
|
103
|
+
this.configureWarmer(warmerName, warmerConfig),
|
|
104
|
+
),
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @description Workaround webpack/bundle plugins and serverless_sdk.
|
|
110
|
+
* Reset the plugin and ignore changes
|
|
111
|
+
*
|
|
112
|
+
* @fulfil {} — Warm up set
|
|
113
|
+
* @reject {Error} Warm up error
|
|
114
|
+
*
|
|
115
|
+
* @return {Promise}
|
|
116
|
+
* */
|
|
117
|
+
async resetWarmerConfigs() {
|
|
118
|
+
Object.entries(this.configsByWarmer).forEach(([warmerName, warmerConfig]) => {
|
|
119
|
+
if (warmerConfig.functions.length === 0) return;
|
|
120
|
+
addWarmUpFunctionToService(this.serverless.service, warmerName, warmerConfig);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* @description Warmup cleanup hook.
|
|
126
|
+
*
|
|
127
|
+
* @fulfil {} — Temp folders cleaned up
|
|
128
|
+
* @reject {Error} Couldn't cleaned up temp folders
|
|
129
|
+
*
|
|
130
|
+
* @return {Promise}
|
|
131
|
+
* */
|
|
132
|
+
async cleanUp() {
|
|
133
|
+
const foldersToClean = Array.from(
|
|
134
|
+
new Set(
|
|
135
|
+
Object.values(this.configsByWarmer)
|
|
136
|
+
.filter((config) => config.cleanFolder)
|
|
137
|
+
.map((config) => config.folderName),
|
|
138
|
+
),
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
await Promise.all(
|
|
142
|
+
foldersToClean.map(async (folderToClean) => {
|
|
143
|
+
try {
|
|
144
|
+
await fs.rm(path.join(this.serviceDir, folderToClean), { recursive: true });
|
|
145
|
+
} catch (err) {
|
|
146
|
+
if (err.code !== 'ENOENT') {
|
|
147
|
+
this.log.error(`WarmUp: Couldn't clean up temporary folder ${folderToClean}.`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}),
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
const defaultDir = path.join(this.serviceDir, '.warmup');
|
|
155
|
+
if (
|
|
156
|
+
foldersToClean.some((folder) => folder.startsWith('.warmup')) &&
|
|
157
|
+
(await fs.readdir(defaultDir)).length === 0
|
|
158
|
+
) {
|
|
159
|
+
await fs.rm(defaultDir, { recursive: true });
|
|
160
|
+
}
|
|
161
|
+
} catch (err) {
|
|
162
|
+
if (err.code !== 'ENOENT') {
|
|
163
|
+
this.log.error("WarmUp: Couldn't clean up temporary folder .warmup.");
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* @description Warmer prewarm functions hook
|
|
170
|
+
*
|
|
171
|
+
* @fulfil {} — Functions warmed up sucessfuly
|
|
172
|
+
* @reject {Error} Functions couldn't be warmed up
|
|
173
|
+
*
|
|
174
|
+
* @return {Promise}
|
|
175
|
+
* */
|
|
176
|
+
async prewarmFunctions() {
|
|
177
|
+
const warmerNames = this.cliOptions.warmers
|
|
178
|
+
? this.cliOptions.warmers.split(',')
|
|
179
|
+
: Object.entries(this.configsByWarmer)
|
|
180
|
+
.filter(([, warmerConfig]) => warmerConfig.prewarm)
|
|
181
|
+
.map(([warmerName]) => warmerName);
|
|
182
|
+
|
|
183
|
+
await Promise.all(
|
|
184
|
+
warmerNames.map(async (warmerName) => {
|
|
185
|
+
const warmerConfig = this.configsByWarmer[warmerName];
|
|
186
|
+
if (!warmerConfig) {
|
|
187
|
+
throw new this.serverless.classes.Error(`Warmer names ${warmerName} doesn't exist.`);
|
|
188
|
+
}
|
|
189
|
+
addWarmUpFunctionToService(this.serverless.service, warmerName, warmerConfig);
|
|
190
|
+
await this.invokeWarmer(warmerName, warmerConfig);
|
|
191
|
+
}),
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* @description Create warm up function code and write it to the handler file
|
|
197
|
+
* and add warm up function to the service
|
|
198
|
+
* */
|
|
199
|
+
async configureWarmer(warmerName, warmerConfig) {
|
|
200
|
+
if (warmerConfig.functions.length === 0) {
|
|
201
|
+
this.log.warning(
|
|
202
|
+
`WarmUp: Skipping warmer "${warmerName}" creation. No functions to warm up.`,
|
|
203
|
+
);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
this.log.notice(
|
|
208
|
+
`WarmUp: Creating warmer "${warmerName}" to warm up ${warmerConfig.functions.length} function${warmerConfig.functions.length === 1 ? '' : 's'}`,
|
|
209
|
+
);
|
|
210
|
+
this.log.info(':');
|
|
211
|
+
warmerConfig.functions.forEach((func) => {
|
|
212
|
+
this.log.info(` * ${func.name}`);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
const handlerFolder = path.join(this.serviceDir, warmerConfig.folderName);
|
|
216
|
+
|
|
217
|
+
await createWarmUpFunctionArtifact(
|
|
218
|
+
warmerConfig.functions,
|
|
219
|
+
warmerConfig.tracing,
|
|
220
|
+
warmerConfig.verbose,
|
|
221
|
+
this.provider.getRegion(),
|
|
222
|
+
handlerFolder,
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
if (warmerConfig.role === undefined) {
|
|
226
|
+
addWarmUpFunctionRoleToResources(
|
|
227
|
+
this.serverless.service,
|
|
228
|
+
this.stage,
|
|
229
|
+
warmerName,
|
|
230
|
+
warmerConfig,
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
addWarmUpFunctionToService(this.serverless.service, warmerName, warmerConfig);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
async invokeWarmer(warmerName, warmerConfig) {
|
|
238
|
+
if (warmerConfig.functions.length === 0) {
|
|
239
|
+
this.log.warning(
|
|
240
|
+
`WarmUp: Skipping prewarming using warmer "${warmerName}". No functions to warm up.`,
|
|
241
|
+
);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
this.log.notice(`WarmUp: Prewarming up your functions using warmer "${warmerName}".`);
|
|
246
|
+
|
|
247
|
+
try {
|
|
248
|
+
const { SERVERLESS_ALIAS } =
|
|
249
|
+
this.serverless.service.getFunction(`warmUpPlugin${capitalize(warmerName)}`).environment ||
|
|
250
|
+
{};
|
|
251
|
+
const params = {
|
|
252
|
+
FunctionName: warmerConfig.name,
|
|
253
|
+
InvocationType: 'RequestResponse',
|
|
254
|
+
LogType: 'None',
|
|
255
|
+
Qualifier: SERVERLESS_ALIAS,
|
|
256
|
+
Payload: warmerConfig.payload,
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
await this.provider.request('Lambda', 'invoke', params);
|
|
260
|
+
this.log.notice(`WarmUp: Warmer "${warmerName}" successfully prewarmed your functions.`);
|
|
261
|
+
} catch (err) {
|
|
262
|
+
this.log.error(
|
|
263
|
+
`WarmUp: Error while prewarming your functions using warmer "${warmerName}".`,
|
|
264
|
+
err,
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
248
268
|
}
|
|
249
269
|
|
|
250
270
|
/** Export WarmUp class */
|