@certik/skynet 0.10.7 → 0.10.8
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/.editorconfig +5 -5
- package/.eslintrc.js +13 -13
- package/.prettierrc.js +3 -3
- package/CHANGELOG.md +381 -372
- package/README.md +23 -23
- package/abi.js +353 -353
- package/ably.js +29 -29
- package/address.js +18 -18
- package/api.js +128 -105
- package/app.js +709 -709
- package/availability.js +58 -58
- package/block.js +83 -83
- package/cli.js +53 -53
- package/const.js +92 -92
- package/deploy.js +676 -676
- package/dynamodb.js +444 -444
- package/env.js +90 -90
- package/examples/api +70 -73
- package/examples/consumer +47 -47
- package/examples/indexer +65 -65
- package/examples/mode-indexer +82 -82
- package/examples/producer +80 -80
- package/indexer.js +596 -595
- package/inquiry.js +14 -14
- package/kafka.js +444 -443
- package/labelling.js +90 -90
- package/log.js +29 -29
- package/metric.js +65 -65
- package/monitor.js +191 -191
- package/opsgenie.js +55 -55
- package/package.json +37 -37
- package/price.js +48 -48
- package/primitive.js +77 -77
- package/proxy.js +157 -157
- package/rateLimit.js +21 -21
- package/s3.js +122 -122
- package/scan.js +74 -74
- package/selector.js +53 -53
- package/slack.js +87 -87
- package/snowflake.js +36 -36
- package/sqs.js +12 -12
- package/token.js +46 -46
- package/transaction.js +41 -41
- package/util.js +67 -67
- package/web3.js +117 -117
package/app.js
CHANGED
|
@@ -1,709 +1,709 @@
|
|
|
1
|
-
const { EOL } = require("os");
|
|
2
|
-
const fetch = require("node-fetch");
|
|
3
|
-
const { SKYNET_API_PREFIX } = require("./const");
|
|
4
|
-
const {
|
|
5
|
-
createIndexerApp,
|
|
6
|
-
createModeIndexerApp,
|
|
7
|
-
getIndexerLatestId,
|
|
8
|
-
getIndexerValidatedId,
|
|
9
|
-
getIndexerState,
|
|
10
|
-
} = require("./indexer");
|
|
11
|
-
const { createDeploy, createModeDeploy } = require("./deploy");
|
|
12
|
-
const { createProducerApp, createConsumerApp, getProducerLatestId } = require("./kafka");
|
|
13
|
-
const { startApiApp } = require("./api");
|
|
14
|
-
const { createMonitor, ERROR_LEVEL } = require("./monitor");
|
|
15
|
-
const { getBinaryName, detectBin, detectWorkingDirectory } = require("./cli");
|
|
16
|
-
|
|
17
|
-
function printAppHelp(hasCheck) {
|
|
18
|
-
console.log(`
|
|
19
|
-
Usage
|
|
20
|
-
|
|
21
|
-
$ ${getBinaryName()} run <options>${hasCheck ? `\n $ ${getBinaryName()} check <options>` : ""}
|
|
22
|
-
$ ${getBinaryName()} deploy <options>
|
|
23
|
-
$ ${getBinaryName()} delete <options>
|
|
24
|
-
`);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function isDeleteCommand(command) {
|
|
28
|
-
return ["delete", "stop", "remove"].includes(command);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
async function checkDeployEnv(env) {
|
|
32
|
-
// check sensitive env
|
|
33
|
-
const missingEnvs = [];
|
|
34
|
-
|
|
35
|
-
for (let key of Object.keys(env)) {
|
|
36
|
-
if (env[key] === SENSITIVE_VALUE) {
|
|
37
|
-
const res = await fetch(`${SKYNET_API_PREFIX}/secrets/verify?name=${key}`);
|
|
38
|
-
|
|
39
|
-
if (res.ok) {
|
|
40
|
-
const hasEnv = await res.json();
|
|
41
|
-
|
|
42
|
-
if (!hasEnv) {
|
|
43
|
-
missingEnvs.push(key);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (missingEnvs.length > 0) {
|
|
50
|
-
console.log(`Missing the following sensitive environment on nomad server:${EOL}- ${missingEnvs.join(EOL + "- ")}`);
|
|
51
|
-
|
|
52
|
-
process.exit(1);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function checkAndSetEnv(env) {
|
|
57
|
-
const missingEnvs = [];
|
|
58
|
-
|
|
59
|
-
for (let key of Object.keys(env)) {
|
|
60
|
-
if (env[key]) {
|
|
61
|
-
process.env[key] = env[key];
|
|
62
|
-
} else if (!process.env[key]) {
|
|
63
|
-
missingEnvs.push(key);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (missingEnvs.length > 0) {
|
|
68
|
-
console.log(`The following environment value shouldn't be empty:${EOL}- ${missingEnvs.join(EOL + "- ")}`);
|
|
69
|
-
|
|
70
|
-
process.exit(1);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function createApp({ parameterErrors, env, check, onRun, onDeploy, onCheck }) {
|
|
75
|
-
if (parameterErrors.length > 0) {
|
|
76
|
-
console.log(`Parameter Validation Failed:${EOL}- ${parameterErrors.join(EOL + "- ")}`);
|
|
77
|
-
|
|
78
|
-
process.exit(1);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return () => {
|
|
82
|
-
const subCommand = process.argv[2];
|
|
83
|
-
|
|
84
|
-
// emulate command line call without subcmd
|
|
85
|
-
process.argv = [process.argv[0], process.argv[1], ...process.argv.slice(3)];
|
|
86
|
-
|
|
87
|
-
if (subCommand === "run") {
|
|
88
|
-
checkAndSetEnv(env);
|
|
89
|
-
|
|
90
|
-
onRun();
|
|
91
|
-
} else if (subCommand === "deploy" || isDeleteCommand(subCommand)) {
|
|
92
|
-
if (isDeleteCommand(subCommand)) {
|
|
93
|
-
process.argv.push("--stop");
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (process.argv.includes("--production")) {
|
|
97
|
-
checkDeployEnv(env)
|
|
98
|
-
.then(() => onDeploy())
|
|
99
|
-
.catch(console.error);
|
|
100
|
-
} else {
|
|
101
|
-
onDeploy();
|
|
102
|
-
}
|
|
103
|
-
} else if (!!check && subCommand === "check") {
|
|
104
|
-
checkAndSetEnv(env);
|
|
105
|
-
|
|
106
|
-
onCheck();
|
|
107
|
-
} else {
|
|
108
|
-
printAppHelp(!!check);
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function checkEnvParameter(env) {
|
|
114
|
-
const errors = [];
|
|
115
|
-
|
|
116
|
-
const envKeys = Object.keys(env);
|
|
117
|
-
|
|
118
|
-
envKeys.forEach((k) => {
|
|
119
|
-
if (!env[k] && env[k] !== SENSITIVE_VALUE) {
|
|
120
|
-
errors.push(`must have valid non-empty value for env.${k}`);
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
return errors;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function checkIndexerBuildParameter(build) {
|
|
128
|
-
const errors = [];
|
|
129
|
-
|
|
130
|
-
if (!build?.func) {
|
|
131
|
-
errors.push("must define build.func");
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (!build?.cpu) {
|
|
135
|
-
errors.push("must define build.cpu");
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (!build?.mem) {
|
|
139
|
-
errors.push("must define build.mem");
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return errors;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function checkCheckParameter(check) {
|
|
146
|
-
if (!check) {
|
|
147
|
-
return [];
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const errors = [];
|
|
151
|
-
|
|
152
|
-
if (!check?.func) {
|
|
153
|
-
errors.push("must define check.func");
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (!check?.schedule) {
|
|
157
|
-
errors.push("must define check.schedule");
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
return errors;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function checkStateParameter(state) {
|
|
164
|
-
const errors = [];
|
|
165
|
-
|
|
166
|
-
if (!state?.type) {
|
|
167
|
-
errors.push("must define state.type");
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (!state?.getMaxId) {
|
|
171
|
-
errors.push("must define state.getMaxId");
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return errors;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
function indexer({ name, selector, build, check, env = {}, region = "us-east-1" }) {
|
|
178
|
-
return createApp({
|
|
179
|
-
parameterErrors: [...checkIndexerBuildParameter(build), ...checkCheckParameter(check), ...checkEnvParameter(env)],
|
|
180
|
-
env,
|
|
181
|
-
check,
|
|
182
|
-
onRun: () => {
|
|
183
|
-
const { run } = createIndexerApp({
|
|
184
|
-
binaryName: `${getBinaryName()} run`,
|
|
185
|
-
name,
|
|
186
|
-
selector,
|
|
187
|
-
build: build.func,
|
|
188
|
-
maxRetry: build.maxRetry,
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
run();
|
|
192
|
-
},
|
|
193
|
-
onDeploy: () => {
|
|
194
|
-
const bin = detectBin();
|
|
195
|
-
|
|
196
|
-
const { deploy } = createDeploy({
|
|
197
|
-
binaryName: `${getBinaryName()} deploy`,
|
|
198
|
-
name,
|
|
199
|
-
workingDirectory: detectWorkingDirectory(),
|
|
200
|
-
bin: `${bin} run`,
|
|
201
|
-
selector,
|
|
202
|
-
region,
|
|
203
|
-
env,
|
|
204
|
-
schedule: build.schedule,
|
|
205
|
-
restart: build.restart,
|
|
206
|
-
cpu: build.cpu,
|
|
207
|
-
mem: build.mem,
|
|
208
|
-
check: check && {
|
|
209
|
-
bin: `${bin} check`,
|
|
210
|
-
schedule: check.schedule,
|
|
211
|
-
cpu: check.cpu || 100,
|
|
212
|
-
mem: check.mem || 100,
|
|
213
|
-
},
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
deploy();
|
|
217
|
-
},
|
|
218
|
-
onCheck: () => {
|
|
219
|
-
const { monitor } = createMonitor({
|
|
220
|
-
binaryName: `${getBinaryName()} check`,
|
|
221
|
-
name,
|
|
222
|
-
type: "stateless",
|
|
223
|
-
selector,
|
|
224
|
-
check: check.func,
|
|
225
|
-
maxRetry: check.maxRetry,
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
monitor();
|
|
229
|
-
},
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
function checkModeIndexerBuildParameter(build) {
|
|
234
|
-
const errors = [];
|
|
235
|
-
|
|
236
|
-
if (!build?.func) {
|
|
237
|
-
errors.push("must define build.func");
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
if (!build?.cpu) {
|
|
241
|
-
errors.push("must define build.cpu");
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
if (!build?.mem) {
|
|
245
|
-
errors.push("must define build.mem");
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
return errors;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
function checkModeIndexerValidateParameter(validate) {
|
|
252
|
-
const errors = [];
|
|
253
|
-
|
|
254
|
-
if (validate) {
|
|
255
|
-
if (!validate.func) {
|
|
256
|
-
errors.push("must define validate.func");
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
if (!validate.cpu) {
|
|
260
|
-
errors.push("must define validate.cpu");
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
if (!validate.mem) {
|
|
264
|
-
errors.push("must define validate.mem");
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
return errors;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
function modeIndexer({ name, selector, state, build, validate, check, env = {}, region = "us-east-1" }) {
|
|
272
|
-
return createApp({
|
|
273
|
-
parameterErrors: [
|
|
274
|
-
...checkModeIndexerBuildParameter(build),
|
|
275
|
-
...checkModeIndexerValidateParameter(validate),
|
|
276
|
-
...checkStateParameter(state),
|
|
277
|
-
...checkCheckParameter(check),
|
|
278
|
-
...checkEnvParameter(env),
|
|
279
|
-
],
|
|
280
|
-
env,
|
|
281
|
-
check,
|
|
282
|
-
onRun: () => {
|
|
283
|
-
const { run } = createModeIndexerApp({
|
|
284
|
-
binaryName: `${getBinaryName()} run`,
|
|
285
|
-
name,
|
|
286
|
-
selector,
|
|
287
|
-
build: build.func,
|
|
288
|
-
maxRetry: build.maxRetry,
|
|
289
|
-
buildBatchSize: build.batchSize,
|
|
290
|
-
buildConcurrency: build.concurrency,
|
|
291
|
-
validate: validate && validate.func,
|
|
292
|
-
validateBatchSize: validate && validate.batchSize,
|
|
293
|
-
validateConcurrency: validate && validate.concurrency,
|
|
294
|
-
state,
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
run();
|
|
298
|
-
},
|
|
299
|
-
onDeploy: () => {
|
|
300
|
-
const bin = detectBin();
|
|
301
|
-
|
|
302
|
-
const { deploy } = createModeDeploy({
|
|
303
|
-
binaryName: `${getBinaryName()} deploy`,
|
|
304
|
-
name,
|
|
305
|
-
workingDirectory: detectWorkingDirectory(),
|
|
306
|
-
bin: `${bin} run`,
|
|
307
|
-
selector,
|
|
308
|
-
region,
|
|
309
|
-
env,
|
|
310
|
-
|
|
311
|
-
deltaSchedule: build.schedule,
|
|
312
|
-
deltaCpu: build.cpu,
|
|
313
|
-
deltaMem: build.mem,
|
|
314
|
-
rebuildCpu: build.cpu,
|
|
315
|
-
rebuildMem: build.mem,
|
|
316
|
-
|
|
317
|
-
validateSchedule: validate && validate.schedule,
|
|
318
|
-
validateCpu: validate && validate.cpu,
|
|
319
|
-
validateMem: validate && validate.mem,
|
|
320
|
-
|
|
321
|
-
check: check && {
|
|
322
|
-
bin: `${bin} check`,
|
|
323
|
-
schedule: check.schedule,
|
|
324
|
-
cpu: check.cpu || 100,
|
|
325
|
-
mem: check.mem || 100,
|
|
326
|
-
},
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
deploy();
|
|
330
|
-
},
|
|
331
|
-
onCheck: () => {
|
|
332
|
-
const { monitor } = createMonitor({
|
|
333
|
-
binaryName: `${getBinaryName()} check`,
|
|
334
|
-
name,
|
|
335
|
-
getState: async (nam, selectorFlags) => {
|
|
336
|
-
const latestId = await getIndexerLatestId(nam, selectorFlags);
|
|
337
|
-
const validatedId = await getIndexerValidatedId(nam, selectorFlags);
|
|
338
|
-
const buildState = await getIndexerState(nam, selectorFlags);
|
|
339
|
-
|
|
340
|
-
return { latestId, validatedId, buildState };
|
|
341
|
-
},
|
|
342
|
-
selector,
|
|
343
|
-
mode: true,
|
|
344
|
-
check: check.func,
|
|
345
|
-
maxRetry: check.maxRetry,
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
monitor();
|
|
349
|
-
},
|
|
350
|
-
});
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
function checkProducerProduceParameter(produce) {
|
|
354
|
-
const errors = [];
|
|
355
|
-
|
|
356
|
-
if (!produce?.func) {
|
|
357
|
-
errors.push("must define produce.func");
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
if (!produce?.topic) {
|
|
361
|
-
errors.push("must define produce.topic");
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
if (!produce?.deadLetterTopic) {
|
|
365
|
-
errors.push("must define produce.deadLetterTopic");
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
if (!produce?.cpu) {
|
|
369
|
-
errors.push("must define produce.cpu");
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
if (!produce?.mem) {
|
|
373
|
-
errors.push("must define produce.mem");
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
return errors;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
function producer({ name, selector, produce, check, state, env = {}, region = "us-east-1" }) {
|
|
380
|
-
const envWithDefaultValues = {
|
|
381
|
-
SKYNET_KAFKA_SERVER: SENSITIVE_VALUE,
|
|
382
|
-
SKYNET_KAFKA_USERNAME: SENSITIVE_VALUE,
|
|
383
|
-
SKYNET_KAFKA_PASSWORD: SENSITIVE_VALUE,
|
|
384
|
-
...env,
|
|
385
|
-
};
|
|
386
|
-
|
|
387
|
-
return createApp({
|
|
388
|
-
parameterErrors: [
|
|
389
|
-
...checkProducerProduceParameter(produce),
|
|
390
|
-
...checkStateParameter(state),
|
|
391
|
-
...checkCheckParameter(check),
|
|
392
|
-
...checkEnvParameter(env),
|
|
393
|
-
],
|
|
394
|
-
env: envWithDefaultValues,
|
|
395
|
-
check,
|
|
396
|
-
onRun: () => {
|
|
397
|
-
const { run } = createProducerApp({
|
|
398
|
-
binaryName: `${getBinaryName()} run`,
|
|
399
|
-
name,
|
|
400
|
-
selector,
|
|
401
|
-
|
|
402
|
-
producer: {
|
|
403
|
-
topic: produce.topic,
|
|
404
|
-
deadLetterTopic: produce.deadLetterTopic,
|
|
405
|
-
produce: produce.func,
|
|
406
|
-
batchSize: produce.batchSize,
|
|
407
|
-
maxRetry: produce.maxRetry,
|
|
408
|
-
},
|
|
409
|
-
|
|
410
|
-
state,
|
|
411
|
-
});
|
|
412
|
-
|
|
413
|
-
run();
|
|
414
|
-
},
|
|
415
|
-
onDeploy: () => {
|
|
416
|
-
const bin = detectBin();
|
|
417
|
-
|
|
418
|
-
const { deploy } = createDeploy({
|
|
419
|
-
binaryName: `${getBinaryName()} deploy`,
|
|
420
|
-
name,
|
|
421
|
-
workingDirectory: detectWorkingDirectory(),
|
|
422
|
-
bin: `${bin} run`,
|
|
423
|
-
selector,
|
|
424
|
-
region,
|
|
425
|
-
env: envWithDefaultValues,
|
|
426
|
-
schedule: "@minutely",
|
|
427
|
-
cpu: produce.cpu,
|
|
428
|
-
mem: produce.mem,
|
|
429
|
-
check: check && {
|
|
430
|
-
bin: `${bin} check`,
|
|
431
|
-
schedule: check.schedule,
|
|
432
|
-
cpu: check.cpu || 100,
|
|
433
|
-
mem: check.mem || 100,
|
|
434
|
-
},
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
deploy();
|
|
438
|
-
},
|
|
439
|
-
onCheck: () => {
|
|
440
|
-
const { monitor } = createMonitor({
|
|
441
|
-
binaryName: `${getBinaryName()} check`,
|
|
442
|
-
name,
|
|
443
|
-
getState: async (nam, selectorFlags) => {
|
|
444
|
-
const latestId = await getProducerLatestId(nam, selectorFlags);
|
|
445
|
-
|
|
446
|
-
return { latestId };
|
|
447
|
-
},
|
|
448
|
-
selector,
|
|
449
|
-
check: check.func,
|
|
450
|
-
maxRetry: check.maxRetry,
|
|
451
|
-
});
|
|
452
|
-
|
|
453
|
-
monitor();
|
|
454
|
-
},
|
|
455
|
-
});
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
function checkConsumerConsumeParameter(consume) {
|
|
459
|
-
const errors = [];
|
|
460
|
-
|
|
461
|
-
if (!consume?.func) {
|
|
462
|
-
errors.push("must define consume.func");
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
if (!consume?.topic) {
|
|
466
|
-
errors.push("must define consume.topic");
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
if (!consume?.cpu) {
|
|
470
|
-
errors.push("must define consume.cpu");
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
if (!consume?.mem) {
|
|
474
|
-
errors.push("must define consume.mem");
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
return errors;
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
function consumer({ name, selector, consume, check, env = {}, region = "us-east-1" }) {
|
|
481
|
-
const envWithDefaultValues = {
|
|
482
|
-
SKYNET_KAFKA_SERVER: SENSITIVE_VALUE,
|
|
483
|
-
SKYNET_KAFKA_USERNAME: SENSITIVE_VALUE,
|
|
484
|
-
SKYNET_KAFKA_PASSWORD: SENSITIVE_VALUE,
|
|
485
|
-
...env,
|
|
486
|
-
};
|
|
487
|
-
|
|
488
|
-
return createApp({
|
|
489
|
-
parameterErrors: [
|
|
490
|
-
...checkConsumerConsumeParameter(consume),
|
|
491
|
-
...checkCheckParameter(check),
|
|
492
|
-
...checkEnvParameter(env),
|
|
493
|
-
],
|
|
494
|
-
env: envWithDefaultValues,
|
|
495
|
-
check,
|
|
496
|
-
onRun: () => {
|
|
497
|
-
const { run } = createConsumerApp({
|
|
498
|
-
binaryName: `${getBinaryName()} run`,
|
|
499
|
-
name,
|
|
500
|
-
selector,
|
|
501
|
-
|
|
502
|
-
consumer: {
|
|
503
|
-
topic: consume.topic,
|
|
504
|
-
consume: consume.func,
|
|
505
|
-
maxRetry: consume.maxRetry,
|
|
506
|
-
},
|
|
507
|
-
});
|
|
508
|
-
|
|
509
|
-
run();
|
|
510
|
-
},
|
|
511
|
-
onDeploy: () => {
|
|
512
|
-
const bin = detectBin();
|
|
513
|
-
|
|
514
|
-
const { deploy } = createDeploy({
|
|
515
|
-
binaryName: `${getBinaryName()} deploy`,
|
|
516
|
-
name,
|
|
517
|
-
workingDirectory: detectWorkingDirectory(),
|
|
518
|
-
bin: `${bin} run`,
|
|
519
|
-
selector,
|
|
520
|
-
region,
|
|
521
|
-
env: envWithDefaultValues,
|
|
522
|
-
schedule: "@minutely",
|
|
523
|
-
cpu: consume.cpu,
|
|
524
|
-
mem: consume.mem,
|
|
525
|
-
check: check && {
|
|
526
|
-
bin: `${bin} check`,
|
|
527
|
-
schedule: check.schedule,
|
|
528
|
-
cpu: check.cpu || 100,
|
|
529
|
-
mem: check.mem || 100,
|
|
530
|
-
},
|
|
531
|
-
});
|
|
532
|
-
|
|
533
|
-
deploy();
|
|
534
|
-
},
|
|
535
|
-
onCheck: () => {
|
|
536
|
-
const { monitor } = createMonitor({
|
|
537
|
-
binaryName: `${getBinaryName()} check`,
|
|
538
|
-
name,
|
|
539
|
-
selector,
|
|
540
|
-
check: check.func,
|
|
541
|
-
maxRetry: check.maxRetry,
|
|
542
|
-
});
|
|
543
|
-
|
|
544
|
-
monitor();
|
|
545
|
-
},
|
|
546
|
-
});
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
function checkApiServeParameter(serve, routes) {
|
|
550
|
-
const errors = [];
|
|
551
|
-
|
|
552
|
-
if (!serve?.prefix) {
|
|
553
|
-
errors.push("must define serve.prefix");
|
|
554
|
-
} else if (!serve.prefix.startsWith("/")) {
|
|
555
|
-
errors.push("server.prefix must start with /, e.g. /my-api");
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
if (!serve?.port) {
|
|
559
|
-
errors.push("must define serve.port");
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
if (!serve?.cpu) {
|
|
563
|
-
errors.push("must define serve.cpu");
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
if (!serve?.mem) {
|
|
567
|
-
errors.push("must define serve.mem");
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
if (routes.some((r) => r.protected) && !serve.apiKey) {
|
|
571
|
-
errors.push("must define serve.apiKey since some routes are protected");
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
return errors;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
function checkApiRoutesParameter(routes) {
|
|
578
|
-
const errors = [];
|
|
579
|
-
|
|
580
|
-
if (!Array.isArray(routes)) {
|
|
581
|
-
errors.push("routes must be an array");
|
|
582
|
-
} else {
|
|
583
|
-
for (let i = 0; i < routes.length; i++) {
|
|
584
|
-
const route = routes[i];
|
|
585
|
-
|
|
586
|
-
if (!route.path) {
|
|
587
|
-
errors.push(`routes[${i}] must define path`);
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
if (!route.handler) {
|
|
591
|
-
errors.push(`routes[${i}] must define handler`);
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
if (route.middlewares && !Array.isArray(route.middlewares)) {
|
|
595
|
-
errors.push(`routes[${i}].middlewares must be an array`);
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
return errors;
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
function api({ name, routes, serve, env = {}, region = "us-east-1" }) {
|
|
604
|
-
// do not support selector for now
|
|
605
|
-
const selector = {};
|
|
606
|
-
|
|
607
|
-
// hard code a simple health check for now
|
|
608
|
-
const check = {
|
|
609
|
-
func: async () => {
|
|
610
|
-
const errors = [];
|
|
611
|
-
|
|
612
|
-
const url = `http://localhost:9999${serve.prefix}/`;
|
|
613
|
-
const res = await fetch(url);
|
|
614
|
-
|
|
615
|
-
if (!res.ok) {
|
|
616
|
-
errors.push({
|
|
617
|
-
type: ERROR_LEVEL.ERROR,
|
|
618
|
-
message: `service ${name} is not healthy`,
|
|
619
|
-
});
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
return errors;
|
|
623
|
-
},
|
|
624
|
-
schedule: every(5).minutes,
|
|
625
|
-
maxRetry: 0,
|
|
626
|
-
};
|
|
627
|
-
|
|
628
|
-
return createApp({
|
|
629
|
-
parameterErrors: [
|
|
630
|
-
...checkApiRoutesParameter(routes),
|
|
631
|
-
...checkApiServeParameter(serve, routes),
|
|
632
|
-
...checkCheckParameter(check),
|
|
633
|
-
...checkEnvParameter(env),
|
|
634
|
-
],
|
|
635
|
-
env,
|
|
636
|
-
check,
|
|
637
|
-
onRun: () => {
|
|
638
|
-
startApiApp({
|
|
639
|
-
binaryName: `${getBinaryName()} run`,
|
|
640
|
-
name,
|
|
641
|
-
selector,
|
|
642
|
-
routes,
|
|
643
|
-
serve,
|
|
644
|
-
});
|
|
645
|
-
},
|
|
646
|
-
onDeploy: () => {
|
|
647
|
-
const bin = detectBin();
|
|
648
|
-
|
|
649
|
-
const { deploy } = createDeploy({
|
|
650
|
-
binaryName: `${getBinaryName()} deploy`,
|
|
651
|
-
name,
|
|
652
|
-
workingDirectory: detectWorkingDirectory(),
|
|
653
|
-
bin: `${bin} run`,
|
|
654
|
-
selector,
|
|
655
|
-
region,
|
|
656
|
-
env,
|
|
657
|
-
schedule: "@minutely",
|
|
658
|
-
cpu: serve.cpu,
|
|
659
|
-
mem: serve.mem,
|
|
660
|
-
service: {
|
|
661
|
-
prefix: serve.prefix,
|
|
662
|
-
port: serve.port,
|
|
663
|
-
},
|
|
664
|
-
check: check && {
|
|
665
|
-
bin: `${bin} check`,
|
|
666
|
-
schedule: check.schedule,
|
|
667
|
-
cpu: check.cpu || 100,
|
|
668
|
-
mem: check.mem || 100,
|
|
669
|
-
},
|
|
670
|
-
});
|
|
671
|
-
|
|
672
|
-
deploy();
|
|
673
|
-
},
|
|
674
|
-
onCheck: () => {
|
|
675
|
-
const { monitor } = createMonitor({
|
|
676
|
-
binaryName: `${getBinaryName()} check`,
|
|
677
|
-
name,
|
|
678
|
-
selector,
|
|
679
|
-
check: check.func,
|
|
680
|
-
maxRetry: check.maxRetry,
|
|
681
|
-
});
|
|
682
|
-
|
|
683
|
-
monitor();
|
|
684
|
-
},
|
|
685
|
-
});
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
const SENSITIVE_VALUE = null;
|
|
689
|
-
|
|
690
|
-
const every = (n = 1) => {
|
|
691
|
-
if (n === 1) {
|
|
692
|
-
return {
|
|
693
|
-
second: "*/1 * * * * * *",
|
|
694
|
-
minute: "0 * * * * * *",
|
|
695
|
-
hour: "0 0 * * * * *",
|
|
696
|
-
day: "0 0 0 * * * *",
|
|
697
|
-
week: "0 0 0 * * 0 *",
|
|
698
|
-
};
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
return {
|
|
702
|
-
seconds: `*/${n} * * * * * *`,
|
|
703
|
-
minutes: `0 */${n} * * * * *`,
|
|
704
|
-
hours: `0 0 */${n} * * * *`,
|
|
705
|
-
days: `0 0 0 */${n} * * *`,
|
|
706
|
-
};
|
|
707
|
-
};
|
|
708
|
-
|
|
709
|
-
module.exports = { indexer, modeIndexer, producer, consumer, api, every, SENSITIVE_VALUE, ERROR_LEVEL };
|
|
1
|
+
const { EOL } = require("os");
|
|
2
|
+
const fetch = require("node-fetch");
|
|
3
|
+
const { SKYNET_API_PREFIX } = require("./const");
|
|
4
|
+
const {
|
|
5
|
+
createIndexerApp,
|
|
6
|
+
createModeIndexerApp,
|
|
7
|
+
getIndexerLatestId,
|
|
8
|
+
getIndexerValidatedId,
|
|
9
|
+
getIndexerState,
|
|
10
|
+
} = require("./indexer");
|
|
11
|
+
const { createDeploy, createModeDeploy } = require("./deploy");
|
|
12
|
+
const { createProducerApp, createConsumerApp, getProducerLatestId } = require("./kafka");
|
|
13
|
+
const { startApiApp } = require("./api");
|
|
14
|
+
const { createMonitor, ERROR_LEVEL } = require("./monitor");
|
|
15
|
+
const { getBinaryName, detectBin, detectWorkingDirectory } = require("./cli");
|
|
16
|
+
|
|
17
|
+
function printAppHelp(hasCheck) {
|
|
18
|
+
console.log(`
|
|
19
|
+
Usage
|
|
20
|
+
|
|
21
|
+
$ ${getBinaryName()} run <options>${hasCheck ? `\n $ ${getBinaryName()} check <options>` : ""}
|
|
22
|
+
$ ${getBinaryName()} deploy <options>
|
|
23
|
+
$ ${getBinaryName()} delete <options>
|
|
24
|
+
`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function isDeleteCommand(command) {
|
|
28
|
+
return ["delete", "stop", "remove"].includes(command);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function checkDeployEnv(env) {
|
|
32
|
+
// check sensitive env
|
|
33
|
+
const missingEnvs = [];
|
|
34
|
+
|
|
35
|
+
for (let key of Object.keys(env)) {
|
|
36
|
+
if (env[key] === SENSITIVE_VALUE) {
|
|
37
|
+
const res = await fetch(`${SKYNET_API_PREFIX}/secrets/verify?name=${key}`);
|
|
38
|
+
|
|
39
|
+
if (res.ok) {
|
|
40
|
+
const hasEnv = await res.json();
|
|
41
|
+
|
|
42
|
+
if (!hasEnv) {
|
|
43
|
+
missingEnvs.push(key);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (missingEnvs.length > 0) {
|
|
50
|
+
console.log(`Missing the following sensitive environment on nomad server:${EOL}- ${missingEnvs.join(EOL + "- ")}`);
|
|
51
|
+
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function checkAndSetEnv(env) {
|
|
57
|
+
const missingEnvs = [];
|
|
58
|
+
|
|
59
|
+
for (let key of Object.keys(env)) {
|
|
60
|
+
if (env[key]) {
|
|
61
|
+
process.env[key] = env[key];
|
|
62
|
+
} else if (!process.env[key]) {
|
|
63
|
+
missingEnvs.push(key);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (missingEnvs.length > 0) {
|
|
68
|
+
console.log(`The following environment value shouldn't be empty:${EOL}- ${missingEnvs.join(EOL + "- ")}`);
|
|
69
|
+
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function createApp({ parameterErrors, env, check, onRun, onDeploy, onCheck }) {
|
|
75
|
+
if (parameterErrors.length > 0) {
|
|
76
|
+
console.log(`Parameter Validation Failed:${EOL}- ${parameterErrors.join(EOL + "- ")}`);
|
|
77
|
+
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return () => {
|
|
82
|
+
const subCommand = process.argv[2];
|
|
83
|
+
|
|
84
|
+
// emulate command line call without subcmd
|
|
85
|
+
process.argv = [process.argv[0], process.argv[1], ...process.argv.slice(3)];
|
|
86
|
+
|
|
87
|
+
if (subCommand === "run") {
|
|
88
|
+
checkAndSetEnv(env);
|
|
89
|
+
|
|
90
|
+
onRun();
|
|
91
|
+
} else if (subCommand === "deploy" || isDeleteCommand(subCommand)) {
|
|
92
|
+
if (isDeleteCommand(subCommand)) {
|
|
93
|
+
process.argv.push("--stop");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (process.argv.includes("--production")) {
|
|
97
|
+
checkDeployEnv(env)
|
|
98
|
+
.then(() => onDeploy())
|
|
99
|
+
.catch(console.error);
|
|
100
|
+
} else {
|
|
101
|
+
onDeploy();
|
|
102
|
+
}
|
|
103
|
+
} else if (!!check && subCommand === "check") {
|
|
104
|
+
checkAndSetEnv(env);
|
|
105
|
+
|
|
106
|
+
onCheck();
|
|
107
|
+
} else {
|
|
108
|
+
printAppHelp(!!check);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function checkEnvParameter(env) {
|
|
114
|
+
const errors = [];
|
|
115
|
+
|
|
116
|
+
const envKeys = Object.keys(env);
|
|
117
|
+
|
|
118
|
+
envKeys.forEach((k) => {
|
|
119
|
+
if (!env[k] && env[k] !== SENSITIVE_VALUE) {
|
|
120
|
+
errors.push(`must have valid non-empty value for env.${k}`);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
return errors;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function checkIndexerBuildParameter(build) {
|
|
128
|
+
const errors = [];
|
|
129
|
+
|
|
130
|
+
if (!build?.func) {
|
|
131
|
+
errors.push("must define build.func");
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (!build?.cpu) {
|
|
135
|
+
errors.push("must define build.cpu");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (!build?.mem) {
|
|
139
|
+
errors.push("must define build.mem");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return errors;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function checkCheckParameter(check) {
|
|
146
|
+
if (!check) {
|
|
147
|
+
return [];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const errors = [];
|
|
151
|
+
|
|
152
|
+
if (!check?.func) {
|
|
153
|
+
errors.push("must define check.func");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!check?.schedule) {
|
|
157
|
+
errors.push("must define check.schedule");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return errors;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function checkStateParameter(state) {
|
|
164
|
+
const errors = [];
|
|
165
|
+
|
|
166
|
+
if (!state?.type) {
|
|
167
|
+
errors.push("must define state.type");
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (!state?.getMaxId) {
|
|
171
|
+
errors.push("must define state.getMaxId");
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return errors;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function indexer({ name, selector, build, check, env = {}, region = "us-east-1" }) {
|
|
178
|
+
return createApp({
|
|
179
|
+
parameterErrors: [...checkIndexerBuildParameter(build), ...checkCheckParameter(check), ...checkEnvParameter(env)],
|
|
180
|
+
env,
|
|
181
|
+
check,
|
|
182
|
+
onRun: () => {
|
|
183
|
+
const { run } = createIndexerApp({
|
|
184
|
+
binaryName: `${getBinaryName()} run`,
|
|
185
|
+
name,
|
|
186
|
+
selector,
|
|
187
|
+
build: build.func,
|
|
188
|
+
maxRetry: build.maxRetry,
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
run();
|
|
192
|
+
},
|
|
193
|
+
onDeploy: () => {
|
|
194
|
+
const bin = detectBin();
|
|
195
|
+
|
|
196
|
+
const { deploy } = createDeploy({
|
|
197
|
+
binaryName: `${getBinaryName()} deploy`,
|
|
198
|
+
name,
|
|
199
|
+
workingDirectory: detectWorkingDirectory(),
|
|
200
|
+
bin: `${bin} run`,
|
|
201
|
+
selector,
|
|
202
|
+
region,
|
|
203
|
+
env,
|
|
204
|
+
schedule: build.schedule,
|
|
205
|
+
restart: build.restart,
|
|
206
|
+
cpu: build.cpu,
|
|
207
|
+
mem: build.mem,
|
|
208
|
+
check: check && {
|
|
209
|
+
bin: `${bin} check`,
|
|
210
|
+
schedule: check.schedule,
|
|
211
|
+
cpu: check.cpu || 100,
|
|
212
|
+
mem: check.mem || 100,
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
deploy();
|
|
217
|
+
},
|
|
218
|
+
onCheck: () => {
|
|
219
|
+
const { monitor } = createMonitor({
|
|
220
|
+
binaryName: `${getBinaryName()} check`,
|
|
221
|
+
name,
|
|
222
|
+
type: "stateless",
|
|
223
|
+
selector,
|
|
224
|
+
check: check.func,
|
|
225
|
+
maxRetry: check.maxRetry,
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
monitor();
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function checkModeIndexerBuildParameter(build) {
|
|
234
|
+
const errors = [];
|
|
235
|
+
|
|
236
|
+
if (!build?.func) {
|
|
237
|
+
errors.push("must define build.func");
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (!build?.cpu) {
|
|
241
|
+
errors.push("must define build.cpu");
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (!build?.mem) {
|
|
245
|
+
errors.push("must define build.mem");
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return errors;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function checkModeIndexerValidateParameter(validate) {
|
|
252
|
+
const errors = [];
|
|
253
|
+
|
|
254
|
+
if (validate) {
|
|
255
|
+
if (!validate.func) {
|
|
256
|
+
errors.push("must define validate.func");
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (!validate.cpu) {
|
|
260
|
+
errors.push("must define validate.cpu");
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (!validate.mem) {
|
|
264
|
+
errors.push("must define validate.mem");
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return errors;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function modeIndexer({ name, selector, state, build, validate, check, env = {}, region = "us-east-1" }) {
|
|
272
|
+
return createApp({
|
|
273
|
+
parameterErrors: [
|
|
274
|
+
...checkModeIndexerBuildParameter(build),
|
|
275
|
+
...checkModeIndexerValidateParameter(validate),
|
|
276
|
+
...checkStateParameter(state),
|
|
277
|
+
...checkCheckParameter(check),
|
|
278
|
+
...checkEnvParameter(env),
|
|
279
|
+
],
|
|
280
|
+
env,
|
|
281
|
+
check,
|
|
282
|
+
onRun: () => {
|
|
283
|
+
const { run } = createModeIndexerApp({
|
|
284
|
+
binaryName: `${getBinaryName()} run`,
|
|
285
|
+
name,
|
|
286
|
+
selector,
|
|
287
|
+
build: build.func,
|
|
288
|
+
maxRetry: build.maxRetry,
|
|
289
|
+
buildBatchSize: build.batchSize,
|
|
290
|
+
buildConcurrency: build.concurrency,
|
|
291
|
+
validate: validate && validate.func,
|
|
292
|
+
validateBatchSize: validate && validate.batchSize,
|
|
293
|
+
validateConcurrency: validate && validate.concurrency,
|
|
294
|
+
state,
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
run();
|
|
298
|
+
},
|
|
299
|
+
onDeploy: () => {
|
|
300
|
+
const bin = detectBin();
|
|
301
|
+
|
|
302
|
+
const { deploy } = createModeDeploy({
|
|
303
|
+
binaryName: `${getBinaryName()} deploy`,
|
|
304
|
+
name,
|
|
305
|
+
workingDirectory: detectWorkingDirectory(),
|
|
306
|
+
bin: `${bin} run`,
|
|
307
|
+
selector,
|
|
308
|
+
region,
|
|
309
|
+
env,
|
|
310
|
+
|
|
311
|
+
deltaSchedule: build.schedule,
|
|
312
|
+
deltaCpu: build.cpu,
|
|
313
|
+
deltaMem: build.mem,
|
|
314
|
+
rebuildCpu: build.cpu,
|
|
315
|
+
rebuildMem: build.mem,
|
|
316
|
+
|
|
317
|
+
validateSchedule: validate && validate.schedule,
|
|
318
|
+
validateCpu: validate && validate.cpu,
|
|
319
|
+
validateMem: validate && validate.mem,
|
|
320
|
+
|
|
321
|
+
check: check && {
|
|
322
|
+
bin: `${bin} check`,
|
|
323
|
+
schedule: check.schedule,
|
|
324
|
+
cpu: check.cpu || 100,
|
|
325
|
+
mem: check.mem || 100,
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
deploy();
|
|
330
|
+
},
|
|
331
|
+
onCheck: () => {
|
|
332
|
+
const { monitor } = createMonitor({
|
|
333
|
+
binaryName: `${getBinaryName()} check`,
|
|
334
|
+
name,
|
|
335
|
+
getState: async (nam, selectorFlags) => {
|
|
336
|
+
const latestId = await getIndexerLatestId(nam, selectorFlags);
|
|
337
|
+
const validatedId = await getIndexerValidatedId(nam, selectorFlags);
|
|
338
|
+
const buildState = await getIndexerState(nam, selectorFlags);
|
|
339
|
+
|
|
340
|
+
return { latestId, validatedId, buildState };
|
|
341
|
+
},
|
|
342
|
+
selector,
|
|
343
|
+
mode: true,
|
|
344
|
+
check: check.func,
|
|
345
|
+
maxRetry: check.maxRetry,
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
monitor();
|
|
349
|
+
},
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function checkProducerProduceParameter(produce) {
|
|
354
|
+
const errors = [];
|
|
355
|
+
|
|
356
|
+
if (!produce?.func) {
|
|
357
|
+
errors.push("must define produce.func");
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (!produce?.topic) {
|
|
361
|
+
errors.push("must define produce.topic");
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (!produce?.deadLetterTopic) {
|
|
365
|
+
errors.push("must define produce.deadLetterTopic");
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (!produce?.cpu) {
|
|
369
|
+
errors.push("must define produce.cpu");
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (!produce?.mem) {
|
|
373
|
+
errors.push("must define produce.mem");
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return errors;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function producer({ name, selector, produce, check, state, env = {}, region = "us-east-1" }) {
|
|
380
|
+
const envWithDefaultValues = {
|
|
381
|
+
SKYNET_KAFKA_SERVER: SENSITIVE_VALUE,
|
|
382
|
+
SKYNET_KAFKA_USERNAME: SENSITIVE_VALUE,
|
|
383
|
+
SKYNET_KAFKA_PASSWORD: SENSITIVE_VALUE,
|
|
384
|
+
...env,
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
return createApp({
|
|
388
|
+
parameterErrors: [
|
|
389
|
+
...checkProducerProduceParameter(produce),
|
|
390
|
+
...checkStateParameter(state),
|
|
391
|
+
...checkCheckParameter(check),
|
|
392
|
+
...checkEnvParameter(env),
|
|
393
|
+
],
|
|
394
|
+
env: envWithDefaultValues,
|
|
395
|
+
check,
|
|
396
|
+
onRun: () => {
|
|
397
|
+
const { run } = createProducerApp({
|
|
398
|
+
binaryName: `${getBinaryName()} run`,
|
|
399
|
+
name,
|
|
400
|
+
selector,
|
|
401
|
+
|
|
402
|
+
producer: {
|
|
403
|
+
topic: produce.topic,
|
|
404
|
+
deadLetterTopic: produce.deadLetterTopic,
|
|
405
|
+
produce: produce.func,
|
|
406
|
+
batchSize: produce.batchSize,
|
|
407
|
+
maxRetry: produce.maxRetry,
|
|
408
|
+
},
|
|
409
|
+
|
|
410
|
+
state,
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
run();
|
|
414
|
+
},
|
|
415
|
+
onDeploy: () => {
|
|
416
|
+
const bin = detectBin();
|
|
417
|
+
|
|
418
|
+
const { deploy } = createDeploy({
|
|
419
|
+
binaryName: `${getBinaryName()} deploy`,
|
|
420
|
+
name,
|
|
421
|
+
workingDirectory: detectWorkingDirectory(),
|
|
422
|
+
bin: `${bin} run`,
|
|
423
|
+
selector,
|
|
424
|
+
region,
|
|
425
|
+
env: envWithDefaultValues,
|
|
426
|
+
schedule: "@minutely",
|
|
427
|
+
cpu: produce.cpu,
|
|
428
|
+
mem: produce.mem,
|
|
429
|
+
check: check && {
|
|
430
|
+
bin: `${bin} check`,
|
|
431
|
+
schedule: check.schedule,
|
|
432
|
+
cpu: check.cpu || 100,
|
|
433
|
+
mem: check.mem || 100,
|
|
434
|
+
},
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
deploy();
|
|
438
|
+
},
|
|
439
|
+
onCheck: () => {
|
|
440
|
+
const { monitor } = createMonitor({
|
|
441
|
+
binaryName: `${getBinaryName()} check`,
|
|
442
|
+
name,
|
|
443
|
+
getState: async (nam, selectorFlags) => {
|
|
444
|
+
const latestId = await getProducerLatestId(nam, selectorFlags);
|
|
445
|
+
|
|
446
|
+
return { latestId };
|
|
447
|
+
},
|
|
448
|
+
selector,
|
|
449
|
+
check: check.func,
|
|
450
|
+
maxRetry: check.maxRetry,
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
monitor();
|
|
454
|
+
},
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function checkConsumerConsumeParameter(consume) {
|
|
459
|
+
const errors = [];
|
|
460
|
+
|
|
461
|
+
if (!consume?.func) {
|
|
462
|
+
errors.push("must define consume.func");
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if (!consume?.topic) {
|
|
466
|
+
errors.push("must define consume.topic");
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
if (!consume?.cpu) {
|
|
470
|
+
errors.push("must define consume.cpu");
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (!consume?.mem) {
|
|
474
|
+
errors.push("must define consume.mem");
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return errors;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function consumer({ name, selector, consume, check, env = {}, region = "us-east-1" }) {
|
|
481
|
+
const envWithDefaultValues = {
|
|
482
|
+
SKYNET_KAFKA_SERVER: SENSITIVE_VALUE,
|
|
483
|
+
SKYNET_KAFKA_USERNAME: SENSITIVE_VALUE,
|
|
484
|
+
SKYNET_KAFKA_PASSWORD: SENSITIVE_VALUE,
|
|
485
|
+
...env,
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
return createApp({
|
|
489
|
+
parameterErrors: [
|
|
490
|
+
...checkConsumerConsumeParameter(consume),
|
|
491
|
+
...checkCheckParameter(check),
|
|
492
|
+
...checkEnvParameter(env),
|
|
493
|
+
],
|
|
494
|
+
env: envWithDefaultValues,
|
|
495
|
+
check,
|
|
496
|
+
onRun: () => {
|
|
497
|
+
const { run } = createConsumerApp({
|
|
498
|
+
binaryName: `${getBinaryName()} run`,
|
|
499
|
+
name,
|
|
500
|
+
selector,
|
|
501
|
+
|
|
502
|
+
consumer: {
|
|
503
|
+
topic: consume.topic,
|
|
504
|
+
consume: consume.func,
|
|
505
|
+
maxRetry: consume.maxRetry,
|
|
506
|
+
},
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
run();
|
|
510
|
+
},
|
|
511
|
+
onDeploy: () => {
|
|
512
|
+
const bin = detectBin();
|
|
513
|
+
|
|
514
|
+
const { deploy } = createDeploy({
|
|
515
|
+
binaryName: `${getBinaryName()} deploy`,
|
|
516
|
+
name,
|
|
517
|
+
workingDirectory: detectWorkingDirectory(),
|
|
518
|
+
bin: `${bin} run`,
|
|
519
|
+
selector,
|
|
520
|
+
region,
|
|
521
|
+
env: envWithDefaultValues,
|
|
522
|
+
schedule: "@minutely",
|
|
523
|
+
cpu: consume.cpu,
|
|
524
|
+
mem: consume.mem,
|
|
525
|
+
check: check && {
|
|
526
|
+
bin: `${bin} check`,
|
|
527
|
+
schedule: check.schedule,
|
|
528
|
+
cpu: check.cpu || 100,
|
|
529
|
+
mem: check.mem || 100,
|
|
530
|
+
},
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
deploy();
|
|
534
|
+
},
|
|
535
|
+
onCheck: () => {
|
|
536
|
+
const { monitor } = createMonitor({
|
|
537
|
+
binaryName: `${getBinaryName()} check`,
|
|
538
|
+
name,
|
|
539
|
+
selector,
|
|
540
|
+
check: check.func,
|
|
541
|
+
maxRetry: check.maxRetry,
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
monitor();
|
|
545
|
+
},
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
function checkApiServeParameter(serve, routes) {
|
|
550
|
+
const errors = [];
|
|
551
|
+
|
|
552
|
+
if (!serve?.prefix) {
|
|
553
|
+
errors.push("must define serve.prefix");
|
|
554
|
+
} else if (!serve.prefix.startsWith("/")) {
|
|
555
|
+
errors.push("server.prefix must start with /, e.g. /my-api");
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
if (!serve?.port) {
|
|
559
|
+
errors.push("must define serve.port");
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
if (!serve?.cpu) {
|
|
563
|
+
errors.push("must define serve.cpu");
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
if (!serve?.mem) {
|
|
567
|
+
errors.push("must define serve.mem");
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if (routes.some((r) => r.protected) && !serve.apiKey) {
|
|
571
|
+
errors.push("must define serve.apiKey since some routes are protected");
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
return errors;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function checkApiRoutesParameter(routes) {
|
|
578
|
+
const errors = [];
|
|
579
|
+
|
|
580
|
+
if (!Array.isArray(routes)) {
|
|
581
|
+
errors.push("routes must be an array");
|
|
582
|
+
} else {
|
|
583
|
+
for (let i = 0; i < routes.length; i++) {
|
|
584
|
+
const route = routes[i];
|
|
585
|
+
|
|
586
|
+
if (!route.path) {
|
|
587
|
+
errors.push(`routes[${i}] must define path`);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
if (!route.handler) {
|
|
591
|
+
errors.push(`routes[${i}] must define handler`);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
if (route.middlewares && !Array.isArray(route.middlewares)) {
|
|
595
|
+
errors.push(`routes[${i}].middlewares must be an array`);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
return errors;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function api({ name, routes, serve, env = {}, region = "us-east-1" }) {
|
|
604
|
+
// do not support selector for now
|
|
605
|
+
const selector = {};
|
|
606
|
+
|
|
607
|
+
// hard code a simple health check for now
|
|
608
|
+
const check = {
|
|
609
|
+
func: async () => {
|
|
610
|
+
const errors = [];
|
|
611
|
+
|
|
612
|
+
const url = `http://localhost:9999${serve.prefix}/`;
|
|
613
|
+
const res = await fetch(url);
|
|
614
|
+
|
|
615
|
+
if (!res.ok) {
|
|
616
|
+
errors.push({
|
|
617
|
+
type: ERROR_LEVEL.ERROR,
|
|
618
|
+
message: `service ${name} is not healthy`,
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
return errors;
|
|
623
|
+
},
|
|
624
|
+
schedule: every(5).minutes,
|
|
625
|
+
maxRetry: 0,
|
|
626
|
+
};
|
|
627
|
+
|
|
628
|
+
return createApp({
|
|
629
|
+
parameterErrors: [
|
|
630
|
+
...checkApiRoutesParameter(routes),
|
|
631
|
+
...checkApiServeParameter(serve, routes),
|
|
632
|
+
...checkCheckParameter(check),
|
|
633
|
+
...checkEnvParameter(env),
|
|
634
|
+
],
|
|
635
|
+
env,
|
|
636
|
+
check,
|
|
637
|
+
onRun: () => {
|
|
638
|
+
startApiApp({
|
|
639
|
+
binaryName: `${getBinaryName()} run`,
|
|
640
|
+
name,
|
|
641
|
+
selector,
|
|
642
|
+
routes,
|
|
643
|
+
serve,
|
|
644
|
+
});
|
|
645
|
+
},
|
|
646
|
+
onDeploy: () => {
|
|
647
|
+
const bin = detectBin();
|
|
648
|
+
|
|
649
|
+
const { deploy } = createDeploy({
|
|
650
|
+
binaryName: `${getBinaryName()} deploy`,
|
|
651
|
+
name,
|
|
652
|
+
workingDirectory: detectWorkingDirectory(),
|
|
653
|
+
bin: `${bin} run`,
|
|
654
|
+
selector,
|
|
655
|
+
region,
|
|
656
|
+
env,
|
|
657
|
+
schedule: "@minutely",
|
|
658
|
+
cpu: serve.cpu,
|
|
659
|
+
mem: serve.mem,
|
|
660
|
+
service: {
|
|
661
|
+
prefix: serve.prefix,
|
|
662
|
+
port: serve.port,
|
|
663
|
+
},
|
|
664
|
+
check: check && {
|
|
665
|
+
bin: `${bin} check`,
|
|
666
|
+
schedule: check.schedule,
|
|
667
|
+
cpu: check.cpu || 100,
|
|
668
|
+
mem: check.mem || 100,
|
|
669
|
+
},
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
deploy();
|
|
673
|
+
},
|
|
674
|
+
onCheck: () => {
|
|
675
|
+
const { monitor } = createMonitor({
|
|
676
|
+
binaryName: `${getBinaryName()} check`,
|
|
677
|
+
name,
|
|
678
|
+
selector,
|
|
679
|
+
check: check.func,
|
|
680
|
+
maxRetry: check.maxRetry,
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
monitor();
|
|
684
|
+
},
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
const SENSITIVE_VALUE = null;
|
|
689
|
+
|
|
690
|
+
const every = (n = 1) => {
|
|
691
|
+
if (n === 1) {
|
|
692
|
+
return {
|
|
693
|
+
second: "*/1 * * * * * *",
|
|
694
|
+
minute: "0 * * * * * *",
|
|
695
|
+
hour: "0 0 * * * * *",
|
|
696
|
+
day: "0 0 0 * * * *",
|
|
697
|
+
week: "0 0 0 * * 0 *",
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
return {
|
|
702
|
+
seconds: `*/${n} * * * * * *`,
|
|
703
|
+
minutes: `0 */${n} * * * * *`,
|
|
704
|
+
hours: `0 0 */${n} * * * *`,
|
|
705
|
+
days: `0 0 0 */${n} * * *`,
|
|
706
|
+
};
|
|
707
|
+
};
|
|
708
|
+
|
|
709
|
+
module.exports = { indexer, modeIndexer, producer, consumer, api, every, SENSITIVE_VALUE, ERROR_LEVEL };
|